redis list大数据量加剧内存碎片,因quicklist中ziplist频繁分配释放、分裂合并,导致jemalloc产生内外碎片;调优需控制单节点大小(list-max-ziplist-size)、分片、改用set/zset,或低峰期memory purge。

为什么List大数据量会加剧内存碎片
Redis的List在元素较多时,默认使用quicklist(由多个ziplist节点组成的双向链表),每个ziplist是连续内存块。但频繁LPUSH/RPOP或LTRIM操作会导致大量小ziplist节点被分配、释放、分裂、合并,尤其当元素大小不一(比如混存10字节和2KB的字符串)时,jemalloc按固定档位(如32/64/128字节)分配空间,极易产生内部碎片;同时节点释放后位置零散,形成外部碎片。
典型现象:INFO memory中mem_fragmentation_ratio持续高于1.7,而used_memory远低于maxmemory,写入新数据却开始触发OOM command not allowed when used memory > 'maxmemory'错误。
用list-max-ziplist-size和list-max-ziplist-entries压低碎片生成
这两个配置控制ziplist何时升级为quicklist节点,本质是限制单个连续内存块的尺寸和数量——越小越容易被复用,也越难产生大块外部碎片。
-
list-max-ziplist-size -2:表示每个ziplist最多容纳8KB(-2=8KB),比默认值(-2)更激进;若业务元素普遍-1(4KB)进一步收紧 -
list-max-ziplist-entries 512:限制单个ziplist最多存512个元素;避免一个ziplist过大导致删除中间元素后留下无法复用的“长条形”空洞 - 必须同步调整
hash-max-ziplist-entries和hash-max-ziplist-value,否则Hash类型也会因类似机制拖累整体碎片率
修改后需重启或CONFIG REWRITE,并观察INFO stats中instantaneous_ops_per_sec是否明显下降——说明ziplist压缩有效减少了内存分配次数。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
避免直接用List存超大集合,改用分片+SET或Sorted Set
当单个List元素数稳定超过10万,或平均元素大小>100KB,quicklist已不是最优结构。此时无论怎么调参,频繁的内存重分配都会推高mem_fragmentation_ratio。
- 拆分为多个小
List:如mylist:001、mylist:002…,用哈希标签保证同属一个slot,再由客户端做路由和合并 - 改用
SET+SPOP/SRANDMEMBER:如果语义允许无序,SET底层用intset(小整数)或hashtable,内存布局更紧凑,且hashtable扩容是成倍增长,碎片更可控 - 改用
ZSET+ score做逻辑顺序:避免LINDEX随机访问引发的quicklist遍历开销,同时ZSET的skiplist节点内存分配更稳定
注意:LRANGE biglist 0 -1这类全量读取在百万级List上会阻塞主线程,本身就会放大碎片影响——分片后LRANGE只作用于子集,风险收敛。
启用activedefrag但别依赖它“救火”
activedefrag yes开启后,Redis会在空闲CPU周期主动合并小碎片,但它不能修复已发生的严重外部碎片,更无法回收被jemalloc长期持有的物理页。它的作用是“减缓恶化”,不是“逆转现状”。
- 必须配对设置:
active-defrag-ignore-bytes 100mb(碎片总量超100MB才启动)、active-defrag-threshold-lower 10(内存使用率>10%才工作) - 不要设
active-defrag-cycle-min过高(如>75),否则会抢占正常请求的CPU,反而降低吞吐 - 生产环境建议配合
MEMORY PURGE定期执行(Redis 6.2+),但该命令会短暂阻塞,务必选在低峰期
最易被忽略的一点:activedefrag对quicklist节点内部的ziplist无效——它只整理jemalloc层面的页级碎片,而List的碎片主要来自ziplist分裂。所以参数调优永远比事后整理更关键。










