当前位置:首页 > 问答 > 正文

Redis里头那种混合过期策略怎么搞的,研究和实现上的一些思考与尝试

Redis在处理键过期这件事情上,并没有采用单一的死板方法,而是用了一种组合拳,官方并没有一个专门的术语叫“混合过期策略”,但根据其源码实现和官方文档的描述,我们可以把它理解成一种“惰性删除”加上“定期抽样删除”的协同工作模式,这种设计主要是为了在内存占用、CPU消耗和响应延迟之间找到一个精妙的平衡点,如果只用一种方法,很容易出问题。

第一部分:为什么不能只用一种方法?

Redis里头那种混合过期策略怎么搞的,研究和实现上的一些思考与尝试

想象一下,如果Redis只采用“惰性删除”,这意味着,一个键即使已经过期了,只要没有客户端来访问它,它就会一直赖在内存里,成为所谓的“僵尸键”,当这种键非常多的时候,就会造成严重的内存浪费,甚至导致内存耗尽,这显然是无法接受的。

反过来,如果Redis试图用一个非常主动的、实时的定时器来监控每一个有过期时间的键,时间一到就立刻删除,行不行呢?理论上最干净,但实践上代价太高,Redis里面可能有数百万个键设置了过期时间,如果为每一个键都维护一个独立的定时器,那么CPU资源会大量消耗在管理这些定时器上,而不是处理真正的客户端请求,这会严重影响Redis的性能。

Redis里头那种混合过期策略怎么搞的,研究和实现上的一些思考与尝试

Redis的设计者必须想一个折中的办法。

第二部分:两种策略如何协同工作(混合策略的核心)

Redis里头那种混合过期策略怎么搞的,研究和实现上的一些思考与尝试

  1. 惰性删除:守株待兔的被动清理

    • 怎么做:这个策略发生在客户端访问某个键的时候,当客户端执行GET key命令时,Redis在返回数据之前,会先检查这个键是否设置了过期时间,以及是否已经过期。
    • 结果:如果过期了,Redis会立即删除这个键,然后向客户端返回一个空值,就好像这个键从来不存在一样,如果没过期,就正常返回数据。
    • 优点:简单直接,CPU开销为零(因为只有在被访问时才检查),不会对不常用的键浪费计算资源。
    • 缺点:对内存不友好,如果过期键再也没被访问,它们就永远无法被清理。
  2. 定期抽样删除:主动出击的定期扫描

    • 怎么做:为了弥补惰性删除的不足,Redis会每隔一段时间(默认是每秒10次,可配置)主动发起一轮过期键清理任务,但这个“主动”也不是蛮干,而是非常聪明的抽样扫描。
    • 过程:它不会检查所有的数据库,也不会检查数据库里所有的过期键,每次任务,它会从一定数量的数据库中(默认16个),随机抽取一定数量(默认20个)的设置了过期时间的键进行检查,这就像质检员不是检查流水线上每一个产品,而是每隔一段时间随机抽检一批。
    • 抽样策略:对于抽中的键,如果发现已经过期,就删除它,Redis还会有一个简单的算法:如果在这一次抽检中,发现过期键的比例很高(比如超过25%),说明这个数据库里“垃圾”很多,那么Redis会立即再进行一轮抽检,直到过期键的比例降下来为止,这是一种自适应的方法,能在垃圾多的时候加快清理速度。
    • 优点:一定程度上解决了内存泄漏问题,能定期回收不再使用的内存。
    • 缺点:毕竟不是全量扫描,仍然会有一些过期键成为漏网之鱼,直到被惰性删除捕获或在下一次抽样中被发现,如果设置得太频繁,会增加CPU压力。

第三部分:研究和实现上的思考与尝试

这种混合策略体现了工程上典型的权衡艺术,其核心思考是:用可控的、微小的性能损失,去换取确定性的内存回收,同时避免出现极端情况。

  • 关于抽样数量的权衡:抽样数量(默认20)是一个关键参数,如果抽得太少,清理效率低,内存回收慢;如果抽得太多,在键数量巨大的情况下,每次扫描的CPU开销会变大,可能影响正常请求,Redis选择了一个经验值,并在遇到大量过期键时通过“连续抽样”来动态加速,这是一种很巧妙的优化。
  • 关于频率的权衡:扫描频率(每秒10次)也是一个平衡点,频率太低,两次扫描之间积累的过期键可能太多,导致内存使用量的毛刺;频率太高,则CPU负担重,每秒10次意味着平均100毫秒一次,对于大多数场景来说,这个延迟是可以接受的。
  • 应对极端情况:除了这两种常规策略,Redis还有一种机制来处理一种极端情况:当内存使用达到maxmemory上限时,它会执行配置好的内存淘汰策略(如LRU、LFU等),这个机制是最后的防线,它会在需要腾出空间时,从所有键(包括没有设置过期时间的键)中根据策略进行淘汰,这可以看作是整个内存管理系统的第三道保险。

Redis的过期策略不是一个简单的功能,而是一个小型的系统,它通过“惰性删除”保证常规操作的高性能,通过“定期抽样删除”实现基本的内存回收效率,再通过“内存淘汰机制”兜底最坏情况,这种分层、混合的设计思路,避免了单一方法的缺陷,以较低的代价实现了足够好的效果,是Redis能够高效稳定运行的重要基石之一,这种思路在很多其他系统的设计中也能看到影子,即通过多种简单策略的组合来应对复杂的需求。