Redis 内存淘汰策略:maxmemory、LRU 和 LFU 怎么选
Redis 内存淘汰章节,讲清 maxmemory、NoEviction、VolatileTTL、VolatileLRU、VolatileRandom、VolatileLFU、AllKeysLRU、AllKeysRandom、AllKeysLFU,以及 LRU 和 LFU 的区别。
相关工具
内存淘汰和过期删除不是一件事
上一篇讲过期删除,处理的是 key 到了过期时间以后怎么清理。这一篇讲内存淘汰,处理的是另一个问题:Redis 的内存达到上限以后,新的写入还要不要继续?如果继续,应该删掉哪些 key 来腾空间?
资料 在过期删除后紧接着讲内存淘汰。它先给出入口:Redis 通过参数 `maxmemory <bytes>` 来设定最大运行内存。当系统内存不足时,Redis 会根据配置的淘汰策略删除一些键,以释放内存。
这句话里有两个关键点。第一,内存淘汰由容量压力触发,不是由 key 的过期时间触发。第二,淘汰谁不是 Redis 随便决定的,而是由 `maxmemory-policy` 控制。策略选错,轻则缓存命中率下降,重则写请求直接报错。

Redis 达到 maxmemory 后,会根据 maxmemory-policy 决定拒绝写入,还是从设置了过期时间的 key 或所有 key 中选择候选对象淘汰。
NoEviction:默认是不淘汰
资料 把淘汰策略先分成两类:不进行数据淘汰,以及进行数据淘汰。不淘汰对应 `NoEviction`。当内存不足以执行写操作时,Redis 会返回错误。资料 也说明,这是默认的淘汰策略。
`NoEviction` 的特点很直接:Redis 不替你删数据。内存达到上限后,读请求通常还能继续,写请求会失败。对于把 Redis 当成重要状态存储、不能随便丢 key 的场景,这个策略比较保守。它宁愿让写入失败,也不悄悄删掉某些业务数据。
但如果 Redis 主要是缓存层,`NoEviction` 可能会让系统在高峰期变得不够灵活。内存满了以后,新缓存写不进去,某些依赖写入的功能可能报错。是否使用它,要看 Redis 里存的是“可丢的缓存”,还是“不能随便丢的状态”。
Volatile 系列:只淘汰带过期时间的 key
文中列了几种以 Volatile 开头的策略:`VolatileTTL`、`VolatileLRU`、`VolatileRandom`、`VolatileLFU`。它们有一个共同边界:只对带有过期时间的 key 生效。没有设置过期时间的 key,不会进入这类策略的淘汰候选。
`VolatileTTL` 会优先淘汰更早过期的键值。这个策略适合一种直觉:既然这些 key 本来就快过期了,内存紧张时先删它们,对业务影响可能更小。它关注的是剩余存活时间。
`VolatileLRU`、`VolatileRandom`、`VolatileLFU` 则分别按最近最少使用、随机、最少频繁使用来淘汰,但范围仍然限制在设置了过期时间的 key 里。文中对这几项的描述都强调“只对带有过期时间的键使用对应策略,其他键使用 NoEviction 策略”。这点很容易被忽略。
AllKeys 系列:所有 key 都可能被淘汰
另一个大类是 AllKeys。文中列到 `AllKeysLRU`、`AllKeysRandom`、`AllKeysLFU`。它们和 Volatile 系列最大的区别,是候选范围扩大到了所有 key,不管这个 key 有没有过期时间。
`AllKeysLRU` 根据最近最少使用原则,淘汰最久未使用的键。`AllKeysRandom` 随机选择一个键淘汰。`AllKeysLFU` 根据最少频繁使用原则,淘汰使用次数最少的键。对于纯缓存场景,AllKeys 系列往往更符合预期,因为缓存本来就是为命中率服务的,内存不足时应该从所有缓存对象里挑一部分牺牲。
但 AllKeys 也意味着风险更大。如果 Redis 里混放了缓存和重要状态,AllKeys 可能把你没设置过期时间的关键 key 也删掉。很多线上问题不是淘汰策略本身错,而是 Redis 里数据职责混杂:有些 key 能丢,有些 key 不能丢,却放在同一个淘汰池里。
怎么查看当前淘汰策略
已有内容提到,可以使用 `config get maxmemory-policy` 命令查看当前 Redis 的内存淘汰策略。这个命令很实用,因为排查缓存异常时,第一步不是猜,而是确认实例当前到底用了什么策略。
比如你发现写请求在内存满时直接失败,就要看看是不是 `noeviction`。如果你发现没有设置过期时间的缓存 key 也被删了,就要确认是不是用了 `allkeys-lru` 或 `allkeys-lfu`。如果只希望淘汰带 TTL 的缓存,却配置成了 AllKeys 系列,结果可能就和预期不一样。
配置检查还要和 `maxmemory` 一起看。只看策略,不看内存上限,判断不完整。没有设置合适的 `maxmemory`,或者内存上限贴得太紧,策略再合理也可能频繁触发淘汰,导致命中率抖动。
LRU 关注最近有没有用过
资料 对 LRU 的解释是:LRU,全称 Least Recently Used,假设最近被访问的数据更有可能在未来再次被访问,所以选择最近最少被使用的对象淘汰。它关注的是“最后一次访问时间”。
举个简单例子。A 昨天被访问过 100 次,但今天一直没人用;B 今天刚被访问过 1 次。站在 LRU 的角度,B 比 A 更新鲜,因为 B 最近被访问过。内存紧张时,A 更可能被淘汰。
LRU 适合访问有明显时间局部性的缓存。比如热点文章、商品详情、接口结果,最近被访问的数据短期内继续被访问的概率更高。它不关心历史总次数,只关心最近一次访问离现在有多远。
Redis 的 LRU 是近似算法
理论上的精确 LRU 需要维护全局访问顺序,每次访问都更新位置。对 Redis 这种高性能系统来说,精确维护成本不低。文中说,Redis 实现的是一种近似 LRU 算法,会在对象结构体中添加额外字段,记录数据最后一次访问时间。
当 Redis 进行内存淘汰时,它不是扫描所有 key 找最老的那个,而是使用随机采样。资料写到,Redis 会随机取 5 个值,这个数量可配置,然后淘汰其中最久没有使用的那个。
这就是“近似”的含义。它不保证每次都淘汰全库里绝对最久没访问的 key,但用很小的采样成本,大致逼近 LRU 的效果。对缓存系统来说,这种取舍通常比精确但昂贵的维护更划算。
LFU 关注使用频率
LFU,全称 Least Frequently Used。文中的解释是:LFU 假设使用频率较低的对象未来仍然会较少被访问,所以选择使用频率最低的对象淘汰。它维护的是访问次数或频率。
还是用刚才的例子。A 昨天被访问过 100 次,B 今天刚被访问过 1 次。LRU 可能更偏向保留 B,因为 B 最近用过;LFU 可能更偏向保留 A,因为 A 的访问频率更高。两种算法看待“热点”的方式不同。
LFU 更适合长期热点比较稳定的场景。比如某些常用配置、热门商品、核心页面数据,即使短时间没被访问,也不一定应该被淘汰。它能避免 LRU 把历史高频但刚好最近没访问的数据过早删掉。
LRU 和 LFU 怎么选
资料 对两者区别总结得很简单:LRU 关注最近的访问情况,认为最近被访问的对象更可能在未来再次被访问;LFU 关注使用频率,认为使用频率较低的对象未来仍然较少被访问。
如果你的业务热点变化快,最近访问更能代表下一段时间的访问,比如新闻、活动页、短周期榜单,LRU 通常更自然。如果你的热点比较稳定,访问次数更能反映长期价值,比如基础配置、长期热门商品、常用字典数据,LFU 可能更合适。
当然,策略不是只靠业务感觉决定。最好结合命中率、淘汰数量、内存占用、回源压力一起观察。如果换成 LFU 后命中率上升、数据库压力下降,说明频率模型更贴近业务;如果热点变化快,LFU 留住一堆旧热点,反而可能不如 LRU。
Volatile 和 AllKeys 的选择,比 LRU/LFU 更基础
很多人选策略时只盯着 LRU 和 LFU,但更先要决定的是 Volatile 还是 AllKeys。这个选择决定了淘汰候选范围。Volatile 只从设置了过期时间的 key 里淘汰,AllKeys 会从所有 key 里淘汰。
如果 Redis 里既有可丢缓存,又有不希望被淘汰的业务状态,Volatile 系列更安全一些,前提是你给可淘汰缓存设置了 TTL。没有 TTL 的 key 不会进入候选池。反过来,如果 Redis 实例专门作为缓存层,所有 key 都可以在内存紧张时被淘汰,那么 AllKeys 系列会更直接。
所以选策略时可以按两步走:先问哪些 key 可以被删,决定 Volatile 还是 AllKeys;再问怎么判断谁更该删,决定 TTL、LRU、LFU 或 Random。把这两层分开,配置就不容易乱。
别把淘汰策略当作容量规划
内存淘汰能救急,但不能替代容量规划。如果实例长期处于 maxmemory 附近,Redis 就会频繁淘汰 key。短期看写入还能继续,长期看缓存命中率会下降,数据库回源压力会上升,业务延迟也会变得不稳定。
更好的做法是给 Redis 明确职责:哪些数据必须有 TTL,哪些数据不能放在会淘汰的实例里,哪些大 key 需要拆分,哪些热点需要单独治理。策略只是最后一道门,前面的数据设计和容量评估才决定系统稳不稳。
排查内存问题时,可以先看 `maxmemory`、`maxmemory-policy`、内存使用曲线、淘汰 key 数、命中率和回源量。只改策略不看数据形态,就像只换刹车片不看车速,表面动了,问题可能还在。
面试可以这样回答
如果被问 Redis 内存淘汰策略,可以先说触发条件:Redis 可以通过 `maxmemory` 设置最大运行内存,内存不足时根据 `maxmemory-policy` 决定是否淘汰 key。默认策略是 `NoEviction`,内存不足时写操作返回错误。
然后按候选范围讲策略:Volatile 系列只淘汰设置了过期时间的 key,包括 `VolatileTTL`、`VolatileLRU`、`VolatileRandom`、`VolatileLFU`;AllKeys 系列从所有 key 中淘汰,包括 `AllKeysLRU`、`AllKeysRandom`、`AllKeysLFU`。
最后解释 LRU 和 LFU:LRU 看最后一次访问时间,淘汰最近最少使用的数据;LFU 看访问频率,淘汰使用次数较低的数据。Redis 的 LRU 是近似实现,会随机采样一批 key,例如 5 个,再淘汰样本中最久未访问的那个。
常见问题
Redis 默认内存淘汰策略是什么?
文中提到默认是 NoEviction。内存不足以执行写操作时,Redis 不淘汰数据,而是返回错误。
Volatile 和 AllKeys 有什么区别?
Volatile 系列只从设置了过期时间的 key 中淘汰;AllKeys 系列会从所有 key 中淘汰,不管 key 有没有 TTL。
LRU 和 LFU 最大区别是什么?
LRU 关注最近一次访问时间,认为最近被访问的数据未来更可能再被访问;LFU 关注访问频率,认为访问次数少的数据未来仍然较少被访问。
Redis 的 LRU 是精确 LRU 吗?
不是。文中提到 Redis 使用近似 LRU,会随机采样若干 key,例如 5 个,再淘汰样本中最久没有使用的那个。