Redis Hash用ziplist编码更省内存因无指针、哈希桶等开销,字段值紧凑存储,实测比hashtable少占40%~70%;但HGET为O(n)顺序查找,字段越多或位置越靠后性能越差,200+字段且频繁查后置字段时易拖垮主线程。

Hash 用 ziplist 编码确实更省空间,但代价是随机访问变慢——这不是配置没调对,而是 Redis 在内存和 CPU 之间做的硬性取舍。
ziplist 为什么比 hashtable 少占内存
ziplist 是一块连续内存,没有指针、没有哈希桶、没有链表节点。每个 field 和 value 紧挨着存,只靠长度前缀和类型标记区分;而 hashtable 要维护数组 + dictEntry 结构 + next 指针 + key/value 双重 SDS 开销。实测一个含 10 个字段的 Hash,ziplist 编码比 hashtable 少占 40%~70% 内存。
关键节省点包括:
- 单个 key 的元数据(如过期时间、引用计数、类型标识)只算 1 份,不是每个 field 单独一份
- 字段名(如
name、status)不重复存储,也不带 JSON 引号/逗号等序列化冗余 - 小整数可被编码为 1 字节长度前缀,字符串短于 64 字节时直接内联,无额外
len/free字段
HGET 性能掉多少?取决于字段位置和数量
ziplist 查找是顺序扫描:从头开始,逐个读 entry 的 prelen 和内容,直到匹配 field。它不支持 O(1) 跳转。
这意味着:
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 查第一个字段(如
name)最快,几乎常数时间 - 查最后一个字段,要遍历全部前面的 entry,耗时与字段总数成正比
- 字段数从 100 增到 500,最差情况 HGET 延迟可能翻 5 倍(实测在 Redis 6.2 中约从 0.02ms → 0.1ms)
- Redis 7.0 起改用
listpack替代 ziplist,缓解了“连锁更新”问题,但查找仍是 O(n)
什么时候 ziplist 反而更费 CPU,甚至拖垮主线程
不是所有“省内存”都值得要。以下场景 ziplist 会成为性能瓶颈:
- Hash 有 200+ 字段,且业务频繁执行
HGET user:123 avatar_url(靠后字段) - 用
HSET在中间插入新字段(如加updated_at),触发整块内存 memcpy 移动后续所有 entry - 字段值长度波动大(如
bio有时 10 字节、有时 200 字节),导致 ziplist 频繁 realloc + copy,产生内部碎片 - 配置
hash-max-ziplist-entries 10000强行维持 ziplist,但实际字段达 8000 —— 此时每次 HGET 平均扫描 4000 个 entry,CPU 使用率飙升
这种“计算密集型 BigKey”,比单纯内存大的 hashtable 更危险:它不占更多 RAM,但会让 Redis 主线程卡住,影响所有请求。
如何判断你的 Hash 还在享受 ziplist 优势
别猜,直接看编码:
- 运行
DEBUG OBJECT myhash,返回中含encoding:ziplist才算生效 - 若返回
encoding:hashtable,说明已触发退化(哪怕你刚删掉 500 个字段,也不会缩回去) - 批量检查可用:
redis-cli --scan --pattern "user:*" | xargs -L1 -I{} redis-cli DEBUG OBJECT {} 2>/dev/null | grep -E "(key|encoding)" -
MEMORY USAGE myhash能看总字节数,但看不出编码方式;INFO memory 的used_memory_dataset_perc偏高,往往暗示大量小对象未被压缩
真正容易被忽略的是:ziplist 的优势只在线性增长阶段成立。一旦字段数或 value 长度越过阈值,切换不可逆,且再无回退机制——设计阶段就要预估字段稳定性,而不是上线后再靠调参硬扛。

















