← 返回题目列表

Redis 的过期删除和内存淘汰机制是什么?

高频 中等 第 6 / 36 题 更新于 2026/07/27
Redis过期删除内存淘汰maxmemory

简化版

Redis 过期删除主要靠惰性删除和主动过期检查:访问 key 时发现过期就删,同时后台周期性抽样删除过期 key。内存淘汰发生在达到 maxmemory 后,Redis 按配置策略淘汰 key,比如 LRU、LFU、随机、TTL 优先或不淘汰。

详细版

过期删除和内存淘汰是两件事:

  • 过期删除:key 设置了 TTL,到期后应该被删除。
  • 内存淘汰:内存达到上限,为了给新写入腾空间而驱逐一部分 key。

过期删除方式:

  • 惰性删除:访问 key 时检查 TTL,过期就删除。
  • 主动删除:Redis 后台周期性抽样检查带过期时间的 key,删除已经过期的部分。

常见淘汰策略:

  • noeviction:不淘汰,写入报错;
  • allkeys-lru / volatile-lru:按近似 LRU 淘汰;
  • allkeys-lfu / volatile-lfu:按近似 LFU 淘汰;
  • allkeys-random / volatile-random:随机淘汰;
  • volatile-ttl:优先淘汰剩余 TTL 更短的 key。

完整版教学

一、过期不代表立刻物理删除

给 key 设置 TTL 后,到了过期时间,这个 key 在逻辑上已经不可用,但 Redis 不一定在那一刻立即把它从内存里删除。

原因很简单:如果 Redis 为每个 key 都设置一个定时器,到点立刻删除,key 很多时定时器成本会很高。所以 Redis 用惰性删除加主动抽样删除来平衡性能和内存释放。

比如 1000 万个 key 都设置了 TTL,如果 Redis 为每个 key 精确定时,到期瞬间就要维护海量定时事件,主线程会承受很高调度成本。Redis 的选择是把过期判断延后到访问时,以及后台周期抽样清理。这样牺牲的是“物理删除的及时性”,换来运行时开销可控。

TTL 到期:
逻辑上:key 已过期,不应再返回给用户
物理上:可能还占内存,等待访问触发或后台抽样清理

二、惰性删除解决访问正确性

惰性删除发生在访问 key 时。Redis 检查这个 key 是否过期,如果过期,就删除并返回不存在。

这保证了用户不会读到已经过期的数据。缺点是如果某些过期 key 再也没人访问,它们可能继续占用内存一段时间。

惰性删除很适合保证语义正确:只要你访问过期 key,Redis 会发现它已过期并当作不存在处理。问题是冷 key 如果过期后永远不再被访问,就不会被惰性删除触发,只能等主动过期检查或内存淘汰处理。因此只靠惰性删除不能保证内存及时回收。

三、主动删除负责后台清理

Redis 会周期性从设置了过期时间的 key 中抽样检查,发现过期就删除。它不是每轮扫描全库,因为全量扫描会阻塞主线程并带来巨大开销。

抽样机制意味着过期 key 的清理是渐进的。业务如果短时间写入大量带 TTL 的 key,到期后可能出现内存释放不够及时的现象。

例如业务批量写入 500 万个 1 小时 TTL 的验证码 key,如果它们在同一时间过期,主动删除会逐步抽样清理,而不是瞬间释放所有内存。此时可能看到内存下降有延迟,甚至在 maxmemory 压力下触发淘汰策略。TTL 加随机值不只防缓存雪崩,也能让过期清理压力更平滑。

删除方式触发时机优点局限
惰性删除访问 key 时保证读不到过期值冷 key 可能滞留
主动删除后台周期抽样渐进释放内存不是全量即时清理
内存淘汰达到 maxmemory保护写入空间可能淘汰未过期 key

记忆钩子:过期删除管“到期了怎么清”,内存淘汰管“内存满了怎么腾”,这两套机制不要混。

四、内存淘汰是 maxmemory 下的保护机制

当 Redis 使用内存达到 maxmemory,如果还要写入新数据,就需要根据策略决定是否淘汰旧 key。

这里要分清两组策略:

  • allkeys-*:所有 key 都可能被淘汰;
  • volatile-*:只在设置了过期时间的 key 中淘汰。

如果你把 Redis 当纯缓存,常用 allkeys-lruallkeys-lfu;如果有些 key 不能被淘汰,就要谨慎设置策略,甚至将缓存和关键数据拆到不同实例。

volatile-* 只从设置了过期时间的 key 中淘汰,这意味着如果大量 key 没有 TTL,而策略是 volatile-lru,Redis 可能找不到合适淘汰对象,写入仍会失败。纯缓存实例更常见 allkeys-lruallkeys-lfu;既有缓存又有重要状态的实例,要么分实例隔离,要么非常谨慎地配置淘汰策略。

五、LRU 和 LFU 都是近似实现

Redis 的 LRU/LFU 不是精确维护一个全局链表,那样成本太高。它采用抽样和近似统计,在性能和效果之间取平衡。

这意味着淘汰结果不是绝对精确的“最久未使用”或“最低频使用”,但对缓存场景通常足够好。

精确 LRU 需要每次访问都移动节点并维护全局链表,在 Redis 高吞吐场景下成本太高。Redis 采用采样方式,从一小批候选 key 中挑更符合策略的淘汰。采样大小越大,结果越接近精确,成本也更高;这就是缓存系统常见的工程折中。

六、生产配置要先区分实例用途

如果 Redis 存的是可重建缓存,淘汰是正常保护机制;如果 Redis 存的是分布式锁、会话、重要状态,随意淘汰可能造成业务错误。最稳妥的方式是按用途拆实例:缓存实例允许淘汰,关键状态实例尽量不淘汰并设置容量告警。还要避免所有 key 没有 TTL,否则缓存会逐步变成“内存数据库”,最终只能靠 maxmemory 硬淘汰。

缓存实例:allkeys-lru/lfu + TTL + 容量监控
状态实例:谨慎 noeviction + 持久化/高可用 + 告警

七、常见误区与追问

  • 误区:key 到期后一定马上从内存消失。 Redis 过期 key 逻辑不可读,但物理删除依赖惰性删除和主动抽样,可能延迟释放。
  • 误区:过期删除和内存淘汰是一回事。 过期删除处理 TTL 到期,内存淘汰处理 maxmemory 满后的空间腾挪。
  • 误区:volatile-lru 会淘汰所有最近少用的 key。 volatile 策略只在设置了 TTL 的 key 中淘汰,没 TTL 的 key 不在候选范围。
  • 误区:LRU/LFU 是绝对精确的全局算法。 Redis 为了性能采用近似采样,不保证每次都淘汰全局最优 key。
  • 追问:为什么 TTL 要加随机值? 可以避免大量 key 同时过期造成回源洪峰,也能平滑主动删除压力。
  • 追问:缓存和关键状态能混一个实例吗? 不建议,缓存淘汰可能影响关键状态,最好按数据重要性和淘汰策略拆分实例。

八、加强记忆

Redis 过期删除和内存淘汰是两件事:过期删除处理 TTL 到期,惰性删除保证访问时不返回过期值,主动抽样负责后台渐进清理;内存淘汰处理 maxmemory 满后如何腾空间,按 LRU、LFU、随机、TTL 等策略选 key。缓存实例可以使用 allkeys-lruallkeys-lfu,关键状态不要和可淘汰缓存混放。TTL 随机化、容量监控和实例隔离,是这题落到生产的关键。