Hash 编码与内存优化是什么
小 Hash 在 Redis 里用紧凑结构存放(7.0 起为 listpack,之前为 ziplist),字段数或字段长度超过阈值后自动转成 hashtable。这个转换只影响内存占用与访问代价,不影响可用性:listpack 逐字节顺序存放、查找是线性扫描但内存极省;hashtable 读写为 O(1) 均摊但每个字段都有额外开销。控制字段数与 value 长度在阈值内,是 Redis 内存优化最直接的手段之一。
基本信息
| 适用版本 | Redis 7.0+ 使用 listpack 与 hash-max-listpack-*;6.x 及以前为 ziplist 与 hash-max-ziplist-* |
|---|---|
| 条目阈值 | hash-max-listpack-entries 默认 128 |
| 单值阈值 | hash-max-listpack-value 默认 64 字节 |
| 查看编码 | OBJECT ENCODING key |
| 转换方向 | 超过阈值即转为 hashtable,不会自动转回 |
| 查看阈值 | CONFIG GET hash-max-listpack-entries(旧版本为 hash-max-ziplist-entries) |
典型使用场景
- 内存优化排查:确认大量小 Hash 是否仍是紧凑编码
- 缓存结构设计:把对象字段控制在阈值内以节省内存
- 对比不同集群内存差异:编码差异常是主要原因
- 评估写入模式:频繁追加字段导致提前转为 hashtable
- 容量评审:用 OBJ_ENCODING 与 MEMORY USAGE 估算真实占用
查看与阈值
查看与阈值
HSET h f1 v1
OBJECT ENCODING h # listpack(旧版本显示 ziplist)
CONFIG GET hash-max-listpack-entries # 128
CONFIG GET hash-max-listpack-value # 64
# 写入一个超 64 字节的值后,整个 Hash 转为 hashtable
HSET h big $(python3 -c "print('a'*100)")
OBJECT ENCODING h # hashtable
# 字段数超过阈值同样会触发转换(转换后不可逆)
OBJECT ENCODING h; MEMORY USAGE h编码对照
| 编码 | 产生条件 | 特点 |
|---|---|---|
| listpack(7.0+) | 字段数 ≤ 128 且值 ≤ 64 字节 | 内存紧凑,线性查找,适合小对象 |
| ziplist(6.x 及以前) | 同样的阈值语义 | 旧命名,参数名带 ziplist |
| hashtable | 字段数或单值超过阈值 | O(1) 访问,内存开销更大 |
| 转换方向 | 单向:listpack → hashtable | 老键不会自动转回紧凑编码 |
编码转换不可逆:设计时把字段数与 value 大小控制在阈值内能省下明显内存;已经转成 hashtable 的键,需要删除重建(或用 CONFIG SET 调大阈值后重新写入)才可能恢复紧凑存储。
调大阈值不是免费的:listpack 上单键访问是线性扫描,阈值设得过大(如几万字段)会让单次 HGET 变慢,必须与访问频率一起权衡;变更参数后只对新写入生效。