You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

配置maxmemory volatile-lru的Redis为何仍触发OOM内存耗尽故障

故障核心原因

你遇到的OOM是Redis过期键回收机制的设计特性,叠加长期运行的碎片累积共同导致的,两个看似矛盾的现象可以对应到具体逻辑:

为什么2天TTL的会话,内存会持续上涨一年才触达阈值

Redis的过期键从来不是到期立刻删除,默认用两种低CPU开销的回收逻辑:

  • 惰性删除:只有当某个键被客户端访问时,才会校验是否过期,过期才执行删除释放内存
  • 定期删除:默认每100ms执行一次,每次随机抽取一批设置了过期时间的键,删除其中已过期的条目,不会全量遍历所有过期键,避免回收操作占用过多CPU影响正常请求
    你存储的是会话数据,必然存在大量「过期后永远不会被再次访问」的冷会话:比如用户单次登录后再也没有上线,对应的会话键到期后,既不会被访问触发惰性删除,又在历次定期删除的随机抽查中成为漏网之鱼,就会作为过期未释放的僵尸键长期残留在内存中。
    从你给出的数据可以直接验证这个判断:重启清空后6天内存仅占781.54M,按平稳负载推算,2天TTL对应的有效会话总内存本来就应该稳定在300M以内,故障前涨到20G,多出来的内存几乎全是一年来慢慢攒下的僵尸键——因为每次定期删除漏回收的键很少,内存上涨速度极慢,花一年时间触达阈值完全符合这个特征。
    另外你给出的键空间统计里,expires=1425758比总键数少了1178个,这部分未设置TTL的键不会被自动回收,但占比极低,不是内存上涨的主因。

为什么配置了volatile-lru,到阈值后不淘汰键反而报OOM

这是对volatile-lru淘汰范围的认知偏差导致的:
volatile-lru的淘汰候选池,只包含设置了过期时间、且当前处于未过期状态的键。那些已经过期但没被回收的僵尸键,根本不会进入淘汰候选列表,不会被LRU逻辑选中释放。
当内存触达maxmemory阈值时,Redis会先尝试跑一轮快速过期回收,再从候选池里选键淘汰。如果此时绝大多数内存被不在淘汰范围内的僵尸键占据,候选池里的有效键总大小远低于需要释放的内存量,Redis就会直接抛出OOM错误,拒绝新的写入请求。
内存碎片是加重故障的次要因素:你这种持续写入、删除小体积会话键的场景,jemalloc分配器的内存碎片率会长期缓慢抬升,如果mem_fragmentation_ratio长期高于1.5,实际占用的物理内存会比Redis统计的used_memory高20%以上,会更早触达内存阈值,但不是触发OOM的核心原因。

修复与优化方案
  • 紧急恢复不需要全量flush:下次再遇到同类问题,用SCAN命令遍历全量键,遍历过程中触碰到过期键就会触发惰性删除,绝大多数被僵尸键占用的内存会立刻释放;如果碎片率过高,执行MEMORY PURGE即可让jemalloc回收空闲的碎片页。
  • 长期配置调整:
    • 把淘汰策略从volatile-lru改成allkeys-lru:内存到阈值时会从所有键里选最久没访问的条目淘汰,那些永远没人访问的僵尸会话会第一批被清理,完全适配会话存储的场景。
    • 调高定期删除执行频率:把hz参数从默认的10调整到20~30,增加每次定期删除抽查的键数量,大幅降低僵尸键漏回收的概率,注意不要把该值调到50以上,避免占用过多CPU影响请求延迟。
    • 开启自动碎片整理:Redis 4.0及以上版本配置activedefrag yes,会在碎片率超过阈值时自动在后台整理内存碎片,避免碎片率长期无限制累积。
    • 你当前配置的save ""禁用持久化是符合会话场景需求的,该配置和本次故障没有关联。

内容的提问来源于stack exchange,提问作者Lisandro

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 14:57:32