前言
Redis作为高性能的键值对数据库,拥有丰富的命令集,这些命令是我们操作和管理Redis的基础。本文将系统地介绍Redis的常用命令,从基础操作到高级应用,帮助读者全面掌握Redis命令的使用技巧。
基本概念
Redis提供了200多个命令,这些命令可以按照数据类型和功能进行分类。在开始详细介绍前,我们需要了解几个基本概念:
- 键(Key):Redis中的每个数据都有一个唯一的键
- 值(Value):Redis支持多种数据类型作为值
- TTL(Time To Live):键的生存时间
- 原子性:Redis的命令操作都是原子性的
Redis命令分类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| ┌───────────────────┐ ┌───────────────────┐ ┌───────────────────┐ │ │ │ │ │ │ │ 字符串命令 │ │ 列表命令 │ │ 哈希命令 │ │ │ │ │ │ │ └───────────────────┘ └───────────────────┘ └───────────────────┘ │ │ │ ▼ ▼ ▼ ┌─────────────────────────────────────────────────────────────────────────┐ │ │ │ Redis命令集 │ │ │ └─────────────────────────────────────────────────────────────────────────┘ ▲ ▲ ▲ │ │ │ ┌───────────────────┐ ┌───────────────────┐ ┌───────────────────┐ │ │ │ │ │ │ │ 集合命令 │ │ 有序集合命令 │ │ 服务器管理命令 │ │ │ │ │ │ │ └───────────────────┘ └───────────────────┘ └───────────────────┘
|
连接与基础操作
连接到Redis服务器
1 2 3 4 5 6 7 8 9 10 11
| redis-cli
redis-cli -h 127.0.0.1 -p 6379
redis-cli -a password
redis-cli -n 1
|
基础键操作命令
命令 |
描述 |
示例 |
SET |
设置键值 |
SET name “Redis” |
GET |
获取值 |
GET name |
DEL |
删除键 |
DEL name |
EXISTS |
检查键是否存在 |
EXISTS name |
EXPIRE |
设置过期时间(秒) |
EXPIRE name 10 |
TTL |
查看剩余生存时间 |
TTL name |
PERSIST |
移除过期时间 |
PERSIST name |
KEYS |
查找键 |
KEYS * |
RENAME |
重命名键 |
RENAME oldkey newkey |
TYPE |
返回键的数据类型 |
TYPE name |
示例操作:
1 2 3 4 5 6 7 8 9 10 11 12
| > SET user:1:name "张三" OK > GET user:1:name "张三" > EXISTS user:1:name (integer) 1 > TTL user:1:name (integer) -1 > EXPIRE user:1:name 60 (integer) 1 > TTL user:1:name (integer) 58
|
字符串命令
字符串是Redis最基本的数据类型,可以存储文本、序列化的对象或数字。
常用字符串命令
命令 |
描述 |
示例 |
SET |
设置字符串值 |
SET key value |
SETNX |
仅键不存在时设置 |
SETNX key value |
SETEX |
设置值并指定过期时间 |
SETEX key seconds value |
MSET |
一次设置多个键值 |
MSET key1 value1 key2 value2 |
GET |
获取字符串值 |
GET key |
MGET |
一次获取多个键值 |
MGET key1 key2 |
INCR |
将整数值加1 |
INCR counter |
INCRBY |
将整数值增加指定数量 |
INCRBY counter 10 |
DECR |
将整数值减1 |
DECR counter |
DECRBY |
将整数值减少指定数量 |
DECRBY counter 10 |
APPEND |
追加字符串 |
APPEND key value |
STRLEN |
返回字符串长度 |
STRLEN key |
GETRANGE |
获取子字符串 |
GETRANGE key start end |
SETRANGE |
覆盖字符串的一部分 |
SETRANGE key offset value |
示例操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| > SET counter 10 OK > INCR counter (integer) 11 > INCRBY counter 5 (integer) 16 > DECR counter (integer) 15 > GET counter "15" > APPEND greeting "Hello" (integer) 5 > APPEND greeting " World" (integer) 11 > GET greeting "Hello World" > STRLEN greeting (integer) 11 > GETRANGE greeting 0 4 "Hello"
|
应用场景
字符串命令的主要应用场景:
- 缓存:存储常用数据
- 计数器:使用INCR系列命令
- 限速器:结合过期时间实现API限流
- 分布式锁:使用SETNX实现简单的分布式锁
哈希命令
哈希是字段-值对的集合,适合存储对象数据。
常用哈希命令
命令 |
描述 |
示例 |
HSET |
设置哈希字段的值 |
HSET user:1 name “张三” age 25 |
HSETNX |
仅当字段不存在时设置 |
HSETNX user:1 email “example@mail.com“ |
HGET |
获取哈希字段的值 |
HGET user:1 name |
HMGET |
获取多个哈希字段 |
HMGET user:1 name age |
HGETALL |
获取所有字段和值 |
HGETALL user:1 |
HDEL |
删除一个或多个哈希字段 |
HDEL user:1 age |
HEXISTS |
检查字段是否存在 |
HEXISTS user:1 name |
HKEYS |
获取所有字段 |
HKEYS user:1 |
HVALS |
获取所有值 |
HVALS user:1 |
HINCRBY |
增加哈希字段的整数值 |
HINCRBY user:1 visits 1 |
HLEN |
获取哈希字段数量 |
HLEN user:1 |
示例操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| > HSET user:1 name "张三" age 25 city "北京" (integer) 3 > HGET user:1 name "张三" > HGETALL user:1 1) "name" 2) "张三" 3) "age" 4) "25" 5) "city" 6) "北京" > HINCRBY user:1 age 1 (integer) 26 > HDEL user:1 city (integer) 1 > HKEYS user:1 1) "name" 2) "age"
|
应用场景
哈希命令的主要应用场景:
- 用户信息存储:每个用户一个哈希表
- 配置信息:存储系统配置
- 购物车:每个用户的购物车商品信息
- 计数统计:如文章的阅读量、点赞数等
列表命令
列表是简单的字符串列表,按照插入顺序排序,可以从头部或尾部添加元素。
常用列表命令
命令 |
描述 |
示例 |
LPUSH |
将元素添加到列表头部 |
LPUSH mylist value1 value2 |
RPUSH |
将元素添加到列表尾部 |
RPUSH mylist value3 value4 |
LPOP |
移除并返回列表头部元素 |
LPOP mylist |
RPOP |
移除并返回列表尾部元素 |
RPOP mylist |
LLEN |
获取列表长度 |
LLEN mylist |
LRANGE |
获取列表范围内的元素 |
LRANGE mylist 0 -1 |
LINDEX |
通过索引获取列表元素 |
LINDEX mylist 0 |
LSET |
通过索引设置列表元素 |
LSET mylist 0 “newvalue” |
LREM |
移除列表中的元素 |
LREM mylist 1 “value” |
LTRIM |
裁剪列表 |
LTRIM mylist 0 99 |
BLPOP |
阻塞式弹出头部元素 |
BLPOP mylist 5 |
BRPOP |
阻塞式弹出尾部元素 |
BRPOP mylist 5 |
示例操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| > LPUSH messages "消息3" "消息2" "消息1" (integer) 3 > RPUSH messages "消息4" "消息5" (integer) 5 > LRANGE messages 0 -1 1) "消息1" 2) "消息2" 3) "消息3" 4) "消息4" 5) "消息5" > LPOP messages "消息1" > RPOP messages "消息5" > LLEN messages (integer) 3 > LINDEX messages 0 "消息2"
|
应用场景
列表命令的主要应用场景:
- 消息队列:生产者LPUSH/RPUSH,消费者RPOP/LPOP
- 最新消息:如朋友圈最新动态、新闻列表
- 任务队列:使用BLPOP/BRPOP实现阻塞式任务队列
- 分页数据:使用LRANGE实现简单分页
集合命令
集合是无序的字符串集合,成员是唯一的。
常用集合命令
命令 |
描述 |
示例 |
SADD |
向集合添加一个或多个成员 |
SADD myset member1 member2 |
SREM |
移除集合中一个或多个成员 |
SREM myset member1 |
SMEMBERS |
返回集合中所有成员 |
SMEMBERS myset |
SISMEMBER |
判断成员是否在集合中 |
SISMEMBER myset member1 |
SCARD |
获取集合成员数 |
SCARD myset |
SINTER |
返回多个集合的交集 |
SINTER set1 set2 |
SUNION |
返回多个集合的并集 |
SUNION set1 set2 |
SDIFF |
返回多个集合的差集 |
SDIFF set1 set2 |
SRANDMEMBER |
返回集合中的随机元素 |
SRANDMEMBER myset 2 |
SPOP |
移除并返回集合中的随机元素 |
SPOP myset |
示例操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| > SADD team:a "张三" "李四" "王五" "赵六" (integer) 4 > SADD team:b "张三" "钱七" "孙八" (integer) 3 > SMEMBERS team:a 1) "张三" 2) "李四" 3) "王五" 4) "赵六" > SINTER team:a team:b 1) "张三" > SUNION team:a team:b 1) "张三" 2) "李四" 3) "王五" 4) "赵六" 5) "钱七" 6) "孙八" > SISMEMBER team:a "李四" (integer) 1 > SCARD team:a (integer) 4
|
应用场景
集合命令的主要应用场景:
- 标签系统:为用户、文章等添加标签
- 唯一性检查:如邮箱地址、用户名等
- 共同好友:使用交集操作
- 随机抽奖:使用SRANDMEMBER或SPOP
有序集合命令
有序集合类似于集合,但每个成员关联一个分数,用于排序。
常用有序集合命令
命令 |
描述 |
示例 |
ZADD |
添加成员和分数 |
ZADD scoreboard 89 “张三” 96 “李四” |
ZREM |
移除成员 |
ZREM scoreboard “张三” |
ZRANGE |
按索引范围获取成员 |
ZRANGE scoreboard 0 -1 |
ZREVRANGE |
反向获取成员 |
ZREVRANGE scoreboard 0 -1 |
ZRANK |
获取成员排名 |
ZRANK scoreboard “李四” |
ZREVRANK |
获取成员反向排名 |
ZREVRANK scoreboard “李四” |
ZSCORE |
获取成员分数 |
ZSCORE scoreboard “李四” |
ZINCRBY |
增加成员分数 |
ZINCRBY scoreboard 5 “张三” |
ZCOUNT |
统计分数范围内的成员数 |
ZCOUNT scoreboard 80 100 |
ZRANGEBYSCORE |
获取指定分数范围的成员 |
ZRANGEBYSCORE scoreboard 80 100 |
ZREMRANGEBYSCORE |
移除指定分数范围的成员 |
ZREMRANGEBYSCORE scoreboard 0 60 |
示例操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| > ZADD leaderboard 1200 "玩家A" 1500 "玩家B" 1100 "玩家C" 2000 "玩家D" (integer) 4 > ZRANGE leaderboard 0 -1 WITHSCORES 1) "玩家C" 2) "1100" 3) "玩家A" 4) "1200" 5) "玩家B" 6) "1500" 7) "玩家D" 8) "2000" > ZREVRANGE leaderboard 0 2 WITHSCORES 1) "玩家D" 2) "2000" 3) "玩家B" 4) "1500" 5) "玩家A" 6) "1200" > ZINCRBY leaderboard 300 "玩家C" "1400" > ZRANK leaderboard "玩家C" (integer) 2
|
应用场景
有序集合命令的主要应用场景:
- 排行榜:游戏分数、销售排名等
- 带权重的队列:优先级任务队列
- 延迟队列:使用时间戳作为分数
- 范围查询:如按成绩、价格区间查询
事务命令
Redis事务允许在单个步骤中执行一组命令。
常用事务命令
命令 |
描述 |
示例 |
MULTI |
标记事务开始 |
MULTI |
EXEC |
执行事务内所有命令 |
EXEC |
DISCARD |
丢弃事务 |
DISCARD |
WATCH |
监视键的修改 |
WATCH key1 key2 |
UNWATCH |
取消监视 |
UNWATCH |
示例操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| > MULTI OK > SET account:1:balance 500 QUEUED > SET account:2:balance 1000 QUEUED > EXEC 1) OK 2) OK
# 使用WATCH实现乐观锁 > WATCH account:1:balance OK > MULTI OK > INCRBY account:1:balance 100 QUEUED > EXEC # 如果account:1:balance在WATCH后被其他客户端修改,EXEC将返回nil 1) (integer) 600
|
发布/订阅命令
Redis提供了发布/订阅功能,用于构建消息系统。
常用发布/订阅命令
命令 |
描述 |
示例 |
SUBSCRIBE |
订阅一个或多个频道 |
SUBSCRIBE channel1 channel2 |
PSUBSCRIBE |
订阅匹配模式的频道 |
PSUBSCRIBE channel* |
PUBLISH |
发送消息到频道 |
PUBLISH channel1 “message” |
UNSUBSCRIBE |
退订频道 |
UNSUBSCRIBE channel1 |
PUNSUBSCRIBE |
退订模式频道 |
PUNSUBSCRIBE channel* |
PUBSUB CHANNELS |
列出活跃频道 |
PUBSUB CHANNELS |
示例操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| # 客户端A > SUBSCRIBE news Reading messages... (press Ctrl-C to quit) 1) "subscribe" 2) "news" 3) (integer) 1
# 客户端B > PUBLISH news "今天发布了新版本" (integer) 1
# 客户端A将收到 1) "message" 2) "news" 3) "今天发布了新版本"
|
脚本命令
Redis支持Lua脚本,用于执行复杂的原子操作。
常用脚本命令
命令 |
描述 |
示例 |
EVAL |
执行Lua脚本 |
EVAL script numkeys key [key …] arg [arg …] |
EVALSHA |
执行已缓存的脚本 |
EVALSHA sha1 numkeys key [key …] arg [arg …] |
SCRIPT LOAD |
加载脚本 |
SCRIPT LOAD script |
SCRIPT EXISTS |
检查脚本是否存在 |
SCRIPT EXISTS sha1 [sha1 …] |
SCRIPT FLUSH |
删除所有脚本 |
SCRIPT FLUSH |
SCRIPT KILL |
杀死正在执行的脚本 |
SCRIPT KILL |
示例操作:
1 2 3 4 5 6
| > EVAL "return redis.call('SET', KEYS[1], ARGV[1])" 1 mykey "Hello" OK
# 原子递增并检查限流 > EVAL "local current = redis.call('INCR', KEYS[1]) if current > tonumber(ARGV[1]) then return 0 else return 1 end" 1 rate:limit:user:123 "10" (integer) 1
|
连接和服务器命令
连接命令
命令 |
描述 |
示例 |
AUTH |
验证密码 |
AUTH password |
PING |
测试连接 |
PING |
ECHO |
回显消息 |
ECHO message |
SELECT |
切换数据库 |
SELECT 1 |
QUIT |
关闭连接 |
QUIT |
服务器管理命令
命令 |
描述 |
示例 |
INFO |
获取服务器信息 |
INFO |
CONFIG GET |
获取配置 |
CONFIG GET maxmemory |
CONFIG SET |
设置配置 |
CONFIG SET maxmemory 1GB |
DBSIZE |
返回当前数据库的键数量 |
DBSIZE |
FLUSHDB |
删除当前数据库所有键 |
FLUSHDB |
FLUSHALL |
删除所有数据库中的所有键 |
FLUSHALL |
TIME |
返回服务器时间 |
TIME |
SLOWLOG |
管理慢查询日志 |
SLOWLOG GET 10 |
CLIENT LIST |
获取客户端连接列表 |
CLIENT LIST |
MONITOR |
实时监控Redis命令 |
MONITOR |
高级应用
位图操作(Bitmap)
位图允许我们对字符串值的位进行操作,非常节省空间。
命令 |
描述 |
示例 |
SETBIT |
设置位的值 |
SETBIT key offset value |
GETBIT |
获取位的值 |
GETBIT key offset |
BITCOUNT |
统计位值为1的数量 |
BITCOUNT key [start end] |
BITOP |
对多个位图进行位运算 |
BITOP AND destkey key [key …] |
BITPOS |
查找第一个指定值的位 |
BITPOS key bit [start] [end] |
示例应用:用户签到记录
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| # 用户1在2021年第5天签到 > SETBIT user:1:sign:202102 5 1 (integer) 0
# 用户1在2021年第6天签到 > SETBIT user:1:sign:202102 6 1 (integer) 0
# 检查用户1是否在第5天签到 > GETBIT user:1:sign:202102 5 (integer) 1
# 获取用户1在2月份的签到次数 > BITCOUNT user:1:sign:202102 (integer) 2
|
HyperLogLog
HyperLogLog是用于基数统计的概率数据结构,占用空间极小。
命令 |
描述 |
示例 |
PFADD |
添加元素 |
PFADD key element [element …] |
PFCOUNT |
获取基数估算值 |
PFCOUNT key [key …] |
PFMERGE |
合并多个HyperLogLog |
PFMERGE destkey sourcekey [sourcekey …] |
示例应用:网站UV统计
1 2 3 4 5 6 7 8 9
| # 添加用户访问记录 > PFADD page:visits:today user1 user2 user3 (integer) 1 > PFADD page:visits:today user1 user4 (integer) 1
# 获取今日UV > PFCOUNT page:visits:today (integer) 4
|
地理空间(GEO)
GEO命令用于存储和查询经纬度坐标。
命令 |
描述 |
示例 |
GEOADD |
添加地理位置 |
GEOADD key longitude latitude member |
GEOPOS |
获取地理位置 |
GEOPOS key member [member …] |
GEODIST |
计算两点距离 |
GEODIST key member1 member2 [unit] |
GEORADIUS |
查找给定经纬度一定半径内的成员 |
GEORADIUS key longitude latitude radius unit |
示例应用:附近的餐厅
1 2 3 4 5 6 7 8 9 10 11 12 13
| # 添加餐厅位置 > GEOADD restaurants 116.39 39.91 "餐厅A" 116.40 39.90 "餐厅B" 116.38 39.92 "餐厅C" (integer) 3
# 计算两个餐厅的距离(米) > GEODIST restaurants "餐厅A" "餐厅B" m "1544.1996"
# 查找某坐标5公里内的餐厅 > GEORADIUS restaurants 116.38 39.90 5 km 1) "餐厅B" 2) "餐厅A" 3) "餐厅C"
|
流(Stream)
Redis 5.0新增的数据类型,用于实现消息队列。
命令 |
描述 |
示例 |
XADD |
添加消息 |
XADD key [MAXLEN ~count] ID field value [field value …] |
XREAD |
读取消息 |
XREAD [COUNT count] [BLOCK milliseconds] STREAMS key [key …] ID [ID …] |
XRANGE |
获取范围内的消息 |
XRANGE key start end [COUNT count] |
XLEN |
获取流的长度 |
XLEN key |
XDEL |
删除消息 |
XDEL key ID [ID …] |
XGROUP |
管理消费者组 |
XGROUP CREATE key groupname id-or-$ |
XREADGROUP |
从消费者组读取 |
XREADGROUP GROUP group consumer [COUNT count] [BLOCK milliseconds] [NOACK] STREAMS key [key …] ID [ID …] |
XACK |
确认消息处理 |
XACK key group ID [ID …] |
示例应用:日志收集系统
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| # 添加日志消息 > XADD logs * type "info" message "用户登录" user_id "123" "1613023671654-0"
# 读取日志消息 > XREAD COUNT 10 STREAMS logs 0 1) 1) "logs" 2) 1) 1) "1613023671654-0" 2) 1) "type" 2) "info" 3) "message" 4) "用户登录" 5) "user_id" 6) "123"
# 创建消费者组 > XGROUP CREATE logs logprocessors $ OK
# 从组中读取消息 > XREADGROUP GROUP logprocessors worker1 COUNT 1 STREAMS logs > 1) 1) "logs" 2) 1) 1) "1613023671654-0" 2) 1) "type" 2) "info" 3) "message" 4) "用户登录" 5) "user_id" 6) "123"
# 确认消息处理 > XACK logs logprocessors 1613023671654-0 (integer) 1
|
实战:常见应用场景的命令组合
分布式锁实现
1 2 3 4 5
| # 获取锁(带超时自动释放) SET resource_name unique_value NX PX 10000
# 释放锁(使用Lua脚本保证原子性) EVAL "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end" 1 resource_name unique_value
|
限流器实现
1 2 3 4 5 6 7 8
| # 简单计数器限流(每分钟允许10次请求) INCR rate:limit:user:123 EXPIRE rate:limit:user:123 60
# 滑动窗口限流 ZADD sliding:window:user:123 current_timestamp request_id ZREMRANGEBYSCORE sliding:window:user:123 0 (current_timestamp - window_size) ZCARD sliding:window:user:123
|
延迟队列实现
1 2 3 4 5 6
| # 添加延迟任务(score为执行时间戳) ZADD delayed:queue current_timestamp+delay task_data
# 获取到期任务 ZRANGEBYSCORE delayed:queue 0 current_timestamp LIMIT 0 1 ZREM delayed:queue task_id
|
排行榜实现
1 2 3 4 5 6 7 8 9 10 11
| # 更新得分 ZADD leaderboard score player_id
# 获取前10名 ZREVRANGE leaderboard 0 9 WITHSCORES
# 获取玩家排名 ZREVRANK leaderboard player_id
# 获取指定玩家前后5名 ZREVRANGE leaderboard (ZREVRANK leaderboard player_id) - 5 (ZREVRANK leaderboard player_id) + 5 WITHSCORES
|
性能优化技巧
使用管道(Pipeline)减少网络开销
1 2 3 4 5 6 7 8 9 10 11
| # 不使用管道 SET key1 value1 SET key2 value2 SET key3 value3
# 使用管道(在客户端实现) PIPELINE SET key1 value1 SET key2 value2 SET key3 value3 EXEC
|
使用Lua脚本保证原子性
1 2
| # 计数器递增并获取值,同时设置过期时间 EVAL "redis.call('INCR', KEYS[1]); redis.call('EXPIRE', KEYS[1], ARGV[1]); return redis.call('GET', KEYS[1]);" 1 counter 60
|
合理使用批量命令
1 2 3 4 5 6 7
| # 单个命令 GET key1 GET key2 GET key3
# 批量命令 MGET key1 key2 key3
|
避免使用昂贵的命令
- KEYS 命令在大型数据库上可能导致性能问题,使用 SCAN 代替
- SORT 命令在大数据集上开销大
- 长时间运行的Lua脚本可能阻塞Redis
- 大型集合上的交集、并集操作
总结
Redis命令丰富而强大,通过灵活组合这些命令,可以实现各种复杂的功能和应用场景。本文介绍了Redis的基础命令到高级应用,希望能帮助读者更好地理解和使用Redis。
在实际应用中,应根据业务场景选择合适的命令和数据结构,并遵循以下原则:
- 选择合适的数据结构:不同数据结构在不同场景下性能表现不同
- 使用批量操作:减少网络往返
- 避免大键值:拆分大键值,避免阻塞服务器
- 合理使用过期时间:避免无限制地增长内存
- 使用Lua脚本:保证操作的原子性和减少网络往返
当你熟练掌握Redis的各种命令后,可以更高效地利用Redis解决实际问题,构建高性能、可扩展的系统。
参考资料