redis 7.0+ 用 listpack 替代 ziplist,解决级联更新问题;zset 仍沿用 skiplist + dict 双结构,配置项名称与默认值(128/64)不变,仅底层实现切换。

Redis 7.0+ 用 listpack 替代 ziplist
Redis 7.0 是分水岭版本,ziplist 被正式废弃,ZSet 的紧凑存储改用 listpack。两者都是连续内存块,但 listpack 解决了 ziplist 臭名昭著的“级联更新”问题——即插入/删除中间元素时,后续所有节点的长度编码可能连锁重写,引发大量内存拷贝。listpack 采用固定头 + 变长内容设计,单个节点修改不会波及其他节点。
配置项名称没变:zset-max-ziplist-entries 和 zset-max-ziplist-value 依然生效,只是底层实现已切换。你查 CONFIG GET zset-max-ziplist-entries 看到的还是这个 key,但 Redis 内部用的是 listpack。
skiplist + dict 组合结构在各版本中保持稳定
无论 Redis 6.x 还是 7.x,当 ZSet 触发“大结构”条件(元素数 > zset-max-ziplist-entries 或任一 member 长度 > zset-max-ziplist-value)时,底层都会升为 skiplist + dict 的双结构:
-
skiplist负责按score排序、范围查询(如ZRANGE、ZRANK),时间复杂度 O(log N) -
dict负责按member查score(如ZSCORE),时间复杂度 O(1),且避免数据重复存储
这个组合逻辑从 Redis 2.8 就确立下来,至今未变。版本升级不会影响它的行为,只可能优化跳表节点内存布局或字典哈希算法细节。
配置项默认值未随版本强制变更,但需注意兼容性
zset-max-ziplist-entries 默认仍是 128,zset-max-ziplist-value 默认仍是 64 字节。但要注意:
- Redis 7.0+ 的
listpack对超长字符串更敏感:即使总元素数 skiplist +dict - 旧版客户端或监控工具若硬编码依赖
ziplist的内存布局(比如解析 RDB 文件),升级到 7.0 后会失败——因为listpack的二进制格式完全不同 -
INFO memory中的mem_usage字段在 7.0+ 更准确反映listpack实际开销,而旧版对ziplist有轻微高估
如何确认当前 ZSet 实际使用的编码?
别猜,用命令实测:
执行 OBJECT ENCODING your_zset_key,返回值只有两种可能:
-
listpack:说明当前满足小数据条件,且运行在 Redis 7.0+ -
skiplist:说明已升为跳表结构(注意:这里返回skiplist,不是ziplist,哪怕你用的是 6.x 版本)
如果返回 ziplist,那一定是 Redis 6.x 或更早版本。Redis 7.0 不会返回这个值,哪怕配置项名字还叫 zset-max-ziplist-*。
真正容易被忽略的是:listpack 虽然更安全,但它不支持原地扩容。一旦某个 member 插入后导致整体 size 超出预分配空间,整个结构会重建——这在高频写入长字符串 member 的场景下,可能比旧版 ziplist 的级联更新更伤性能。











