很多团队设置了 maxmemory-policy,就以为 Redis 会像一个精确缓存那样自动保留最有价值的数据。这个理解有风险:Redis 的 LRU/LFU 都是近似算法。

当 used_memory 超过 maxmemory,Redis 会根据策略选择候选 key。候选不是全量排序,而是采样;策略也要区分 allkeys、volatile 和 noeviction。

先把机制边界说清楚

这一篇讨论内存达到上限后的淘汰决策,不讨论过期删除。过期是 key 的生命周期,淘汰是内存压力下的牺牲选择。

整体路径

maxmemory 触发后的淘汰决策

上面这张图先把主线铺开:策略决定候选池,采样决定被淘汰对象。要判断淘汰会不会生效,得同时确认 maxmemory 是否开启、策略选的是什么、以及写入压力是否真的触发了淘汰检查。

底层机制

  • allkeys-lru / allkeys-lfu / allkeys-random:从所有 key 中按策略淘汰,适合纯缓存实例。
  • volatile-lru / volatile-lfu / volatile-random / volatile-ttl:只淘汰设置了 TTL 的 key,适合缓存和非缓存混用时的有限保护。
  • noeviction:不淘汰,内存不足时让写命令报错(读命令仍可继续)。这是 maxmemory-policy默认值
  • 注意一个反直觉点:maxmemory = 0 表示不限制内存、不触发淘汰,这是 maxmemory 的默认值。所以「设了淘汰策略但 Redis 还是不淘汰」最常见的原因就是 maxmemory 没设或设成 0。
  • LRU 和 LFU 都通过采样近似实现(采样数由 maxmemory-samples 控制,默认 5),不维护全局精确链表——每次访问移动节点的成本会破坏单线程性能,采样 5 个已足够接近真实 LRU,调大可逼近但代价上升。LFU 用 24 位时钟和对数计数器(lfu-log-factorlfu-decay-time)跟踪访问频率。
  • 淘汰只在写命令路径上检查(freeMemoryIfNeeded),纯读命令不会触发淘汰。

「Redis 内存满了却没淘汰」排查清单:① maxmemory 是不是 0 或没设;② 策略是 noeviction;③ 是不是只有读请求没写请求;④ volatile-* 但候选 key 都没设 TTL 导致无 key 可淘汰,退化成报错;⑤ used_memory 与实际不符(lazyfree 延迟、对象共享)。淘汰策略能帮你降级,但不能替你做数据分级。

取舍与边界

淘汰策略不是兜底神器。把缓存数据和准持久数据混在同一个实例里,再指望策略自动理解业务优先级,迟早会翻车。

典型问题:用机制化例子排查

  • 纯缓存实例优先 allkeys-lfuallkeys-lru,业务状态数据尽量独立实例。
  • 使用 volatile-* 时确保应被淘汰的 key 都设置了 TTL。
  • 监控 evicted_keys、hit rate 和内存曲线,淘汰增加说明容量或热点模型已变化。
  • 不要用淘汰策略掩盖大 key、热 key 和缓存雪崩问题。

收束:一句判断

淘汰策略能帮你降级,但不能替你做数据分级。


关于十三Tech

我是十三,All in AI Agent 方向的架构师,专注 AI 工程实践。

我相信 AI 是程序员的最佳搭档,也希望帮助每一位开发者更好地驾驭 AI。

如果你想继续跟完这套「图解 Redis」,欢迎关注公众号 「十三Tech」。后续会继续按数据结构、底层机制、持久化、高可用和实战排查这条线更新。

十三Tech公众号二维码