redis中key的过期策略
- [1. 定时器的实现原理](#1. 定时器的实现原理)
-
- [1.1 基于优先级队列/堆](#1.1 基于优先级队列/堆)
- [1.2 基于时间轮实现的定时器](#1.2 基于时间轮实现的定时器)
- [2. Redis中key的过期策略](#2. Redis中key的过期策略)
-
- [2.1 定期删除](#2.1 定期删除)
- [2.2 惰性删除](#2.2 惰性删除)
更多Redis相关内容,请关注 Redis专栏。
提到过期策略,我们的第一反应可能是使用定时器(在某个时间到达之后,执行指定的任务)。
1. 定时器的实现原理
1.1 基于优先级队列/堆
优先级队列是按照指定优先级,优先级高的先出,优先级低的后出。
这个优先级,是自定义的。在redis过期key的场景中,可以将优先级定义为"过期时间越早,优先级越高"。我们就可以把这些设置了过期时间的key加入到一个优先级队列中,并且按照上面的定义设置优先级。
那么,毫无疑问的,这个优先级队列的队首元素就是过期时间最早的,此时可以给这个优先级队列分配一个线程,让这个线程检查队首元素是否过期。
但是该线程不能检查的太频繁,否则CPU一直空转,会消耗资源。
为了解决这个问题,我们可以给该线程根据队首元素的过期时间,给线程设置一个等待,时间到了,就唤醒该线程。如果在线程休眠的时刻,来了一个过期时间比队首元素更早的key,只需要在添加新任务的时候,唤醒一下该线程,重新检查队首元素,再根据过期时间重新调整阻塞时间即可。
1.2 基于时间轮实现的定时器
把时间划分成很多个小段,具体要分成多少个小段,一个小段多长时间,需要根据实际的业务需求来进行设定。

在每个小段上都挂着一个链表,链表中的每个节点都代表一个要执行的任务。指针会每个固定的时间间隔(每个小格子的时间)移动,每走到一个新的各自上就会尝试将链表上的任务执行一下(有可能任务还没有到达过期时间,就会执行不成功)。
假设现在来了一个过期时间为200ms的key,就会放到指针当前指向的小格子后的第二个格子里面。
但是Redis并没有采取上述的方案。
2. Redis中key的过期策略
2.1 定期删除
如果直接遍历所有的key,肯定是行不通的。因为redis是单线程的程序,主要的任务就是处理每个命令,如果key的数量太大,就会消耗太多时间,导致正常处理命令被阻塞住了。
所以redis采用每次抽取一部分,来验证时候到达过期时间,来保证这个抽取检查的过程足够快。
2.2 惰性删除
惰性删除就比较符合懒人的心理了,用我妈的话来说就是"早了不动,晚了挠腚"🤦♀️。
我们假设key已经到了过期时间了,但是并不会删除它,它还存在,知道一次访问,正好用到了这个已经过期了的key,此时就会让redis服务器出发删除该key的操作,同时给客户端返回一个nil。
redis采取的上述两种策略的结合,但是整体的效果一般,仍然会有很多过期的key的删除不及时。
redis为此还提供了一系列的内存淘汰机制。参考 【Redis】-- 缓存 这篇博客。