Redis 内存淘汰策略实战:什么时候淘汰、怎么选、怎么防缓存击穿

大家好,我是晚安code。

Redis 报错:OOM command not allowed when used memory > 'maxmemory',业务写不进去,人一脸懵。这种错十有八九是 Redis 内存淘汰策略默认的 noeviction 在作妖------内存满了,一个 key 都不删,写命令直接拒收。先声明,我下面讲的线上场景都是示例场景,但排障流程都是真话。

这篇把 Redis 内存淘汰策略一次讲透:什么时候触发、8 种策略怎么选、和缓存击穿什么关系、底层到底怎么删。点个收藏,别等 OOM 了再回来翻。

一、什么时候触发内存淘汰

Redis 的内存淘汰不是后台定时清理,而是被写命令「逼」出来的------内存一旦越过 maxmemory 红线,下一个写操作得先腾地方再放行。(2026 年 8 月整理,基于 Redis 4.0+,6.x/7.x 都适用)

内存淘汰(Eviction):Redis 内存达到 maxmemory 上限后,主动删除一部分 key 来释放空间的机制。你可以把它理解成「缓存满了就得扔点东西,扔谁由策略说了算」。

触发时机很关键:只有写命令会触发。每次执行 SET、LPUSH、INCR 这类会涨内存的命令时,Redis 检查 used_memory 是否超过 maxmemory,超了就调用淘汰逻辑,循环删 key 直到降到线以下,才放行这个写命令。GET 这类读命令不会触发。maxmemory 默认是 0(64 位下不限制),你配了上限,淘汰策略才开始上班。

整个触发链路长这样(图 1):写命令 → 检查内存 → 超限按策略淘汰 → 还没降下来就继续淘汰。

配了 noeviction 却不知道它存在的同学,最容易在 redis-cli 里撞见这句话:

bash 复制代码
(error) OOM command not allowed when used memory > 'maxmemory'

这时候 DEL 还能删(删了才腾得出空间),但任何会涨内存的写命令全被拒。业务表现就是:缓存写不进去,新热点数据全部 miss。

我见过有人在生产环境 debug 了一个下午,最后才发现是默认的 noeviction 策略在「装死」------内存满了宁可拒绝写入也不删数据。

二、8 种策略:先看两个维度

选淘汰策略,永远先问自己两个问题:Redis 里存的东西能丢吗?能丢的话,按什么规律丢?

maxmemory-policy:redis.conf 里决定超限时删哪个 key 的策略参数,默认值是 noeviction------一个都不删,写命令直接报错。

表面看有 8 种策略,其实是从两个维度组合出来的:

  • 淘汰范围volatile-* 只从「设了过期时间(TTL)」的 key 里挑;allkeys-* 从所有 key 里挑。
  • 淘汰算法:LRU 看最近多久没被访问,LFU 看被访问的频率,Random 纯随机,TTL 看谁剩的命最短。

范围 × 算法一组合,8 种策略全在这张表里了:

策略 淘汰范围 按什么淘汰 一句话适用
noeviction 不淘汰 --- 数据不能丢,宁可报错也不删
allkeys-lru 所有 key 最近最少用 通用缓存,保住热点(最常用)
allkeys-lfu 所有 key 最不常用 访问冷热分明,怕偶发访问骗过 LRU
allkeys-random 所有 key 随机 访问完全无规律,几乎不用
volatile-lru 有 TTL 的 key 最近最少用 缓存+存储混合,业务数据不设 TTL
volatile-lfu 有 TTL 的 key 最不常用 同上,但更看重真实热度
volatile-random 有 TTL 的 key 随机 几乎不用
volatile-ttl 有 TTL 的 key 最快过期 优先清掉马上要过期的临时数据

绝大多数业务,配置一行就够:

bash 复制代码
maxmemory 512mb
maxmemory-policy allkeys-lru   # 通用缓存场景,最常用组合

这里有个新手容易懵的地方:两个维度不是「非此即彼」,而是组合拳。看下面这张图,左边挑「删哪些」,右边挑「按什么删」,搭在一起才是完整的策略。

