调度和就绪状态下Redis过期键多线程清理优化那些事儿
- 问答
- 2026-01-04 22:32:51
- 20
这篇文章主要讲的是Redis是怎么处理那些设置了过期时间的键的,特别是当这些键到期后,Redis如何把它们清理掉以释放内存,这事儿听起来简单,但做起来挺讲究的,因为Redis是单线程处理命令的,如果清理过期键占用了太多时间,就会阻塞其他正常的命令,影响性能,Redis设计了一套组合拳,核心是两种策略:一种叫“惰性删除”,另一种叫“定期删除”,这篇文章的优化重点,就落在“定期删除”这个策略的演进上,特别是如何引入多线程来加速这个过程。
我们来看看“惰性删除”,这个概念很简单,用到的时候再检查”,当客户端尝试访问一个键时,Redis会先检查这个键是否已经过期了,如果过期了,Redis会立刻把这个键删除,然后返回一个空值给客户端,就像这个键从来不存在一样,这种策略的好处是针对性很强,只会在真正需要的时候才付出删除的成本,不会浪费CPU时间在没人关心的键上,但它的缺点也很致命:如果有很多过期键一直没人访问,那么它们就会一直占着内存不释放,成了“内存垃圾”,这就好比家里堆满了过期杂志,你只有想找某本杂志的时候才会发现它过期了然后扔掉,如果你一直不去翻,它们就永远堆在那里。
为了解决惰性删除的不足,Redis必须有一个主动清理的机制,这就是“定期删除”,Redis会定期地(默认是每秒10次)主动去扫描数据库,找出一部分过期的键并把它们删除掉,这个过程的官方名称是“主动过期键收集”,代码里叫activeExpireCycle函数,这篇文章里提到的“调度”和“就绪”状态,其实就是指这个定期任务的不同阶段。
在早期版本中,这个定期删除任务是在Redis的主线程上执行的,它会依次检查每个数据库,从一个包含过期键的哈希表中随机抽取一定数量的键进行检查和删除,为了保证不阻塞主线程太长时间,Redis给这个任务设置了严格的执行时间上限,如果到了时间还没做完,它就停下来,等下一次周期再继续,这就是所谓的“调度”逻辑——决定什么时候开始千活,干多久。
随着内存越来越大,过期键的数量可能变得非常庞大,单线程的定期删除就像一个人打扫一个巨大的体育馆,每次只能扫一小块区域,速度跟不上垃圾产生的速度,结果就是,内存中可能还是会堆积很多本该被清理掉的过期键,导致内存使用率居高不下,甚至可能触发内存淘汰机制(当内存不足时,Redis会根据策略删除一些键,即使它们没过期),这显然不是我们想看到的。
优化来了,这篇文章的核心内容就是描述Redis如何优化这个定期删除过程,关键的改进就是引入了多线程,根据文章描述(这里引用自“Redis过期键多线程清理优化”相关的技术博客和源码分析文章),在Redis的某个较新版本(如6.2或7.0之后)中,定期删除任务被改造成了多线程模式。
具体是怎么做的呢?文章提到,这个优化并没有改变“惰性删除”的逻辑,它仍然是单线程的,优化的是“定期删除”部分,Redis的主线程现在扮演了一个“调度器”的角色,它仍然负责启动定期清理任务,但它自己不再亲自去扫描和删除键了。 Instead,它会把这些脏活、累活——也就是扫描数据库并删除过期键的实际工作——分发给多个后台的“BIO”(后台I/O)线程去并行处理。
这个过程可以这样理解:主线程(调度器)决定要开始一轮清理了(“调度”状态),它把需要清理的任务(划分好的数据库或数据库中的一部分)放进一个任务队列里,那些一直处于等待状态的BIO线程(“就绪”状态)看到队列里有活了,就立刻开始工作,每个线程领一个任务,同时去不同的地方扫描和删除过期键,这样就实现了清理工作的并行化,大大提高了清理效率。
文章指出,这种多线程优化带来了显著的好处,最直接的就是提升了大内存场景下过期键的清理速度,能更及时地释放内存,降低了因为过期键堆积而导致内存不足的风险,由于繁重的清理工作被卸载到了后台线程,主线程被阻塞的时间极大地减少了,这使得Redis能够更快速、更稳定地响应客户端的请求,整体性能和服务质量得到了提升。
这篇文章讲的“那些事儿”,就是Redis通过将原本由主线程单线程执行的、像打扫卫生一样的定期过期键清理任务,巧妙地改造成了一个“主线程调度 + 多后台线程并行执行”的模型,这个优化巧妙地平衡了内存回收的及时性和对主线程的性能影响,是Redis在高性能、低延迟道路上的一次重要演进,它说明了即使在以单线程模型为核心的系统中,通过将合适的任务异步化、并行化,也能有效突破性能瓶颈。

本文由酒紫萱于2026-01-04发表在笙亿网络策划,如有疑问,请联系我们。
本文链接:https://www.haoid.cn/wenda/74589.html
