Redis 数据结构全景解析
Redis 不是简单的 key-value 缓存,它更像一把“瑞士军刀”。
只要掌握数据结构,就能把同一份内存用出 10 倍效率。
0. 开场白:为什么聊数据结构?
面试常问“Redis 有几种数据类型?”——很多人答 5 种(String、List、Hash、Set、ZSet)。
但 Redis 7.0 源码里已出现 9 种编码(encoding),未来还会更多。
理解“类型 vs 编码”是区分菜鸟与专家的第一步。
对外类型(type) | 内部可能编码(encoding) | 场景关键字 |
---|---|---|
string | int、embstr、raw、sdshdr5… | 计数器、缓存 |
list | ziplist、quicklist | 消息队列 |
hash | ziplist、hashtable | 对象缓存 |
set | intset、hashtable | 去重、抽奖 |
zset | ziplist、skiplist | 排行榜 |
stream | rax + listpack | Kafka-lite |
bitmap / hyperloglog / geo | 特殊紧凑编码 | 大数据去重、LBS |
下面按使用频率由浅入深展开。
1. String:最被低估的“全能选手”
- 底层:简单动态字符串(SDS),预分配策略减少 80% 内存重分配。
- 三大绝技
- 缓存:SET / GET 常规操作。
- 计数器:INCR、INCRBY 原子自增,秒杀库存必备。
- 分布式锁:SET key value NX PX 30000 一条命令解决“SETNX + EXPIRE”非原子问题。
- 面试题:为什么 512 MB 以下都用 embstr?
答:embstr 将 redisObject 与 SDS 连续分配,一次 malloc,CPU cache 友好。
2. List:双端队列 & 阻塞队列
- 编码进化史:ziplist(<=3.2) → quicklist(4.0) → listpack(7.0)。
- 典型用法
- 栈:
LPUSH + LPOP
- 队列:
RPUSH + LPOP
- 阻塞队列:
BLPOP mylist 0
(0 表示永不超时)
- 栈:
- 坑:ziplist 转 quicklist 的阈值
list-max-ziplist-size -2
,负值表示节点字节限制。
3. Hash:小对象之王
- 阈值:hash-max-ziplist-entries 512 / hash-max-ziplist-value 64。
小 Hash 用 ziplist,大 Hash 自动转 hashtable,O(1) vs O(N) 差异巨大。 - 实战:
单 key 管理对象字段,比多个 String 省内存 30%+。HSET user:1001 name kim age 18 HINCRBY user:1001 age 1
4. Set:无序去重 + 数学集合
- 编码:全整数且元素 ≤ set-max-intset-entries 512 → intset,否则 hashtable。
- 花式用法
- 抽奖:
SRANDMEMBER lucky 1
随机 1 人 - 交并差:
SINTER tag:redis tag:cache
找出共同标签的文章。
- 抽奖:
5. Sorted Set:排行榜 & 延迟队列
- 底层:skiplist + dict 双索引,O(logN) 范围查询 + O(1) 成员定位。
- 排行榜
ZINCRBY rank:2025 1 player:42 ZREVRANGE rank:2025 0 9 WITHSCORES
- 延迟队列:score 存执行时间戳,用
ZRANGEBYSCORE
定时轮询即可。
6. Stream:Redis 的 Kafka
- 组成:
- 消息:
XADD mystream * field value
- 消费组:
XGROUP CREATE mystream g1 $
- 消息:
- 特点:
- 消息持久化,重启不丢。
- 支持 ACK、PEL(pending list),比 List 做队列更可靠。
7. 三大“特种类型”
类型 | 命令示例 | 用途 |
---|---|---|
Bitmap | SETBIT uv:20250801 10086 1 | 日活统计(1 bit/用户) |
HyperLogLog | PFADD ip:20250801 1.1.1.1 | 去重计数,误差 < 0.81% |
Geo | GEOADD shops 116.397 39.909 “beijing” | 附近 5 km 门店 |
8. 内存优化 3 板斧
- 编码对齐:
DEBUG OBJECT key
看 encoding,针对性调阈值。 - 压缩:开启
list-compression-depth 2
对中间节点压缩。 - 短结构:小数据量用 ziplist/listpack,可把内存压到 1/8。
9. 小结与思维导图
Redis 数据结构 = 对外类型 + 内部编码 + 阈值参数↓用对结构,省内存,提性能