Redis Key 过期后会立刻删除吗?过期删除与内存淘汰策略详解
2026/6/1 17:11:56 网站建设 项目流程

Redis 里的 key 可以设置过期时间,但 key 到期并不意味着会被立刻删除。Redis 需要在 CPU 成本和内存释放之间做平衡,所以采用的是:惰性删除 + 定期删除

当内存真的不够用时,Redis 还会根据内存淘汰策略删除一部分 key。

这篇把两个问题放在一起讲:

  1. key 过期后怎么删除?
  2. 内存满了以后怎么淘汰?

一、惰性删除:访问时才检查

惰性删除的意思是:设置了过期时间后,Redis 不会一直盯着这个 key。只有当客户端访问这个 key 时,Redis 才检查它是否过期。

优点是对 CPU 友好,因为 Redis 不需要为每个过期 key 立刻做检查。

缺点是对内存不友好:如果一个 key 已经过期,但再也没人访问它,它可能会继续占在内存里。

二、定期删除:周期性抽样清理

为了避免过期 key 一直占内存,Redis 会定期抽取一部分 key 检查并删除过期数据。

定期清理有两种模式:

模式特点
SLOW定时任务,默认 10hz,每次不超过 25ms
FAST执行频率不固定,两次间隔不低于 2ms,每次不超过 1ms

定期删除的优点是可以主动释放内存,同时通过限制时长和频率减少对 CPU 的影响。缺点是很难精确控制执行频率:太频繁会影响 CPU,太少又会让过期 key 占用内存。

所以 Redis 最终采用组合策略:

Redis 过期删除策略 = 惰性删除 + 定期删除

三、内存满了怎么办?

过期删除解决的是“已经设置 TTL 的 key 到期后怎么清理”。但如果 Redis 内存已经满了,新的写入进来怎么办?

这就涉及内存淘汰策略。Redis 会按照配置的规则选择一部分 key 删除。

四、Redis 的 8 种淘汰策略

策略作用范围淘汰规则
noeviction不淘汰内存不足时新写入直接报错
volatile-ttl设置了 TTL 的 key剩余 TTL 越小越先淘汰
allkeys-random全体 key随机淘汰
volatile-random设置了 TTL 的 key随机淘汰
allkeys-lru全体 key淘汰最近最少使用
volatile-lru设置了 TTL 的 key淘汰最近最少使用
allkeys-lfu全体 key淘汰访问频率最低
volatile-lfu设置了 TTL 的 key淘汰访问频率最低

默认策略是noeviction,也就是内存不足时不删除任何数据,新写入会报错。

五、LRU 和 LFU 的区别

  • LRULeast Recently Used,最近最少使用。它看的是“多久没被访问过”。

    当前时间 - 最后一次访问时间 = 越大越优先淘汰

  • LFULeast Frequently Used,最少频率使用。它看的是“访问次数多不多”。

    访问频率越低,淘汰优先级越高

简单理解:LRU 更关注最近有没有用,LFU 更关注长期用得多不多

六、开发中怎么选?

业务特点推荐策略
有明显冷热数据allkeys-lru
访问频率差别不大allkeys-random
有置顶数据不能淘汰volatile-lru,置顶 key 不设置 TTL
短时高频访问明显allkeys-lfuvolatile-lfu
不允许 Redis 自动删数据noeviction

面试里经常会问:“数据库有 1000 万数据,Redis 只能缓存 20 万,怎么保证 Redis 里都是热点数据?”

可以回答:使用allkeys-lruallkeys-lfu,让 Redis 自动淘汰不常访问的数据,留下更热的数据。

七、深入分析:策略背后的权衡与调优

前面的章节介绍了 Redis 过期删除和内存淘汰的“是什么”和“怎么选”。这一节我们来深入探讨一下“为什么”——即这些策略背后的设计哲学、潜在问题以及生产环境中的调优思路。

7.1 惰性删除与定期删除的深层权衡

Redis 选择“惰性删除 + 定期删除”的组合,本质上是空间(内存)与时间(CPU)的经典权衡

  • 纯惰性删除的极端情况:想象一个场景,一批用于临时会话的 key 在凌晨3点同时过期,但此后再也没有被访问。在纯惰性删除策略下,这些“僵尸 key”会一直占用内存,直到 Redis 重启或内存淘汰被触发。这可能导致内存使用率虚高,甚至引发不必要的淘汰。
  • 纯定期删除的挑战:如果试图通过提高定期删除的频率和力度来立刻清理所有过期 key,会显著增加 CPU 开销,在 key 数量巨大时可能影响主线程处理正常命令的延迟,违背 Redis 高性能的初衷。

