Redis Hash 数据结构演进史

从压缩列表到字典的完美平衡

Redis 2.0

ziplist 实现 Hash

2010 年,Hash 类型诞生。采用 ziplist(压缩列表)作为底层实现, field-value 对连续存储,内存紧凑。

// ziplist 存储结构
|zlbytes|zltail|zllen|field1|val1|field2|val2|...|zlend|

• zlbytes: 列表总字节数
• zltail: 尾节点偏移量
• zllen: 节点数量
•zlend: 结束标记 (0xFF)
连续内存存储 O(1) 首尾操作 小数据友好 无指针开销
Redis 2.2

字典雏形

开始引入 dict 字典结构,为后续编码转换做准备。 Hash 增长超过阈值时开始考虑字典实现。

// dictEntry 结构
void *key 8 bytes
void *val 8 bytes
dictEntry *next 8 bytes
每个 bucket: 24 bytes
O(1) 单点查询 哈希冲突链
Redis 3.0 ⭐ 重大改进

ziplist ↔ dict 编码转换

引入完整的编码转换机制。当 Hash 字段数小于 hash-max-ziplist-entries(512) 且每个 field/value 长度小于 hash-max-ziplist-value(64字节)时使用 ziplist, 否则转换为 dict。

ziplist 编码
优点: 内存紧凑
缺点: 查询 O(n)
适用: 小数据量
dict 编码
优点: 查询 O(1)
缺点: 内存开销大
适用: 大数据量
自动编码转换 阈值可配置 512 字段阈值 64 字节限制
Redis 4.0

渐进式 Rehash

引入渐进式 rehash 机制。当 dict 需要扩容/缩容时, 分多次完成,避免一次性大内存迁移导致的阻塞。

// 渐进式 Rehash 流程
Step 1: 创建新哈希表 ht[1]
Step 2: 每次操作迁移 1 个 bucket
Step 3: 迁移完成 → 交换 ht[0] ↔ ht[1]
特点: 读写操作分摊重哈希成本
零阻塞扩容 O(1) 均摊复杂度 后台分片执行
Redis 6.0

Hash 编码参数调整

优化 hash-max-ziplist-entries 默认值从 512 → 1000, hash-max-ziplist-value 从 64 → 512 字节。 适应更复杂的应用场景。

// Redis 6.0 配置
hash-max-ziplist-entries 1000
hash-max-ziplist-value 512 bytes
更大 ziplist 阈值 更灵活配置
Redis 7.0+ ⭐ 重大变革

listpack 替换 ziplist

Redis 7.0 将 Hash 的 ziplist 替换为 listpack。 listpack 是 ziplist 的改进版,解决了 ziplist 级联更新问题。

// listpack vs ziplist 区别
ziplist: 每个节点存储前节点长度
    → 删除中间节点可能级联更新

listpack: 存储总长度,跳过式遍历
    → 删除操作无需更新后续节点
无级联更新 listpack 编码 向后兼容 性能更稳定

Hash 编码转换机制

Redis 根据数据量自动选择最优编码,平衡内存与性能

小数据量: listpack/ziplist

适用条件:
• 字段数 < 1000
• 每 field/value < 512 bytes

优势:
• 内存紧凑
• CPU 缓存友好
• 无指针开销
field|value 连续存储
f1
v1
f2
v2

大数据量: dict (字典)

触发条件:
• 字段数 ≥ 1000

• field/value ≥ 512 bytes

结构:
• 哈希表 + 链表解决冲突
• O(1) 单点查询
哈希桶数组
k1→v1
-
k2→v2
-
k3→v3

编码阈值配置

hash-max-ziplist-entries 1000
hash-max-ziplist-value 512 bytes

Hash 编码对比

查询性能

ziplist/listpack O(n)
dict O(1)

内存占用

ziplist/listpack 极低
dict 较高

适用场景

ziplist/listpack 小数据量
dict 大数据量

级联更新

ziplist ❌ 存在
listpack ✅ 无
dict ✅ 无

演进总结

Redis Hash 从最初单一的 ziplist 实现,演变为智能的双编码系统:

ziplist/listpack自动转换dict

核心优化路径:
小数据省内存(紧凑存储)| 大数据保性能(O(1)查询)| 渐进式扩容(零阻塞)

这套"自动档"编码切换机制,让 Redis Hash 能够在不同数据规模下都保持最优表现。