谈谈 Redis 的过期策略?

一则或许对你有用的小广告

欢迎 加入小哈的星球 ,你将获得: 专属的项目实战(已更新的所有项目都能学习) / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

  • 新开坑项目: 《Spring AI 项目实战(问答机器人、RAG 增强检索、联网搜索)》 正在持续爆肝中,基于 Spring AI + Spring Boot3.x + JDK 21...点击查看;
  • 《从零手撸:仿小红书(微服务架构)》 已完结,基于 Spring Cloud Alibaba + Spring Boot3.x + JDK 17...点击查看项目介绍; 演示链接: http://116.62.199.48:7070/;
  • 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/

谈谈 Redis 的过期策略?谈谈 Redis 的过期策略?

面试考察点

  1. 基础掌握度:面试官不仅仅是想知道 Redis 有过期策略,更是想知道你是否清楚 Redis 采用的是 惰性删除 + 定期删除 的组合方案,而不是定时删除,以及为什么要这样设计。

  2. 原理理解深度:考察你是否了解过期字典的内部结构,定期删除的采样逻辑(随机抽取、循环检测),以及这些设计背后的性能与内存权衡。

  3. 知识关联能力:能否自然过渡到 "过期策略解决不了内存问题时怎么办" → 内存淘汰策略(maxmemory-policy),展示完整的知识体系。

核心答案

Redis 采用 惰性删除 + 定期删除 两种策略配合使用:

策略触发时机行为特点
惰性删除访问 Key 时发现过期才删除对 CPU 友好,但可能有垃圾数据残留
定期删除每隔一段时间随机抽查一批 Key,删除过期的折中方案,平衡 CPU 和内存

一句话结论:Redis 不使用定时删除(到期立即删),因为那会消耗大量 CPU 资源。而是用 "惰性 + 定期" 的组合策略,在 CPU 和内存之间取一个平衡。如果过期策略清理不过来,内存还是满了,就会触发 内存淘汰策略

深度解析

一、三种过期策略的理论对比

在讲 Redis 的方案之前,先了解一下理论上过期策略有三种:

上图展示了三种过期策略的理论对比:

  • 定时删除:每个 Key 设置一个定时器,到期立即删除。内存最优,但 CPU 开销太大,Redis 没有采用
  • 惰性删除:只在访问时检查。CPU 最友好,但过期不访问的 Key 会一直占内存,Redis 采用
  • 定期删除:每隔一段时间抽查。是 CPU 和内存的折中方案,Redis 采用

Redis 选择 惰性 + 定期 的组合策略,既不会消耗过多 CPU,又能及时清理大部分过期数据。

二、过期字典:Redis 如何记录 Key 的过期时间?

上图展示了 Redis 存储过期时间的内部结构:

  • Redis 在每个数据库(redisDb)中维护了两个字典:dict(主字典,存储所有 Key-Value 数据)和 expires(过期字典,存储设置了过期时间的 Key 及其过期时间戳)。
  • 过期字典的 key 指向主字典中的同一个 Key 对象(不是副本),所以不会额外占用太多内存。
  • 当检查一个 Key 是否过期时,只需在 expires 字典中查找对应的时间戳,与当前时间比较即可。

三、惰性删除的工作流程

上图展示了惰性删除的判断流程:

  • 核心思路:每次访问 Key 时,顺便检查一下是否过期。如果过期了,就删除并返回空;没过期就正常返回数据。
  • 优点:对 CPU 极度友好,只有在业务需要访问时才执行删除操作,不会浪费 CPU 资源。
  • 缺点:如果有些 Key 过期了但 一直没有被访问,它们就会一直占用内存,成为 "垃圾数据"。

四、定期删除的工作流程

定期删除是 Redis 主动清理过期数据的机制,它的执行逻辑比惰性删除复杂得多。

上图展示了定期删除的完整流程,核心逻辑:

  • 随机抽取:每 100ms 从过期字典中随机抽取 20 个 Key,检查是否过期。
  • 自适应循环:如果这 20 个 Key 中过期的比例超过 25%,说明当前过期 Key 很多,就再随机抽 20 个继续清理,直到过期比例降到 25% 以下,或者执行时间超过上限(默认 25ms)。
  • 为什么限制 25ms:定期删除是在 Redis 主线程中执行的,如果清理时间过长会阻塞正常请求,所以必须严格限制单次执行时长。

