redis 的 hash 在元素个数 ≤ hash-max-ziplist-entries 且每个字段和值长度 ≤ hash-max-ziplist-value 时使用 ziplist 编码;否则升级为 hashtable,且不可逆。

Hash 什么时候会用 ziplist 编码?
Redis 的 hash 默认不是一直用哈希表(hashtable)存的。当满足两个条件时,它会退化成更省内存的 ziplist:元素个数 ≤ hash-max-ziplist-entries,且每个字段和值的长度都 ≤ hash-max-ziplist-value。一旦任一条件被突破,整个 hash 就会立即升级为 hashtable —— 这个过程不可逆,且会翻倍占用内存。
常见错误现象:HSET user:1001 name "Alice" age "30" city "Beijing" 看似很小,但如果某次写入一个长字符串(比如 HSET user:1001 bio "...",bio 超过 hash-max-ziplist-value),整个 key 就立刻转成 hashtable,哪怕之前只有 3 个短字段。
- 默认值(Redis 7.0+):
hash-max-ziplist-entries= 512,hash-max-ziplist-value= 64 - 如果你存的是 JSON 片段、base64 图片 ID 或带空格的描述文本,64 字节很容易超
- 升级后无法自动降级,即使你后续删掉长字段,内存也不会回收回 ziplist
怎么查当前 hash 是什么编码?
别猜,直接用 OBJECT ENCODING 命令确认:
redis-cli> HSET myhash a 1 b 2 (integer) 2 redis-cli> OBJECT ENCODING myhash "ziplist" redis-cli> HSET myhash c "x".repeat(65) (integer) 1 redis-cli> OBJECT ENCODING myhash "hashtable"
注意:OBJECT ENCODING 返回 ziplist 才说明还在紧凑模式;返回 hashtable 就已经“失守”了。线上如果发现大量 hash 占用异常高,优先批量检查这个。
- 用
SCAN配合OBJECT ENCODING可以做粗筛,但别在大实例上高频执行 -
redis-cli --scan --pattern "user:*" | xargs -L 1 -I {} redis-cli OBJECT ENCODING {}这类管道容易丢结果,建议用 Lua 脚本原子判断 - 监控层面可采集
INFO memory中的used_memory_dataset_perc,突然下降可能意味着大量 ziplist 升级(因 hashtable 冗余更高)
调参前必须想清楚的三件事
改 hash-max-ziplist-entries 或 hash-max-ziplist-value 不是越大越好,本质是在内存、CPU、延迟之间做权衡。
- 增大
hash-max-ziplist-value到 128 或 256,对存短文本(如邮箱、手机号)友好,但若误存日志片段,单个 field 就可能撑爆 ziplist,触发一次性扩容,卡住主线程 - 减小
hash-max-ziplist-entries(比如设成 64)能更快进入 hashtable,降低单次查找的平均复杂度(O(1) vs O(N)),但内存立刻多出 3–5 倍 - 这些参数是全局的,影响所有
hash,不能按 key 区分。如果你有“用户资料”(小而密)和“用户行为日志”(大而稀)两类 hash,硬靠一套参数必然顾此失彼
真实场景中,更推荐:对明确知道结构的小型 hash(如配置项、状态映射),用 HGETALL + 客户端缓存;对可能变大的,从设计上就避免塞进单个 hash,改用 JSON.SET 或拆成多个 key。
内存溢出真正在哪发生?
很多人以为调低 ziplist 阈值就能防溢出,其实不然。真正的风险点不在 ziplist 本身,而在它升级成 hashtable 后的**内存放大效应**和**碎片累积**。
- 一个 10 字段、每字段平均 20 字节的 hash,在 ziplist 下约占 300 字节;升级后通常要 2KB+(含 dict 结构头、bucket 数组、指针等)
- 频繁 HDEL + HSET 混用,会导致 ziplist 升级后产生大量小内存块,jemalloc 很难合并,
mem_fragmentation_ratio悄悄升到 1.8 以上 - Redis 6+ 的
active-defrag对 hashtable 效果有限,别指望它自动救场
所以最有效的“防止”,其实是控制写入节奏和数据形态:避免在 hash 中存不确定长度的内容,不把 hash 当通用对象容器用。ziplist 参数只是兜底手段,不是安全绳。