三、每种策略适合什么场景(举例子)

判断用 volatile 还是 allkeys,关键就看一件事:你的 Redis 是不是「身兼两职」------既当缓存,又当存储。

说几个我配过、也见过别人配过的典型场景:

1)纯缓存、访问有热点 ------ allkeys-lru。 电商首页的热销榜,数据全在缓存里,流量集中在少数爆款。内存一紧,LRU 优先淘汰没人看的冷门商品,把爆款留在内存里,命中率最稳。这是我用得最多的一档。

2)缓存 + 存储混合 ------ volatile-lru。 Redis 里既有用户会话 session(设了 30 分钟 TTL),又有不想丢的用户表缓存(不设 TTL)。用 volatile-lru 只动 session,业务数据一个都不碰。注意有个坑:session 也得真设 TTL,否则 volatile 系列「无米可炊」,直接退化成 noeviction------这是我一朋友踩过的第二个翻车现场,排障排到怀疑人生才发现是自己没设 TTL。

3)访问冷热分明、怕偶发访问骗算法 ------ allkeys-lfu。 精准投放位的数据,平时几乎没人碰,但每个月固定被大规模拉一次。LRU 看到它「最近没被访问」就随手删了;LFU 记得它历史访问得多,会手下留情。

4)和上游强同步的临时缓存 ------ volatile-ttl。 这些 key 设了很短的 TTL,反正时间一到也要重拉,那内存紧张时就先删这些「命不久矣」的,性价比最高。

5)绝不能丢的数据 ------ noeviction。 分布式锁、消息队列这类,宁可报错也别让锁悄悄消失------锁丢了比报错可怕多了。

四、淘汰策略和缓存击穿是什么关系

淘汰策略不会直接制造缓存击穿,但选错了策略,等于亲手把还没过期的热点 key 推到了刀口上。

缓存击穿(Cache Breakdown):单个热点 key 过期或被清掉的瞬间,大量并发请求同时未命中缓存、全部打到数据库。可以理解成独木桥桥板塌了,人群全挤向桥头。

教科书里的击穿,说的是「热点 key 到点过期」。但淘汰会把风险面扩大:allkeys-lru 在内存压力下,会把还没过期、只是最近访问频率降下来的热点 key 也当冷数据清掉。 缓存从有变无,一堆请求瞬间回源数据库,效果和击穿一模一样------而且更隐蔽,因为 key 压根没过期,你排查半天都想不到是淘汰干的。

看这张时序图(图 2),key 消失瞬间发生了什么:

我见过的真实翻车(示例场景):大促前运营预热了一批商品数据,内存被别的冷数据占满,预热数据被 allkeys-lru 当「冷数据」清了。秒杀一开始,所有请求直接打穿数据库,监控里 evicted_keys 冲上一个台阶------那批 key 明明一个都没过期。

那怎么防?三板斧:热点数据不设过期时间 (物理永不过期,也永远不会被 volatile 系列淘汰);重建时加互斥锁 (只放一个线程去查库,其他人等待);逻辑过期 + 异步重建(value 里记个逻辑过期时间,过期了先返回旧值再偷偷刷新)。前两招治标,第三招治本。

监控是最后的哨兵:INFO stats 里的 evicted_keys 一旦飙升,别犹豫,先看是不是策略把热点清走了。

五、底层原理:近似 LRU/LFU 与淘汰池

Redis 用的从来不是教科书里的标准 LRU,而是一种「随机采样 + 淘汰池」的近似方案,拿一点点精度换常数级内存开销。

LRU(Least Recently Used):按「最近多久没被访问」淘汰的算法,最久没被用的先删,是淘汰家族里最常用的选手。

标准 LRU 得给每个 key 维护一条双向链表,走到哪排到哪,内存开销太大,Redis 不想付这笔账。它改成:每个 key 的对象头里塞一个 24 bit 字段,记最近一次访问时间(俗称 LRU 时钟)。要淘汰时,随机抽 maxmemory-samples 个 key(默认 5 个),比谁最久没被访问,删最旧的那个。这就是近似 LRU------像老师抽查作业,不会逐本看完全班,随机抽几本判个大概。

