Redis 的内存淘汰策略有哪些?LRU 和 LFU 有什么区别?
简化版
Redis 内存达到 maxmemory 后,会按配置的淘汰策略删除部分 key。常见策略有 noeviction、volatile-lru、allkeys-lru、volatile-ttl、volatile-random、allkeys-random、volatile-lfu、allkeys-lfu。LRU 淘汰最近最少使用的 key,看“最近有没有访问”;LFU 淘汰访问频率低的 key,看“访问次数多不多”。
详细版
Redis 淘汰策略分两类:只淘汰设置了过期时间的 key(volatile),或在所有 key 中淘汰(allkeys)。
| 策略 | 含义 | 适合场景 |
|---|---|---|
| noeviction | 不主动淘汰,需要增内存的命令可能报错 | 宁可拒绝写也不自动驱逐 |
| allkeys-lru | 所有 key 中淘汰近似 LRU | 通用缓存 |
| volatile-lru | 只在设置 TTL 的 key 中淘汰 LRU | 混合缓存与重要数据 |
| allkeys-lfu | 所有 key 中淘汰低频 key | 热点稳定场景 |
| volatile-lfu | TTL key 中淘汰低频 key | 有过期时间的热点缓存 |
| volatile-ttl | 在 TTL Key 中优先淘汰最早过期者 | 希望优先清短 TTL |
| allkeys-random | 在所有 Key 中随机淘汰 | 访问价值接近时 |
| volatile-random | 在 TTL Key 中随机淘汰 | 仅允许驱逐 TTL Key |
如果 Redis 只是缓存,常用 allkeys-lru 或 allkeys-lfu。如果 Redis 里混有不能随便淘汰的数据,要谨慎使用 allkeys 类策略,最好从架构上隔离不同用途。
完整版教学
一、为什么需要淘汰策略
Redis 是内存数据库,内存有限。设置 maxmemory 后,当写入新数据导致内存超限,Redis 必须决定是拒绝写入,还是删掉一些旧 key 给新 key 腾空间。这个决策就是淘汰策略。
缓存系统通常允许丢弃部分 key,因为丢了还能从数据库重建;但如果 Redis 存的是会话、队列、锁、计数等关键数据,随便淘汰就可能造成业务错误。所以策略选择首先要看 Redis 在系统里的角色。
二、volatile 和 allkeys 的区别
volatile 类策略只从设置了过期时间的 key 中选择淘汰对象。没有 TTL 的 key 不会被淘汰。allkeys 类策略会从所有 key 中选择,无论有没有 TTL。
如果 Redis 专门做缓存,所有数据都可重建,allkeys 更直接。如果 Redis 里混放了缓存和不能丢的数据,volatile 看起来更安全,但更好的做法是拆实例或拆库,避免不同可靠性要求的数据混在一起。
三、LRU 和 LFU 怎么选
LRU 关注最近访问时间。一个 key 最近没被访问,就容易被淘汰。它适合访问热点变化较快的场景,比如新闻、活动页、搜索结果。
LFU 关注访问频率。一个 key 长期被访问很多次,即使短时间没访问,也不容易被淘汰。它适合热点比较稳定的场景,比如基础配置、热门商品、长期热门内容。
Redis 的 LRU/LFU 都是近似实现,不会维护全局精确排序。LRU 根据采样 Key 的空闲时间选择候选;LFU 也会采样,但 Redis 对象元数据中的频率计数不是简单累计次数,而是概率增长并随时间衰减,相关行为受 lfu-log-factor 和 lfu-decay-time 影响。衰减让曾经的热点最终有机会被淘汰。
maxmemory 64gb
maxmemory-policy allkeys-lfu
lfu-log-factor 10
lfu-decay-time 1
这些只是配置示例,不是通用推荐值;maxmemory 要根据宿主机余量确定,LFU 参数要用真实访问分布压测。
四、noeviction 的含义
noeviction 不会为新写入主动淘汰 Key;达到 maxmemory 后,需要额外分配内存的命令通常会返回错误,纯读以及部分不增加内存的操作仍可能执行。这适合宁可拒绝写也不允许自动驱逐的场景,但它不是持久性或不丢数据保证,业务还必须正确处理写失败。
作为纯缓存时,noeviction 往往不是好选择,因为内存满后新热点写不进去,缓存命中率会恶化。纯缓存更常用 allkeys-lru 或 allkeys-lfu。
五、淘汰策略之外还要做什么
淘汰策略只是最后防线。更重要的是容量规划、key 大小控制、TTL 设计、热点监控。大 key 会让内存快速膨胀,过长 TTL 会让冷数据长期占内存,缺少监控会让你直到写入报错才发现内存满了。
生产上要监控 used_memory、maxmemory、evicted_keys、命中率、Big Key、延迟和碎片率。如果 evicted_keys 持续增长且命中率下降,说明内存不足或缓存设计有问题;若淘汰时延迟升高,还要检查淘汰采样成本、大对象释放和写入突发。
六、面试追问与工程边界
面试官常会追问“内存满了只调淘汰策略够不够”。不够。淘汰策略只能在内存不足时被动删 key,真正的问题可能是 key 设计不合理、TTL 太长、大 key 太多或缓存了不该缓存的数据。要结合容量评估和数据治理一起做。
还要注意业务语义。被淘汰的 Key 应该是可重建的缓存数据。如果 Redis 里放了分布式锁、消息状态、会话等关键数据,被 allkeys 策略淘汰可能造成严重业务问题。不同可靠性的数据最好拆实例,不要指望一个淘汰策略兼顾所有场景。
七、候选集为空与容量水位
volatile 系列只从带 TTL 的 Key 中选择。如果实例已经达到 maxmemory,但没有可淘汰的带 TTL Key,新增内存的命令会像 noeviction 一样失败;它不会转而删除无 TTL Key。因此“重要数据不设 TTL、普通缓存设 TTL”只有在普通缓存始终留有足够候选和空间时才成立,混部仍可能互相挤压。
容量不能贴着 maxmemory 运行。假设实例上限 64GiB,业务数据 52GiB,看似还有 12GiB,但还需考虑复制/AOF 缓冲、客户端输出缓冲、内存碎片和 fork 写时复制。应根据压测设运行水位,并在淘汰持续发生前扩容或降低数据集;maxmemory 限制的口径与操作系统 RSS 也不是同一个数。
策略验证要用真实访问分布压测。例如突发热点适合 LRU,长期稳定热点可能由 LFU 保留得更好;若 LFU 衰减过慢,历史热点会占住内存,过快又近似短期 LRU。选择策略后观察命中率、淘汰速率和业务回源成本,而不是只看策略名称。
淘汰策略决定“压力到来时牺牲谁”,容量规划决定“是否长期处在必须牺牲数据的状态”。
八、过期删除与内存淘汰不是一回事
TTL 到期是业务生命周期结束,Redis 通过访问时的惰性删除和后台主动过期回收这些 Key;淘汰则是在 maxmemory 压力下,即使 Key 尚未到期也按策略驱逐。两者触发条件不同,监控时也要区分 expired_keys 与 evicted_keys。
| 现象 | 更可能的原因 | 排查重点 |
|---|---|---|
expired_keys 上升 | 大批 TTL 到期 | TTL 分布、是否缺少抖动 |
evicted_keys 上升 | 内存水位触发淘汰 | 容量、数据集、策略 |
| 写命令 OOM、淘汰为 0 | noeviction 或无候选 | 当前策略、TTL 候选集 |
| RSS 远高于 used_memory | 碎片或进程额外内存 | allocator、fork、缓冲区 |
Key 被淘汰后,下次访问会 miss 并回源。若淘汰集中在热点 Key,数据库 QPS 与延迟会同步上升;因此评估策略不能只看释放了多少内存,还要看被驱逐数据的重建成本。一个 1KB 但每秒万次访问的 Key,业务价值可能高于一个 100KB 冷 Key。
九、怎样安全切换策略
从 LRU 切到 LFU 后,频率信息需要通过真实访问逐步形成,不能假设切换瞬间就得到稳定的热点排序。应先在相似流量实例灰度,保持相同数据集和水位,对比命中率、回源 QPS、P99 和淘汰速率,再决定扩大范围。
切换策略不会解决已经超出容量的数据集,也不会拆分 Big Key。若持续淘汰是因为版本化 Key 未清理或某个集合异常增长,应先修复 Key 生命周期;把策略来回切换只会改变被牺牲的对象。
十、常见误区与追问
- 误区:LRU 总能淘汰最不重要数据。 Redis LRU 是近似采样,且最近使用不等于业务价值最低。
- 误区:noeviction 最安全。 内存满后写请求会失败,业务要能处理错误。
- 误区:volatile 策略总能腾出空间。 没有带 TTL 的候选 Key 时,需要增加内存的命令仍会失败。
- 误区:淘汰策略可以替代 TTL 设计。 TTL 体现业务生命周期,淘汰只是在内存压力下兜底。
- 追问:LRU 和 LFU 区别是什么? LRU 看最近访问时间,LFU 看访问频率。
- 追问:volatile 和 allkeys 区别是什么? volatile 只淘汰设置过期时间的 key,allkeys 面向所有 key。
- 追问:如何选择策略? 缓存型 Redis 常用 allkeys-lru/lfu,混合存储要谨慎隔离。
- 追问:LFU 计数会不会永远增长? 不会,它采用概率计数并按配置随时间衰减,不是原始访问次数。
十一、加强记忆
Redis 淘汰策略解决内存满了删谁的问题。volatile 只淘汰有 TTL 的 key,allkeys 淘汰所有 key;LRU 看最近访问,LFU 看访问频率;noeviction 不删但写入报错。纯缓存常用 allkeys-lru/allkeys-lfu,混放重要数据要谨慎,最好隔离实例。策略之外还要控大 key、设合理 TTL、监控淘汰和命中率。