redis 7.0的listpack能压低mem_fragmentation_ratio,因其entry长度高度集中、无连锁更新、内存布局规整,使jemalloc更精准匹配分配桶,内部碎片减少,实测该比率从1.4–1.7降至1.1–1.3。

为什么Redis 7.0的listpack能压低mem_fragmentation_ratio
因为listpack让jemalloc分配更“守规矩”——entry长度高度集中、无连锁更新、内存布局更规整,jemalloc更容易把空闲块复用回原尺寸桶里,内部碎片自然变少。实测中mem_fragmentation_ratio普遍从1.4–1.7降到1.1–1.3。
listpack怎么避开ziplist的连锁更新坑
ziplist每个entry存的是prevlen(前一个entry长度),插入/扩容时可能触发后续所有entry重写长度字段,导致反复realloc和内存搬移;listpack改存currentlen(自身长度),每次只动当前entry,彻底切断多米诺链。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 典型场景:插入一个300字节字符串到10k个253字节字符串组成的ziplist中间 → 后续所有entry的
prevlen从1字节扩到5字节 → 全量重分配 - 同样操作在listpack里:只 realloc 当前entry所在位置,其他entry完全不动
- 后果差异:ziplist最坏O(N)延迟;listpack稳定O(1)局部操作
哪些配置会让listpack“形同虚设”
listpack不是自动生效的,它依赖参数控制是否启用。一旦配置越界,Redis会直接跳过listpack,降级成quicklist或hashtable,碎片优化就归零了。
-
list-max-ziplist-size设为负数(如-2)→ 强制用quicklist,listpack永不触发 -
hash-max-ziplist-entries或zset-max-ziplist-entries设得太小(如16)→ 数据稍一增长就升级成hashtable,反而增加指针开销和碎片 - 混合使用
LPOP/LPUSH频繁触达listpack边界 → 可能反复扩容/缩容,产生外部碎片 - 验证是否生效:
DEBUG OBJECT key返回encoding: listpack才算真正在用;若显示quicklist或linkedlist,说明已退化
listpack的代价你得心里有数
它不是银弹。省下的内存和躲过的连锁更新,换来了两个明确限制:不支持随机访问加速,也不支持原地变长更新。
- 遍历必须从头开始(没有prevlen,无法反向跳转),
LINDEX key -1这种操作要O(N),而ziplist还能靠zltail快速定位尾部 - 如果某个entry内容变长超出当前分配空间(比如字符串从"123"变成"123456789..."),listpack只能整体realloc + memcpy,不能像ziplist那样尝试局部腾挪
- 所以它最适合静态或追加为主的场景:购物车商品、设备上报状态快照、feed流缓存等;不适合高频中间插入/更新的队列
mem_fragmentation_ratio下降就松口气——得确认DEBUG OBJECT返回的是listpack,再结合业务读写模式看是否真受益。否则可能只是表面好看,背后延迟毛刺照旧。