Redis 3.0 又加了张「小抄」:淘汰池。抽中的候选先丢进一个容量 16 的池子里,跨轮次保留,池里永远留着一批「最该淘汰」的候选。这样精度蹭蹭往上涨,接近理论 LRU,内存开销还是常数。看下图,抽查出来的候选进池子排队,队尾最该删。

LFU 则换了套思路。LFU(Least Frequently Used):按「最近一段时间被访问的次数」淘汰的算法,访问得越少越先被删。它治的是 LRU 的软肋:一个 key 只是「刚刚」被访问了一次,就会被当成刚翻红的明星保护起来,而它可能一年到头就这一次。

Redis 4.0 起复用那 24 bit 字段:高 16 位记「上次衰减时间」,低 8 位当访问计数器。8 位最多记到 255,所以访问次数不是每次 +1,而是按概率递增 ------越到后面越难涨,类似 Morris 计数器,省内存又够用。计数器还会随时间衰减(lfu-decay-time,默认 1 分钟降一次),避免一个数据十年前火过就一直占着坑。想微调采样精度,用 maxmemory-samples;想调 LFU 敏感度,动 lfu-log-factorlfu-decay-time

六、两个高频疑问一次说清

这两个问题几乎每次讲淘汰都有人问,我一次性说清。

*可能有人会问:volatile- 系列,如果我的 key 全都没设过期时间会怎样?**

会退化成 noeviction 的行为------想淘汰却发现「无米可炊」,内存满了照样报 OOM。所以用 volatile 系列之前,先确认你手里有足够多设了 TTL 的临时数据。
可能有人会问:内存淘汰和过期键删除,是一回事吗?

不是。过期删除是 key 到点自己消失(惰性删除 + 每秒抽样的定期删除);内存淘汰是内存超限时被迫删,跟 key 过没过期没关系。两套机制、两个触发源,面试和排查都容易混,记住它们各管各的就行。

说白了,Redis 内存淘汰策略没有银弹,只有「合适」:纯缓存就 allkeys-lru,身兼存储就用 volatile 系列守住业务数据,绝不能丢的就 noeviction 别硬塞。配完盯住两个指标------evicted_keys 和命中率,比背策略名字有用得多。

缓存击穿的完整防御(互斥锁重建、逻辑过期、永不过期)值得单独写一篇,想看的评论区喊一声。

我是晚安code,持续分享编程干货。觉得有用的话记得点赞收藏和关注~也欢迎在评论区聊聊:你在生产环境用的是哪套 Redis 内存淘汰策略,遇到过热点 key 被 LRU 误杀吗?

相关推荐
goyeer1 小时前
Liunx日志管理与journalctl
java·linux·运维·服务器·运维开发·信息化·信息化企业管理
Dovis(誓平步青云)2 小时前
从Redis指标采集到异常告警:redis_exporter + Prometheus 完整实战
服务器·数据库·人工智能·redis·架构·prometheus·vibe coding
tianyu2342 小时前
Java 正则表达式终极指南:从 Pattern 到 Matcher,从双重转义到性能优化,一篇全搞定
java·开发语言·正则表达式·group·find·pattern·matcher
Emily156853598705 小时前
Emerson 1C31129G03 Analog Input Module
java·开发语言·前端·plc·emerson·1c31129g03·input module
就叫_这个吧9 小时前
Java递归方法实现面包屑导航
java·开发语言
fīɡЙtīиɡ ℡10 小时前
AI 应用系统设计
java·开发语言·人工智能
城管不管10 小时前
重生——第十一次面试之挖财一面2026.8.19已OC
java·服务器·jvm·数据库·spring·面试·职场和发展
码匠许师傅10 小时前
【C++ 面试真题】26. 聊聊 C++ 的智能指针
java·c++·面试
AI绘画哇哒哒10 小时前
【建议收藏!】35岁后端血泪忠告,这3类人别硬转Agent(过来人亲述)
java·人工智能·后端·ai·程序员·大模型·agent