P51 Redis 明明配置了 LRU 为啥还会 OOM
面试题:Redis 配了 maxmemory + LRU 淘汰策略,为什么还会报 OOM?
1. 前提:LRU 淘汰只在达到 maxmemory 后才触发
ini
maxmemory 512mb
maxmemory-policy allkeys-lru只有 Redis 已用内存达到 maxmemory 时才会触发淘汰。如果:
- 还没到 maxmemory,写不进去是因为单次写入太大;
- 或者淘汰策略配置不对,就会 OOM。
2. 常见原因
① maxmemory-policy 设置成了 noeviction(默认)
noeviction 表示内存满了不淘汰任何 key,新写入直接报错(OOM 错误)。这是最常见的原因——配了 maxmemory 却没配淘汰策略。
② 大 key / 大对象瞬时写入
一次写入一个超大对象(如几十 MB 的 value),当前剩余内存不够,淘汰来不及腾出空间就报错。
③ 内存碎片 / 复制积压缓冲区等"隐藏"内存
- 客户端输出缓冲区、复制积压缓冲区(repl-backlog)、AOF 重写缓冲都不受 maxmemory 精确限制;
- 内存碎片(
used_memory_rss远大于used_memory)会让实际占用超限。
④ 淘汰是"近似 LRU",不是精确 LRU
Redis 的 LRU 是采样近似实现(默认 samples=5),采样池里挑最久未用的淘汰,可能误淘汰热数据或没淘汰到该淘汰的,但一般不会直接导致 OOM。
⑤ 多个实例/子进程内存
- AOF/RDB 持久化子进程(fork)会占用额外内存(Copy-on-Write);
- 集群多实例部署在同一台机器,共享物理内存,单个实例没到 maxmemory 但机器内存已满。
3. 排查思路
bash
INFO memory
# 看 used_memory / used_memory_rss / maxmemory / 碎片率bash
CONFIG GET maxmemory-policy # 确认不是 noeviction- 检查有没有大 key(
redis-cli --bigkeys); - 检查持久化配置和子进程内存;
- 看碎片率,必要时
MEMORY PURGE或重启。
4. 解决
- 配置合理的淘汰策略:
allkeys-lru(全部 key)/volatile-lru(只淘汰带 TTL 的); - 拆分/控制大 key;
- 调大 maxmemory 或加实例;
- 开启
maxmemory-policy的同时给 key 设 TTL; - 监控 used_memory_rss 和碎片率。
一句话总结
Redis OOM 最常见是 maxmemory-policy 默认 noeviction(满了不淘汰、新写入报错);其次是瞬时大 key、内存碎片、fork 子进程和复制缓冲等超限内存;先用 INFO memory + CONFIG GET maxmemory-policy 排查,再配好淘汰策略、控大 key、留内存余量。