Redis 内存碎片化治理:used_memory 低却 OOM?内存碎片整理 + 对象池优化
朋友公司的 Redis 集群监控上
used_memory才 4GB,但used_memory_rss已经到了 8GB——是 used 的两倍。运维加了内存,过两个月又一样。最离谱的是有一次used_memory只有 2GB,Redis 却报了 OOM。排查发现内存碎片率 3.5,8GB 的物理内存被碎片占了一半多。
这就是 Redis 的内存碎片问题。used_memory 是你存进去的有用数据,used_memory_rss 是 Redis 实际向操作系统申请的内存。两者之间的差,就是碎片。
今天聊聊怎么用 Redis 自带的内存碎片整理和对象复用,把碎片率降到 1.1 以下。
碎片怎么来的
Redis 的内存分配是 jemalloc 管理的。当你反复写入和删除不同大小的 Key,就会出现这样的场景:
内存布局(简化):
|AAAA| 空闲 |BB| 空闲 |CCCC| 空闲 |DD| 空闲 |...
删掉的 Key 留下的空隙不够大,放不下新的 Key。新 Key 只能往后申请新内存。结果就是 used 没涨多少,rss 一直涨。
碎片率计算公式:
mem_fragmentation_ratio = used_memory_rss / used_memory
大于 1.5 就该关注了,大于 2 必须处理。
方案一:Redis 内存碎片主动整理
Redis 4.0 以后有了 activedefrag,自动在后台整理碎片:
# redis.conf
activedefrag yes
active-defrag-ignore-bytes 100mb # 碎片超过 100MB 才启动
active-defrag-threshold-lower 10 # 碎片率低于 10% 不整理
active-defrag-threshold-upper 100 # 碎片率超过 100% 积极整理
整理的效果是:把分散的空隙合并成连续的可用空间。不需要停服,后台线程慢慢挪数据。
但要注意,碎片整理本身消耗 CPU。高峰期整理会影响性能。active-defrag-cycle-min 和 active-defrag-cycle-max 控制整理的激进程度,保守一点设 5 和 25 就可以了。
手动触发整理的方案:
# 看碎片率
redis-cli INFO memory | grep mem_fragmentation_ratio
# 手动整理
redis-cli MEMORY PURGE
方案二:应用层对象复用
另一个产生碎片的来源是频繁创建和销毁小对象。比如每次接口调用都 new 一个 200 字节的临时对象,每个对象对应 Redis 里一条数据。高并发下,这种小对象频繁分配释放,jemalloc 的内存池里全是小碎片。
从应用层减少碎片的方法:对象池复用。
不是把 Redis 的数据放对象池,而是把你 Java 应用里跟 Redis 交互的临时对象(序列化缓冲、中间变量)复用起来:
// ❌ 每次调用 new 一个
byte[] buf = new byte[4096];
redis.get(key, buf);
// ✅ 对象池复用
byte[] buf = bufferPool.borrow();
try {
redis.get(key, buf);
} finally {
bufferPool.return(buf);
}
效果不是直接的——碎片产生在 jemalloc 侧,对象池在你应用的 JVM 侧。但减少应用层的内存分配抖动,会间接减少 Redis 侧的碎片——因为你的应用每次发过来的数据大小更稳定,jemalloc 的内存分配模式更可预测。
生产建议
碎片治理是日常运维,不是一次性的。
监控:三个指标挂 Prometheus —— mem_fragmentation_ratio(碎片率)、used_memory_rss(实际内存)、used_memory(逻辑内存)。碎片率持续超 1.5 告警。
自动整理:Redis 4.0+ 开 activedefrag yes,设合理的阈值。CPU 紧张就降低 active-defrag-cycle-max。
应用层配合:别频繁创建大小不同的临时对象。序列化缓冲区用对象池复用,让每次写入的数据大小模式更稳定。
如果碎片率已经到了 2 以上,最快的办法是重启 Redis。重启后 jemalloc 的内存池重新初始化,碎片归零。但重启期间服务不可用,这是一个 trade-off。
总结
Redis OOM 不一定是真内存不够,很可能是碎片吃了你的物理内存。
mem_fragmentation_ratio 大于 2 就要行动:先开 activedefrag 自动整理,CPU 不够就降低整理的激进程度。碎片率实在太高了就计划重启,同时应用层用对象池让写入模式更稳定。
有用的话转给还在给 Redis 加内存的运维。
标题:Redis 内存碎片化治理:used_memory 低却 OOM?内存碎片整理 + 对象池优化
作者:jiangyi
地址:http://www.jiangyi.space/articles/2026/06/15/1781421937494.html
公众号:服务端技术精选
评论