适用读者:只会用 string 存 JSON 的人。
前置:S12-B01。
实测环境:Redis 7.0.15(2026-11-05)。
覆盖旧文:861(110) 1578(126) 882(91) = 327 点。
覆盖扩展:E-S12-003(编码升级阈值)、E-S12-011(SDS/quicklist 结构)。
上一篇:S12-B01 · 单线程为什么还快
下一篇:S12-B03 · 过期、淘汰与内存。
1. 一句话结论
Redis 的"五种类型"对外是逻辑结构,对内是多种编码:小规模用紧凑编码(listpack/intset),超阈值才升级成标准结构(hashtable/skiplist/quicklist)。
实测:
u string embstr
l list quicklist
h hash listpack
z zset listpack
s set hashtable
| key | 类型 | 编码 | 为什么 |
|---|---|---|---|
u("hello") |
string | embstr |
短字符串一次性分配 |
l(a,b,c) |
list | quicklist |
7.0 的 list 统一 quicklist |
h(1 个字段) |
hash | listpack |
小 hash 用紧凑数组 |
z(2 个成员) |
zset | listpack |
小 zset 用紧凑数组 |
s(x,y) |
set | hashtable |
字符串成员:7.0 无 listpack set,直接哈希 |
2. 五种类型与适用场景
| 类型 | 结构 | 典型用途 |
|---|---|---|
| string | 二进制安全字符串 | 缓存 JSON、计数器(INCR) |
| hash | 字段→值 | 对象属性、购物车 |
| list | 双向链表 | 队列/栈、简单消息 |
| set | 无序集合 | 去重、交并差(共同好友) |
| zset | 跳表 + 哈希 | 排行榜、延迟队列(score=时间) |
判据:"我要按什么访问数据?" 按 key → string;按字段 → hash;按顺序 → list;去重/集合运算 → set;按分数排序 → zset。
3. 编码升级阈值(E-S12-003)
| 类型 | 紧凑编码 | 升级条件(默认) |
|---|---|---|
| string | int/embstr |
长度 > 44 字节 → raw |
| hash | listpack |
字段 > 128 或值 > 64 字节 → hashtable |
| zset | listpack |
成员 > 128 或值 > 64 字节 → skiplist |
| set | intset(全整数)/listpack(7.2+) |
元素 > 512 或非整数 → hashtable |
| list | quicklist |
一直是(内部节点用 listpack) |
实测验证:200 个元素的 list 仍然是 quicklist(list 没有"升级"这一说,只有节点内 listpack 的大小)。
"为什么阈值重要":listpack 省内存(无指针),但 O(n) 查找;hashtable 快但占内存。 阈值是"内存 vs 速度"的分界。
4. ZSet 为什么用跳表(A-882)
| 结构 | 范围查询 | 实现复杂度 |
|---|---|---|
| 平衡树 | O(log n) | 高(旋转) |
| 跳表 | O(log n) | 低(多层索引 + 随机层高) |
跳表的优势:实现简单、支持无锁并发(虽然 Redis 单线程用不上)、范围查询天然有序。
"为什么不用红黑树":范围查询时跳表只需沿底层链表走,红黑树需中序遍历。
5. 内存优化实践
| 手段 | 说明 |
|---|---|
| 控制元素数 | 保持在阈值内可省大量内存 |
| 用 hash 代替多个 key | 小对象聚合(注意别变大 key,A02) |
| 值不要太长 | 超过 64 字节会升级编码 |
用 OBJECT ENCODING 检查 |
上线前确认编码符合预期 |
6. 五个误区
| 误区 | 真相 |
|---|---|
| "五种类型就五种结构" | 每类有 2–4 种编码 |
| "list 一定是链表" | 是 quicklist(链表 of listpack) |
| "set 都是哈希表" | 全整数时是 intset |
| "zset 一定用跳表" | 小规模用 listpack |
| "编码不影响性能" | 编码决定内存与复杂度 |
7. 自检
- 五种类型各自的典型用途?
embstr与raw的分界是多少字节?- hash 什么时候从 listpack 升级?
- zset 为什么选跳表而不是红黑树?
- 怎么确认一个 key 的实际编码?
8. 下一篇
S12-B03 · 过期、淘汰与内存
编码讲完,下一篇讲"内存满了怎么办":过期删除策略与八种淘汰策略(含实测淘汰 29,803 个 key)。
本篇属于系列《S12 · Redis 与缓存》。旧文来源:A-861/1578/882;扩展点 E-S12-003/011。编码输出为 Redis 7.0.15 实测。