问题1:Redis是如何知道一个key是否过期呢?
????????Redis会利用两个字典分别记录key-value对(dict)以及key-ttl对(expires)。
????????在设置键的过期时间时,会创建一个回调事件,当过期时间达到时, 自动执行回调事件去删除键。但是立即删除对 cpu 是最不友好的。
????????惰性删除是指某个键值过期后,此键值不会马上被删除,而是等到下次访问该键值的时候,等到去expires中去查询才会发现过期了,此时才会删除。但是如果过期了,后面不在访问的话,就会一直在expires中,所以惰性删除的缺点就是会浪费内存。
????????由上面的两幅图我们发现其实在执行写操作和读操作时,都会先去expires字典中检查key是否过期。 ?
通过一个定时任务,周期性的抽样部分的key,然后对过期的key执行删除。执行周期有两种:
1.Redis初始化时会设置一个定时任务serverCron(),按照server.hz的频率来执行过期key清理,模式为SLOW。
SLOW模式规则:
(1)执行频率受到server.hz的影响,默认为10,即每秒执行10次,每一个周期100ms。
(2)执行清理耗时不能超过一个周期时间的25%(即25ms)。
(3)逐个遍历,抽取20个key判断是否过期。
(4)如果没达到时间上限(25ms)并且过期key比列大于10%,则再进行一次抽样,否则结束。
2.Redis的每个事件循环前会调用beforeSleep()函数,执行过期key清理,模式为FAST。
FAST模式规则:
(1)与beforeSleep()(在注册FD监听前执行)调用频率有关,但是两次调用间隔不能低于2ms,如果低了,直接越过。
(2)执行清理耗时不超过1ms。
(3)逐个遍历,抽取20个key判断是否过期。
(4)如果没达到时间上限(1ms)并且过期key比列大于10%,则再进行一次抽样,否则结束。
可以看到,第二种为被动删除,第一种和第三种为主动删除,且第三种实时性更高。每隔一段时间执行一次删除操作,并通过限制删除操作执行的时长和频率,来减少删除操作对 cpu 的影响。另一方面定时删除也有效的减少了因惰性删除带来的内存浪费。
所以redis使用的过期键值删除策略是:惰性删除加上定期删除,两者配合使用。
????????内存淘汰:就是当Redis内存使用达到设置的阈值时,Redis主动挑选部分key删除以释放更多内存的流程。在配置文件中可以进行设置淘汰策略:
Redis支持8种不同策略来选择要删除的key,如下:
noeviction(默认):不淘汰任何key,但是内存满时不允许写入新数据。
volatile-TTL:对设置了TTL的key,比较key的剩余TTL值,TTL越小越先被淘汰。
allkeys-Random:对全体的key,随机进行淘汰。也就是直接从db->dict中随机挑选。
volatile-Random:对设置了TTL的key,随机进行淘汰。也就是从expires中随机挑选。
allkeys-LRU:对全体key,基于LRU算法进行淘汰。
volatile-LRU:对设置了TTL的key,基于LRU算法进行淘汰。
allkeys-LFU:对全体的key,基于LFU算法进行淘汰。
volatile-LFU:对设置了TTL的key,基于LFU算法进行淘汰。
总体的流程如下:
?
手写LRU算法:
力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
手写LFU算法:
力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
?