谈谈 Redis 的过期策略?

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

Redis 的过期策略解决的是一個关键问题:对于那些设置了 TTL 的键,Redis 如何知道并在何时删除它们?

如果只设置 TTL 而没有有效的清理机制,会导致大量已过期的键依然占用内存,造成严重的内存泄漏。Redis 通过一个精巧的 "惰性删除" 与 "定期删除" 相结合的组合策略 来解决这个问题,在 CPU 和内存之间取得了优异的平衡。

一、 核心策略:惰性删除

这是 Redis 的第一道防线,一种被动式的删除策略。

机制原理:

  • 当客户端尝试访问一个键时,Redis 会首先检查该键是否设置了过期时间。
  • 如果设置了并且当前时间已超过过期时间,那么 Redis 会立即删除这个键,然后像这个键不存在一样返回结果。
  • 核心思想: 只有在被访问时,才去检查并删除过期键。

实例:

# 设置一个 10 秒后过期的键
127.0.0.1:6379> SET mykey "hello" EX 10
OK

# 5 秒内访问,正常返回值
127.0.0.1:6379> GET mykey
"hello"

# 等待 15 秒后访问,触发惰性删除,返回 nil
127.0.0.1:6379> GET mykey
(nil)
# 此时,mykey 已被删除

优缺点分析:

  • 优点:
    • 对 CPU 极其友好:删除操作只发生在键被访问时,不会消耗额外的 CPU 周期去检查那些永不会被访问的过期键。
    • 实现简单:逻辑清晰,集成在命令处理流程中。
  • 缺点:
    • 对内存不友好,是内存泄漏的主因:如果一个键设置了 TTL 但之后永远不再被访问,那么即使它早已过期,也会一直残留在内存中,占用空间。这是一种典型的内存泄漏。

二、 核心策略:定期删除

为了弥补惰性删除的缺陷,Redis 引入了第二道防线:定期删除。这是一种主动式的删除策略。

机制原理:

  • Redis 会周期性地随机抽取一部分设置了过期时间的键,检查它们是否过期,并删除其中的过期键。
  • 这个过程不是遍历所有键(那样会严重阻塞 Redis),而是采用了一种自适应、可中断的抽样算法。

详细工作流程:

  1. 定时触发:默认情况下,Redis 每秒会进行 10 次定期删除操作(可编辑 redis.conf 配置文件中的 hz 配置项调整)。
  2. 随机抽样:每次定期删除,Redis 会从设置了过期时间的键中随机抽取 20 个键(默认值,可配置)作为样本。
  3. 删除过期键:检查这 20 个样本键,删除其中所有已过期的键。
  4. 判断是否继续:这是一个关键步骤。Redis 会计算在这批样本中,过期键的比例。
    • 如果比例超过 25%:说明当前数据库中过期键较多,Redis 会立即重复步骤 2 和 3,再次随机抽取 20 个键进行检查和删除。
    • 如果比例低于 25%:说明过期键不多,本次定期删除任务结束。
  5. 时间限制:为了防止这个过程无限循环下去消耗过多 CPU,整个定期删除过程有一个隐式的时间上限。

这个过程保证了:

  • 当内存中过期键很多时,Redis 会变得更"积极",进行多轮清理。
  • 当内存中过期键很少时,Redis 会很快结束工作,避免 CPU 空转。

优缺点分析:

  • 优点:
    • 减少了内存泄漏:主动清理了那些不再被访问的"僵尸"键。
    • CPU 消耗可控:通过抽样和循环判断,避免了对整个数据库的扫描。
  • 缺点:
    • 不是实时的:仍然有一些键可能在过期后,需要等待下一次定期删除周期才能被清理掉。
    • 增加了 CPU 开销:尽管是可控的,但依然比纯粹的惰性删除消耗更多 CPU。

三、 生产环境中的考量与配置

1. 策略总结

Redis 通过 "惰性删除 + 定期删除" 的组合拳,实现了一种权衡:

  • 惰性删除 确保了 CPU 资源不被浪费在清理永远不会被访问的键上。
  • 定期删除 确保了内存资源不被大量已过期的"僵尸"键长期占用。

这是一个在 CPU 利用率 和 内存利用率 之间取得的经典权衡。

2. 重要配置参数

  • hz:默认值为 10。它定义了每秒执行定期删除等后台任务的频率。提高这个值(如设为 20)会让 Redis 更频繁地检查过期键,内存清理更及时,但也会消耗更多 CPU。通常不建议超过 100。
# 在 redis.conf 中调整
hz 20

3. 监控与告警

即使有这两种策略,仍然可能出现内存缓慢增长的情况,尤其是在有很多设置了长 TTL 且不再被访问的键时。因此,在生产环境中必须:

  • 监控 used_memory:关注内存使用量的趋势。
  • 设置内存告警:当内存使用率超过 80% 时触发告警。
  • 使用 SCAN 命令:定期扫描数据库,分析是否存在大量已过期但未被删除的键。

总结

面试官,Redis 的过期策略是一个精妙的设计:

  1. 它不是单一机制,而是一个互补的混合系统。惰性删除负责"按需清理",定期删除负责"主动巡检"。
  2. 其核心设计哲学是权衡。在保证性能(CPU)和资源利用率(内存)之间找到了一个高效的平衡点,避免了传统定时扫描带来的全局阻塞问题。
  3. 理解这个机制对生产运维至关重要。它解释了为什么有时 Redis 的内存不会在键过期后立即下降,也指导我们如何通过调整 hz 等参数来优化 Redis 的行为以满足特定业务场景的需求。

简单来说,你可以把惰性删除看作是一个"守门员",只在球(访问请求)来时行动;而定期删除则是一个"巡逻队",定期在场地内巡视,清理发现的垃圾。两者协同工作,共同维护着 Redis 内存的整洁与高效。