当一个键处于过期的状态,其实在 Redis 中这个内存并不是实时就被从内存中进行摘除,而是 Redis 通过一定的机制去把一些处于过期键进行移除,进而达到内存的释放,那么当一个键处于过期,Redis 会在什么时候去删除?

几时被删除存在三种可能性,这三种可能性也代表了 Redis 的三种不同的删除策略。

  • 定时删除:在设置键过去的时间同时,创建一个定时器,让定时器在键过期时间来临,立即执行对键的删除操作。
  • 惰性删除:放任键过期不管,但是每次从键空间获取键时,都会检查该键是否过期,如果过期的话,就删除该键。
  • 定期删除:每隔一段时间,程序都要对数据库进行一次检查,删除里面的过期键,至于要删除多少过期键,由算法而定。

1、定时删除

设置键的过期时间,创建定时器,一旦过期时间来临,就立即对键进行操作。

这种对内存是友好的,但是对 CPU 的时间是最不友好的,特别是在业务繁忙,过期键很多的时候,删除过期键这个操作就会占据很大一部分 CPU 的时间。

要知道 Redis 是单线程操作,在内存不紧张而 CPU 紧张的时候,将 CPU 的时间浪费在与业务无关的删除过期键上面,会对 Redis 的服务器的响应时间和吞吐量造成影响。

另外,创建一个定时器需要用到 Redis 服务器中的时间事件,而当前时间事件的实现方式是无序链表,时间复杂度为 O(n),让服务器大量创建定时器去实现定时删除策略,会产生较大的性能影响,所以,定时删除并不是一种好的删除策略。

2、惰性删除

与定时删除相反,惰性删除策略对 CPU 来说是最友好的,程序只有在取出键的时候才会进行检查,是一种被动的过程。

与此同时,惰性删除对内存来说又是最不友好的,一个键过期,只要不再被取出,这个过期键就不会被删除,它占用的内存也不会被释放。

很明显,惰性删除也不是一个很好的策略,Redis 是非常依赖内存和较好内存的,如果一些长期键长期没有被访问,就会造成大量的内存垃圾,甚至会操成内存的泄漏。

在对执行数据写入时,通过 expireIfNeeded 函数对写入的 Key 进行过期判断。

其中 expireIfNeeded 在内部做了三件事情,分别是:

  • 查看 Key 是否过期。
  • 向 Slave 节点传播执行过去 Key 的动作。
  • 删除过期 Key。

3、定期删除

上面两种删除策略,无论是定时删除和惰性删除,这两种删除方式在单一的使用上都存在明显的缺陷,要么占用太多 CPU 时间,要么浪费太多内存。

定期删除策略是前两种策略的一个整合和折中:

  • 定期删除策略每隔一段时间执行一次删除过期键操作,并通过限制删除操作执行的时间和频率来减少删除操作对 CPU 时间的影响。
  • 通过合理的删除执行的时长和频率,来达到合理的删除过期键。