生产启示:监控expired_keys指标。如果这个值增长缓慢,而used_memory持续高位,可能意味着有过期 key 堆积,需要考虑调整hz配置(控制定期删除频率),或在业务层确保对已过期的 key 有访问清理机制。

7.2 内存淘汰策略的算法实现与近似性

需要明确的是,Redis 实现的 LRU 和 LFU 都是近似算法,而非精确实现。

  • Redis LRU:并非维护一个全局的精确访问时间链表(那会带来巨大开销),而是对每个 key 采样一个 24 位的“空闲时间(idle time)”戳。淘汰时,从采样池中选出空闲时间最长的 key 进行淘汰。这是一种概率性 LRU,在保证性能的同时,大体上能淘汰掉最久未使用的数据。
  • Redis LFU:使用 Morris 计数器来近似统计访问频率,并且设计有衰减机制(lfu-decay-time),防止早期高频访问的 key 永久驻留。这比简单的访问计数更智能,能适应访问模式的变化。

生产启示:理解其近似性很重要。allkeys-lru不能保证绝对淘汰最旧的,但能很好地维护热数据集。对于要求极高的场景,可能需要业务层自己实现更精确的缓存管理(如二次缓存、本地缓存等)。

7.3 配置maxmemory-policy的实践细节

  1. volatile-xxx策略的陷阱:如果使用volatile-lruvolatile-lfuvolatile-random,但 Redis 实例中没有 key 设置 TTL,那么当内存满时,这些策略将无 key 可淘汰,效果会退化成noeviction,导致写入错误。务必确保有足够多的 key 设置了过期时间。
  2. allkeys-lru与持久化:如果使用allkeys-lru且开启了 RDB 或 AOF 持久化,被淘汰的 key 虽然从内存中删除,但可能仍然存在于持久化文件中,在下次重启加载时会被重新载入内存。这可能导致重启后内存瞬间打满。需要评估是否有必要对持久化文件进行清理。
  3. 监控指标:关注evicted_keys(被淘汰的 key 总数)和used_memoryevicted_keys的突然飙升是内存压力的明确信号。

7.4 超越内置策略:架构层面的思考

当内置策略无法满足需求时,可以考虑架构升级:

  • 拆分实例:将不同类型、不同重要性的数据存放到不同的 Redis 实例中,并配置不同的maxmemory和淘汰策略。例如,用户会话数据使用allkeys-lru,而重要的配置数据使用noeviction并存放在独立的小内存实例中。
  • 使用 Redis 4.0+ 的模块或新数据结构:例如,对于可容忍丢失的缓存数据,可以考虑使用RedisBloom模块的布隆过滤器先进行一层过滤,减少无效 key 的存储。
  • 客户端策略:在客户端实现本地缓存(如 Guava Cache、Caffeine),并设置更短的 TTL 和大小限制,形成多级缓存体系,减轻 Redis 层的压力和淘汰的敏感性。

7.5 面试深度问题示例

  1. “Redis 的 LRU 算法和传统 LRU 有什么区别?”

    • :Redis 使用的是近似 LRU。它通过随机采样一批 key,并淘汰其中空闲时间最长的 key,而不是维护一个全局的精确链表。这在性能和效果上取得了很好的平衡,虽然不绝对精确,但实践表明足以很好地维护热数据集合。
  2. “如何发现和解决 Redis 中过期 key 大量堆积的问题?”

    • :首先通过INFO命令观察expired_keys的增长是否与预期相符,以及used_memory是否异常高。若存在堆积,可尝试:① 调高hz配置增加定期删除频率;② 检查业务代码,确保对设置了过期时间的 key 有后续的访问或清理逻辑;③ 对于已知会批量过期的 key,可以考虑将其过期时间加上随机抖动,避免同时过期。
  3. “在使用了allkeys-lru的 Redis 中,为什么有时最近访问的数据还是被淘汰了?”

    • :因为 Redis 的 LRU 是近似的。在内存压力极大、key 更新极快的场景下,采样过程可能无法总是捕捉到最新的访问模式。此外,如果所有 key 的访问都非常频繁,LRU 的“最近最少使用”判据也会变得模糊。此时可以评估是否切换为allkeys-lfu,或考虑扩容内存。

七、总结

Redis key 过期不会立刻删除,而是惰性删除定期删除配合;内存不够时才会触发淘汰策略。平时开发最常用的是allkeys-lru,能让最近常访问的数据尽量留在缓存里。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询