Redis 核心数据类型完全手册(详细版)
一、通用键操作(所有类型通用)
| 命令 | 语法 | 详细说明 |
|---|---|---|
keys | keys pattern | 查找所有匹配 pattern 的键,如 keys * 返回所有键,keys user:* 返回所有以 user: 开头的键。⚠️ 生产环境慎用,数据量大时会阻塞 Redis |
type | type key | 返回键对应值的数据类型,返回值:string、list、set、hash、zset、none(不存在) |
del | del key [key ...] | 删除一个或多个键,返回成功删除的个数 |
exists | exists key [key ...] | 判断键是否存在,返回存在的个数(注意:多个键时返回存在的数量) |
ttl | ttl key | 查看键剩余的过期时间,单位:秒 |
expire | expire key seconds | 为键设置过期时间(秒),到期后自动删除 |
pexpire | pexpire key milliseconds | 为键设置过期时间(毫秒) |
persist | persist key | 移除键的过期时间,使其变为永久有效 |
ttl 返回值详解:
| 返回值 | 含义 | 说明 |
|---|---|---|
-2 | 键不存在 | 该键已被删除或从未存在 |
-1 | 永久有效 | 键没有设置过期时间 |
>=0 | 剩余秒数 | 键距离过期还剩多少秒 |
示例:
bash
127.0.0.1:6379> set name zhangsan OK 127.0.0.1:6379> expire name 60 (integer) 1 127.0.0.1:6379> ttl name (integer) 58 127.0.0.1:6379> persist name (integer) 1 127.0.0.1:6379> ttl name (integer) -1 127.0.0.1:6379> del name (integer) 1 127.0.0.1:6379> ttl name (integer) -2
二、字符串(String)
底层原理: 简单动态字符串(SDS),可以存储字符串、整数、浮点数,最大支持 512MB。
| 命令 | 语法 | 详细说明 |
|---|---|---|
set | set key value [EX seconds] [PX milliseconds] [NX|XX] | 设置键值对。EX:过期秒数;PX:过期毫秒数;NX:仅当键不存在时设置;XX:仅当键存在时设置 |
get | get key | 获取键对应的值,不存在返回 (nil) |
mset | mset key1 value1 key2 value2 ... | 批量设置多个键值对,原子操作 |
mget | mget key1 key2 ... | 批量获取多个键的值 |
incr | incr key | 将键的值加 1(值必须为数字),不存在则初始为 0 再加 1 |
decr | decr key | 将键的值减 1(值必须为数字) |
incrby | incrby key n | 将键的值增加 n(n 可为正负整数) |
decrby | decrby key n | 将键的值减少 n |
incrbyfloat | incrbyfloat key n | 将键的值增加 n(n 可为浮点数) |
strlen | strlen key | 获取键对应值的字符串长度(字节数) |
append | append key value | 在原有值末尾追加内容,返回新值的长度 |
getset | getset key value | 设置新值并返回旧值(原子操作) |
setnx | setnx key value | 仅当键不存在时设置(等同于 set key value NX),常用于分布式锁 |
示例:
bash
127.0.0.1:6379> set name zhangsan OK 127.0.0.1:6379> get name "zhangsan" 127.0.0.1:6379> strlen name (integer) 8 127.0.0.1:6379> append name lisi (integer) 12 127.0.0.1:6379> get name "zhangsanlisi" 127.0.0.1:6379> set age 18 OK 127.0.0.1:6379> incr age (integer) 19 127.0.0.1:6379> incrby age 5 (integer) 24 127.0.0.1:6379> decrby age 3 (integer) 21
三、列表(List)—— 有序、可重复
底层原理: Redis 3.2 之前使用压缩列表(ziplist)或双向链表(linkedlist),3.2 之后统一使用 quicklist(压缩列表 + 双向链表的混合体)。特点: 有序、可重复、支持双端操作,最多存储 2^32 - 1 个元素。
| 命令 | 语法 | 详细说明 |
|---|---|---|
lpush | lpush key value [value ...] | 将一个或多个值从左侧(头部)插入列表,返回插入后列表长度 |
rpush | rpush key value [value ...] | 将一个或多个值从右侧(尾部)插入列表,返回插入后列表长度 |
lpop | lpop key | 从左侧(头部)移除并返回第一个元素,列表为空返回 (nil) |
rpop | rpop key | 从右侧(尾部)移除并返回最后一个元素,列表为空返回 (nil) |
blpop | blpop key [key ...] timeout | 阻塞式左弹出,列表为空则阻塞等待 timeout 秒,超时返回 (nil) |
brpop | brpop key [key ...] timeout | 阻塞式右弹出,列表为空则阻塞等待 |
llen | llen key | 获取列表长度(元素个数) |
lrange | lrange key start stop | 获取指定索引范围内的元素。索引从 0 开始,-1 表示最后一个元素 |
lindex | lindex key index | 获取指定索引位置的元素,索引可为负数(-1 表示最后一个) |
lset | lset key index value | 修改指定索引位置的元素值,索引越界报错 |
linsert | linsert key BEFORE|AFTER pivot value | 在列表中某个指定元素的前/后插入新元素 |
lrem | lrem key count value | 移除列表中与 value 相等的元素。count > 0 从头部开始移除 count 个;count < 0 从尾部开始移除 |count| 个;count = 0 移除所有 |
rpoplpush | rpoplpush source destination | 将 source 列表尾元素弹出并推入 destination 列表头部(原子操作) |
示例:
bash
127.0.0.1:6379> lpush list a b c (integer) 3 127.0.0.1:6379> lrange list 0 -1 1) "c" 2) "b" 3) "a" 127.0.0.1:6379> rpush list x y z (integer) 6 127.0.0.1:6379> lrange list 0 -1 1) "c" 2) "b" 3) "a" 4) "x" 5) "y" 6) "z" 127.0.0.1:6379> lpop list "c" 127.0.0.1:6379> rpop list "z" 127.0.0.1:6379> llen list (integer) 4
四、集合(Set)—— 无序、不重复
底层原理: 当元素都为整数且数量较少时使用整数集合(intset),否则使用哈希表(dict,与 Java 的 HashSet 类似)。特点: 无序、元素唯一(自动去重)、支持集合运算,最多存储 2^32 - 1 个元素。
| 命令 | 语法 | 详细说明 |
|---|---|---|
sadd | sadd key member [member ...] | 向集合中添加一个或多个元素,已存在的元素会被忽略,返回成功添加的元素个数 |
srem | srem key member [member ...] | 从集合中移除一个或多个元素,返回成功移除的元素个数(图中 arem 实际为 srem) |
smembers | smembers key | 获取集合中所有元素。⚠️ 元素量大时慎用,会阻塞 Redis |
sismember | sismember key member | 判断元素是否在集合中,存在返回 1,不存在返回 0 |
scard | scard key | 获取集合中元素的个数 |
sinter | sinter key [key ...] | 求多个集合的交集(共同元素) |
sinterstore | sinterstore destination key [key ...] | 求交集并将结果存储到 destination 集合中 |
sunion | sunion key [key ...] | 求多个集合的并集(所有不重复元素) |
sunionstore | sunionstore destination key [key ...] | 求并集并将结果存储到 destination 集合中 |
sdiff | sdiff key [key ...] | 求多个集合的差集(第一个集合中有但其他集合中没有的元素) |
sdiffstore | sdiffstore destination key [key ...] | 求差集并将结果存储到 destination 集合中 |
spop | spop key [count] | 从集合中随机移除并返回一个或多个元素 |
srandmember | srandmember key [count] | 从集合中随机返回一个或多个元素(不移除) |
smove | smove source destination member | 将元素从 source 集合移动到 destination 集合(原子操作) |
示例:
bash
127.0.0.1:6379> sadd fruits apple banana orange apple (integer) 2 # apple 重复,只添加了 apple 和 banana,orange 已存在? 实际返回2说明apple已存在被忽略 127.0.0.1:6379> smembers fruits 1) "banana" 2) "apple" 3) "orange" 127.0.0.1:6379> sadd set1 a b c d (integer) 4 127.0.0.1:6379> sadd set2 c d e f (integer) 4 127.0.0.1:6379> sinter set1 set2 1) "c" 2) "d" 127.0.0.1:6379> sunion set1 set2 1) "a" 2) "b" 3) "c" 4) "d" 5) "e" 6) "f" 127.0.0.1:6379> sdiff set1 set2 1) "a" 2) "b" 127.0.0.1:6379> sismember set1 a (integer) 1 127.0.0.1:6379> sismember set1 x (integer) 0
五、哈希(Hash)—— 键值对集合
底层原理: 当字段数量少且值较小时使用压缩列表(ziplist),否则使用哈希表(dict,与 Java 的 HashMap 类似)。特点: 适合存储对象,可单独操作对象的某个字段,最多存储 2^32 - 1 个字段。
| 命令 | 语法 | 详细说明 |
|---|---|---|
hset | hset key field value [field value ...] | 设置哈希表中一个或多个字段的值,返回成功新增的字段个数 |
hget | hget key field | 获取哈希表中指定字段的值,字段不存在返回 (nil) |
hgetall | hgetall key | 获取哈希表中所有字段和值(交替返回:field1, value1, field2, value2...)。⚠️ 字段量大时慎用 |
hdel | hdel key field [field ...] | 删除哈希表中一个或多个字段,返回成功删除的字段个数 |
hexists | hexists key field | 判断哈希表中是否存在指定字段,存在返回 1,不存在返回 0 |
hlen | hlen key | 获取哈希表中字段的数量 |
hkeys | hkeys key | 获取哈希表中所有字段名 |
hvals | hvals key | 获取哈希表中所有字段值 |
hmset | hmset key field value [field value ...] | 批量设置多个字段值(功能同 hset,已废弃但仍可用) |
hmget | hmget key field [field ...] | 批量获取多个字段的值 |
hincrby | hincrby key field n | 将哈希表中指定字段的整数值增加 n |
hincrbyfloat | hincrbyfloat key field n | 将哈希表中指定字段的浮点数值增加 n |
hsetnx | hsetnx key field value | 仅当字段不存在时设置(原子操作) |
示例:
bash
127.0.0.1:6379> hset user:1001 name zhangsan age 25 city beijing (integer) 3 127.0.0.1:6379> hget user:1001 name "zhangsan" 127.0.0.1:6379> hgetall user:1001 1) "name" 2) "zhangsan" 3) "age" 4) "25" 5) "city" 6) "beijing" 127.0.0.1:6379> hincrby user:1001 age 1 (integer) 26 127.0.0.1:6379> hexists user:1001 email (integer) 0 127.0.0.1:6379> hdel user:1001 city (integer) 1
六、有序集合(ZSet)—— 带权重、有序、不重复
底层原理: 当元素数量少且值较小时使用压缩列表(ziplist),否则使用跳表(skiplist)+ 哈希表(dict) 的组合。跳表负责范围查询和排序,哈希表负责 O(1) 的单点查询。特点: 元素唯一(不可重复),每个元素绑定一个权重(score),按权重自动升序排序,支持范围查询。
| 命令 | 语法 | 详细说明 |
|---|---|---|
zadd | zadd key [NX|XX] [GT|LT] [CH] [INCR] score member [score member ...] | 添加一个或多个元素(带权重)。权重为数字(整数或浮点数),元素已存在则更新权重 |
zrem | zrem key member [member ...] | 移除一个或多个元素,返回成功移除的个数 |
zcard | zcard key | 获取有序集合中元素的个数 |
zrange | zrange key start stop [WITHSCORES] | 按索引范围获取元素(升序,权重小→大)。WITHSCORES 同时返回权重值 |
zrevrange | zrevrange key start stop [WITHSCORES] | 按索引范围获取元素(降序,权重大→小) |
zrangebyscore | zrangebyscore key min max [WITHSCORES] [LIMIT offset count] | 按权重范围获取元素(升序)。min 和 max:-inf 表示负无穷,+inf 表示正无穷;(10 表示不包含 10 |
zrevrangebyscore | zrevrangebyscore key max min [WITHSCORES] | 按权重范围获取元素(降序) |
zscore | zscore key member | 获取指定元素的权重值,不存在返回 (nil) |
zrank | zrank key member | 获取指定元素的排名(升序,从 0 开始),不存在返回 (nil) |
zrevrank | zrevrank key member | 获取指定元素的排名(降序,从 0 开始) |
zcount | zcount key min max | 统计权重在 min 和 max 之间的元素个数 |
zincrby | zincrby key increment member | 将指定元素的权重增加 increment(正数增加,负数减少) |
zremrangebyrank | zremrangebyrank key start stop | 按索引范围删除元素 |
zremrangebyscore | zremrangebyscore key min max | 按权重范围删除元素 |
权重排序规则:
-
默认按权重从小到大(升序)排列
-
权重相同的情况下,按元素本身的字典序排列(如
"a"<"b")
示例(基于你的截图):
bash
# 添加 3 个元素,权重分别为 10、50、30 127.0.0.1:6379> zadd zs1 10 zhangsan 50 lisi 30 wangwu (integer) 3 # 再添加一个元素,权重 15 127.0.0.1:6379> zadd zs1 15 zhaoliu (integer) 1 # 删除 zhaoliu 127.0.0.1:6379> zrem zs1 zhaoliu (integer) 1 # 查看集合大小(目前剩 3 个) 127.0.0.1:6379> zcard zs1 (integer) 3 # 删除 wangwu 127.0.0.1:6379> zrem zs1 wangwu (integer) 1 # 再次查看大小(剩 2 个) 127.0.0.1:6379> zcard zs1 (integer) 2 # 按索引取第 0~1 个(升序,权重最小在前) 127.0.0.1:6379> zrange zs1 0 1 1) "zhangsan" # 权重 10 2) "lisi" # 权重 50 # 按索引取全部 127.0.0.1:6379> zrange zs1 0 5 1) "zhangsan" 2) "lisi" # 按权重范围取 10~20 之间的元素 127.0.0.1:6379> zrangebyscore zs1 10 20 1) "zhangsan" # 只有 zhangsan 权重为 10 # 按权重范围取 10~100 之间的元素 127.0.0.1:6379> zrangebyscore zs1 10 100 1) "zhangsan" # 权重 10 2) "lisi" # 权重 50 # 查看元素权重(存在) 127.0.0.1:6379> zscore zs1 zhangsan "10" # 查看元素权重(不存在) 127.0.0.1:6379> zscore zs1 zhangli (nil) # 查看元素排名(升序,从 0 开始) 127.0.0.1:6379> zrank zs1 zhangsan (integer) 0 127.0.0.1:6379> zrank zs1 lisi (integer) 1
七、五种数据类型对比总结
| 对比维度 | String | List | Set | Hash | ZSet |
|---|---|---|---|---|---|
| 是否有序 | — | ✅ 有序(插入顺序) | ❌ 无序 | — | ✅ 有序(权重排序) |
| 是否可重复 | — | ✅ 可重复 | ❌ 不可重复 | — | ❌ 不可重复 |
| 是否有权重 | — | ❌ 无 | ❌ 无 | — | ✅ 有 score |
| 底层结构 | SDS | quicklist | intset / dict | ziplist / dict | ziplist / skiplist+dict |
| 范围查询 | 不支持 | 支持按索引 | 不支持 | 不支持 | 支持按索引/权重 |
| 集合运算 | 不支持 | 不支持 | ✅ 支持交集/并集/差集 | 不支持 | ✅ 支持交集/并集(需特殊命令) |
| 典型场景 | 缓存、计数器、分布式锁 | 消息队列、最新列表、日志 | 去重、共同好友、抽奖、标签 | 存储对象(用户信息、商品信息) | 排行榜、优先级队列、延时任务 |
八、Set 与 ZSet 对比详解
| 对比维度 | Set(集合) | ZSet(有序集合) |
|---|---|---|
| 排序方式 | 无序(按哈希随机存储) | 按 score 权重升序排列 |
| 权重概念 | ❌ 无 | ✅ 每个元素都有一个 score 权重 |
| 范围查询 | ❌ 不支持(只能取全部) | ✅ 支持按索引范围(zrange)和按权重范围(zrangebyscore) |
| 相同点 | 元素都是 String 类型,且都不允许重复 | 元素都是 String 类型,且都不允许重复 |
| 底层结构 | intset / dict | ziplist / skiplist + dict |
| 典型场景 | 共同好友、好友推荐、抽奖池去重 | 游戏排行榜、带权重的任务队列、延时队列 |
九、高频面试题及追问
Q1:zrange 和 zrangebyscore 有什么区别?
| 命令 | 排序方式 | 筛选条件 | 用途 |
|---|---|---|---|
zrange | 按权重排序(升序/降序) | 按索引位置筛选 | 取第几名到第几名,如"取前三名" |
zrangebyscore | 按权重排序(升序/降序) | 按权重数值范围筛选 | 取分数在某个区间内的,如"取分数在 80~100 之间的" |
Q2:ZSet 的底层为什么用跳表而不用红黑树/B+树?
-
跳表实现简单,代码量远小于红黑树
-
范围查询效率高:跳表在范围查询时比红黑树更友好(红黑树需要中序遍历)
-
并发友好:跳表更容易实现无锁/细粒度锁
-
B+树主要用于磁盘存储(如 MySQL),Redis 在内存中,跳表足够
Q3:keys * 为什么不推荐在生产环境使用?
-
Redis 是单线程模型
-
keys *会遍历整个键空间,时间复杂度 O(n) -
数据量大时会长时间阻塞 Redis,导致所有其他请求超时
-
替代方案:使用
SCAN命令渐进式遍历,每次只返回少量数据,不阻塞
Q4:ttl 返回 -1 和 -2 分别代表什么?
| 返回值 | 含义 | 场景 |
|---|---|---|
-1 | 永久有效,无过期时间 | 使用 persist 移除过期时间后;或从未设置过期时间 |
-2 | 键不存在或已过期被删除 | 键被 del 删除;或 expire 到期后自动删除 |
Q5:ZSet 中权重相同怎么办?排序规则是什么?
-
先按权重从小到大排序
-
权重相同时,按元素值的字典序排列(如
"a"<"b"<"c")
Q6:Hash 和 String(存 JSON)的区别?各自适用场景?
| 对比维度 | Hash | String(JSON) |
|---|---|---|
| 修改字段 | 可单独修改某个字段(hset),无需整体覆盖 | 修改一个字段需要整体读取→修改→整体写入 |
| 内存占用 | 字段少时更省内存(ziplist 压缩) | 整体存为字符串,开销较大 |
| 读取便利性 | 可单独获取某个字段(hget) | 需要整体取出后解析 JSON |
| 适用场景 | 对象字段多、需要频繁修改部分字段 | 对象整体读取为主、不常修改 |
Q7:Redis 的过期删除策略是什么?
-
惰性删除:访问 key 时检查是否过期,过期则删除
-
定期删除:每隔一段时间(默认 100ms)随机抽取一批 key 检查并删除过期 key
-
两者结合:惰性删除保证 CPU 不浪费,定期删除保证过期 key 不长期占用内存
Q8:Redis 的内存淘汰策略有哪些?(当内存满了)
| 策略 | 说明 |
|---|---|
noeviction | 不淘汰,写入报错 |
allkeys-lru | 所有 key 中淘汰最近最少使用 |
volatile-lru | 设置了过期时间的 key 中淘汰 LRU |
allkeys-random | 所有 key 中随机淘汰 |
volatile-random | 设置了过期时间的 key 中随机淘汰 |
volatile-ttl | 淘汰设置了过期时间且剩余 TTL 最短的 key |
allkeys-lfu | 所有 key 中淘汰最不经常使用 |
volatile-lfu | 设置了过期时间的 key 中淘汰 LFU |
十、业务场景速查
| 业务场景 | 推荐数据结构 | 使用命令示例 |
|---|---|---|
| 缓存用户信息 | Hash | hset user:1001 name zhangsan age 25 |
| 分布式锁 | String | set lock:key uuid NX EX 10 |
| 文章浏览量/点赞数 | String | incr article:1001:views |
| 最新消息列表(按时间) | List | lpush news:list "news1" → lrange news:list 0 9 |
| 消息队列(生产者-消费者) | List | 生产者 rpush queue task,消费者 lpop queue 或 brpop queue 5 |
| 抽奖去重(防止重复中奖) | Set | sadd lottery:2026 user1001 → smembers lottery:2026 |
| 共同好友/好友推荐 | Set | sinter user:1001:friends user:1002:friends |
| 商品标签/文章标签 | Set | sadd article:1:tags java redis → sinter article:1:tags article:2:tags |
| 实时排行榜(如游戏排行榜) | ZSet | zadd rank 100 player1 → zrevrange rank 0 9 WITHSCORES |
| 带权重的任务队列(优先处理高权重) | ZSet | zadd task_queue 10 task_a → zrangebyscore task_queue 0 10 |
| 延时任务(按时间权重排序) | ZSet | 用当前时间戳做权重:zadd delay_queue $(date +%s) task,定时任务取权重小于当前时间的 |
| 商品筛选(按价格/销量区间) | ZSet | zrangebyscore products:price 100 500 |
386



被折叠的 条评论
为什么被折叠?



