oom错误不等于物理内存耗尽,而是redis因淘汰策略未生效拒绝写入;需通过info memory检查used_memory、evicted_keys等指标,并结合notify-keyspace-events监听或审计ttl设置定位问题。

看到 OOM command not allowed when used memory > 'maxmemory' 就认定是内存真满了?
不是。这个错误只说明 Redis 拒绝了写入,但 used_memory 和 maxmemory 的差值可能仍有几十 MB,甚至上百 MB。真正卡住的往往是淘汰策略没生效,而不是物理内存耗尽。
先跑这行命令确认真实水位:
redis-cli -p 6379 INFO memory | grep -E "(used_memory|maxmemory|mem_allocator|evicted_keys)"
-
used_memory接近maxmemory(比如差值 evicted_keys 为 0 → 很可能是策略配错或 key 全无 TTL -
mem_allocator是jemalloc,但mem_fragmentation_ratio> 1.5 → 内存碎片高,used_memory虚高,实际可用内存远低于显示值 -
evicted_keys持续上涨,但业务侧仍报“数据不见了” → 说明淘汰确实发生了,得查具体哪些 key 被干掉了
CONFIG GET maxmemory-policy 返回 noeviction 却还在丢数据?
不可能。如果策略真是 noeviction,Redis 绝对不会主动删任何 key,只会报 OOM 错误、拒绝写入。你看到的“丢数据”,大概率是以下三种情况之一:
Redis 8.2.3 是一款安全优先的高性能键值存储系统。该版本紧急修复了可能引发远程代码执行(RCE)的高危漏洞(CVE-2025-62507),并解决了 HyperLogLog 及 Cuckoo Filter 等数据结构在特定场景下的崩溃问题。建议所有用户立即升级,以保障生产环境的系统稳定与数据安全。
- 业务自己调用了
DEL、FLUSHDB或过期 key 被惰性/定期删除机制清理了(和淘汰策略无关) - 配置被动态改过:
CONFIG SET maxmemory-policy allkeys-lru执行后没同步到 redis.conf,重启就回退,但运行时已在淘汰 - 集群模式下只查了某个节点,其他节点策略不同,而客户端路由到了淘汰策略宽松的节点,导致数据不一致
怎么定位到底哪个 key 被淘汰了?
Redis 日志默认不记录具体淘汰了谁,evicted_keys 只是个计数器。想追查,得靠组合手段:
- 启用慢日志 + 内存事件通知:
CONFIG SET notify-keyspace-events "Ev",再用redis-cli --csv psubscribe '__keyevent@*__:evicted'实时监听(注意:仅限单机,集群需逐节点开) - 用
redis-cli --bigkeys扫描大 key,尤其关注那些没设 TTL 的 hash/set —— 如果策略是volatile-*类型,它们根本不会进淘汰候选池 - 在怀疑时间段内导出
INFO keyspace对比 key 数量变化,再结合SCAN分批查 TTL:SCAN 0 MATCH * COUNT 1000→ 对每个 key 跑TTL,看是否批量返回-1(无 TTL)或-2(已删)
allkeys-lru 下冷数据还在,热数据反而丢了?
LRU 在 Redis 里是“近似”实现,不是精确链表维护。它从候选池随机采样 N 个 key(默认 5 个),再从中挑 LRU 最久的那个淘汰。这意味着:
- 如果热 key 访问模式是“短时高频爆发”,但之后几小时没再碰,它可能在某次采样中被当成冷 key 清掉
- 大量小 key 挤占了采样池,导致真正的大 key 没被抽中,长期滞留,间接推高内存压力
-
allkeys-lfu更适合稳态热点场景,但 LFU 计数器有衰减机制(默认每分钟右移一次),突发流量后的热度会快速回落
真正难排查的,往往不是策略选错,而是 TTL 设置和 key 命名习惯埋的雷:比如所有缓存都用 cache:user:123 这种格式,但部分业务漏写了 EX,导致它们永远进不了 volatile-* 策略的视线——这种问题,光看日志和指标根本发现不了,必须代码层审计。










