Hash 的编码是什么
Hash 类型的底层实现有两种:字段少、字段值小时用 listpack 把全部字段值连续存放在一块紧凑内存里,省去指针与哈希表的额外开销;字段数或单个字段长度超过阈值后转为 hashtable,每个字段用字典条目表示,查询与更新稳定高效但内存开销明显上升。阈值由 hash-max-listpack-entries 与 hash-max-listpack-value 两个配置决定,7.0 之前的命名是 ziplist 系列。
基本信息
| 两种编码 | listpack(紧凑)与 hashtable(字典) |
|---|---|
| 条目阈值 | hash-max-listpack-entries 默认 128 |
| 值长度阈值 | hash-max-listpack-value 默认 64 字节 |
| 查看方式 | OBJECT ENCODING key |
| 命名变化 | 7.0 起配置项由 ziplist 改为 listpack,旧名作为别名保留 |
典型使用场景
- 对象缓存:用户资料、商品属性这类字段数不多的对象,listpack 编码下内存占用接近理论上限的紧凑程度
- 按维度聚合计数:把一个实体的多种计数放进一个 Hash,字段级 HINCRBY 原子递增
- 配置与开关集合:字段数量可控、读多写少的键值对集合
- 内存优化核查:用 OBJECT ENCODING 确认线上键是否真的处在紧凑编码,而不是已经悄悄升级
- 容量评估:按编码阈值估算每个键的平均开销,判断是否需要调整阈值或改变数据组织方式
观察编码变化
观察编码变化
redis-cli HSET user:1 name alice age 30
redis-cli OBJECT ENCODING user:1 # listpack
redis-cli CONFIG GET hash-max-listpack-entries # 默认 128
redis-cli CONFIG GET hash-max-listpack-value # 默认 64
redis-cli HSET user:1 bio <一段超过 64 字节的文本>
redis-cli OBJECT ENCODING user:1 # raw 或 hashtable,已升级
redis-cli MEMORY USAGE user:1 # 对比升级前后的实际占用编码对照
| 编码 | 适用条件 | 特点 |
|---|---|---|
| listpack | 字段数与字段长度都在阈值内 | 连续内存、无指针开销、内存最省 |
| hashtable | 字段数或字段长度超阈值 | 查询稳定 O(1),内存开销明显更高 |
| 不可逆 | 一旦升级不会自动退回 | 删除元素后仍保持 hashtable |
| 阈值可调 | 通过 CONFIG SET 调整 | 调大能让更多键保持紧凑,但单命令阻塞时间随之上升 |
编码升级是单向的:Hash 从 listpack 转为 hashtable 后,即使把字段删到只剩一个也不会退回紧凑编码,只有键被删除后重新写入才会重新开始。因此阈值调整对存量数据无效,只影响此后新建的键。
把阈值调大能让更多键保持紧凑,但单个键包含的元素越多,对该键执行 HGETALL 或删除整个键时的单次阻塞时间越长。批量操作大 Hash 会长时间占用主线程,需要评估元素规模后再决定阈值。