定期删除的局限性

  • 由于是 随机抽样,总会有一些过期 Key 被遗漏,不会全扫一遍。
  • 如果过期 Key 数量非常多,而随机抽取的概率有限,仍然会有大量过期 Key 残留在内存中。

五、过期策略 + 内存淘汰策略 = 完整方案

"惰性 + 定期" 只能清理 过期了的 Key。如果有些 Key 没有设置过期时间,或者过期策略清理速度跟不上新 Key 的写入速度,内存还是会满。这时就需要 内存淘汰策略 出场了。

# redis.conf 配置最大内存和淘汰策略
maxmemory 4gb
maxmemory-policy allkeys-lru

Redis 8 种内存淘汰策略

策略行为适用场景
noeviction不淘汰,写入直接报错(默认数据不能丢失
allkeys-lru从所有 Key 中淘汰最久未使用的通用缓存(推荐)
allkeys-lfu从所有 Key 中淘汰使用频率最低的(Redis 4.0+)热点数据明显的缓存
allkeys-random从所有 Key 中随机淘汰无访问热点
volatile-lru从设置了过期的 Key 中淘汰最久未使用的混合使用(部分持久化)
volatile-lfu从设置了过期的 Key 中淘汰使用频率最低的混合使用
volatile-random从设置了过期的 Key 中随机淘汰混合使用
volatile-ttl从设置了过期的 Key 中淘汰剩余时间最短的业务有明确 TTL 需求

生产环境推荐allkeys-lru(通用缓存)或 allkeys-lfu(热点明显的缓存)。

面试高频追问

  1. 追问一:为什么 Redis 不用定时删除?

    定时删除需要为每个设置了过期时间的 Key 维护一个定时器,如果同时有几十万个 Key 设置了过期时间,就会有几十万个定时器同时运行,严重消耗 CPU 资源,影响 Redis 处理正常请求的性能。Redis 作为高性能缓存,把 CPU 留给业务处理更重要。

  2. 追问二:LRU 和 LFU 淘汰策略的区别?

    • LRU(Least Recently Used):淘汰最久没被访问的 Key,基于 "最近访问的数据大概率还会被访问" 的假设。Redis 的 LRU 是近似算法,随机采样 N 个 Key(默认 5 个),淘汰其中最久未访问的。
    • LFU(Least Frequently Used):淘汰访问频率最低的 Key,基于 "访问频率高的数据更有价值" 的假设。Redis 4.0 引入,更适合 热点数据明显 的场景(比如 20% 的数据占 80% 的访问量)。
  3. 追问三:如何查看 Redis 当前内存使用情况?

    # 查看内存使用详情
    INFO memory
    
    # 关键指标:
    # used_memory:已用内存
    # maxmemory:最大内存限制
    # maxmemory_policy:当前淘汰策略
    

常见面试变体

  • 变体一:"Redis 的 Key 过期了是怎么被删除的?"
  • 变体二:"Redis 过期策略和内存淘汰策略的区别?"
  • 变体三:"Redis 内存满了怎么办?"
  • 变体四:"Redis 的 LRU 算法了解吗?是精确的还是近似的?"

记忆口诀

过期策略:惰性删除(访问才删)+ 定期删除(随机抽查,过期超 25% 继续抽)。

为什么不单独用一种:惰性删不干净(残留垃圾),定期删不精确(随机抽样),两者互补。

内存满了:过期策略搞不定 → 内存淘汰策略接手 → allkeys-lru 最常用。

总结

Redis 过期策略采用 惰性删除 + 定期删除 的组合方案。惰性删除在访问时才检查并删除过期 Key,对 CPU 友好;定期删除每 100ms 随机抽样一批 Key 清理过期的,防止大量过期数据残留。两者互补,在 CPU 和内存之间取得平衡。如果过期策略仍无法释放足够内存,就会触发 内存淘汰策略(推荐 allkeys-lruallkeys-lfu)。