过期键删除策略

一般一个键过期了,有以下三种不同的策略可以进行删除:

(1)定时删除:在设置键的过期时间的同时,创建一个定时器(timer),让定时器在键的过期时间来临时,立即执行对键的删除操作。

定时删除策略对内存是最友好的:通过使用定时器,定时删除策略可以保证过期键会尽可能快地被删除,并释放过期键所占的内存

缺点:对CPU时间最不友好,在过期键较多的情况下,删除过期键这一行为可能会占用相当一部分CPU,在内存不紧张但是CPU时间非常紧张的情况下,将对CPU时间用在删除和当前任务无关的过期键上,无疑会对服务器的响应时间和吞吐量造成影响,另外创建一个定时器需要用到Redis服务器中的时间时间,而当前时间时间的实现方式为无需链表,查找一个时间的时间复杂度为O(N),并不能高效地处理大量时间事件,因此让Redis创建大量的定时器来实现定时删除策略,现阶段并不现实

(2)惰性删除:放任键过期不管,但是每次从键空间中获取键时,都检查取得的键是否过期,如果过期的话,就删除键;如果没有过期,就返回该键

惰性删除策略对CPU来说是最友好的,程序只会在取出键时才对键进行过期检查,可以保证删除过期键的操作只会在非做不可得情况下进行,并且删除得目标仅限于当前处得键,这个策略不会在删除其他无关得过期键上花费任何CPU事件

缺点:对内存不友好,如果一个键已经过期,而这个键又仍保留在数据库中,那么只要这个过期键不被删除,它所占用得内存就不会释放

在使用惰性删除策略时,如果数据库中存在非常多得过期键,而这些过期键又恰好没有被查询,那么它们也许会永远不会被删除(除非用户手动执行FLUSHDB)并占用内存,我们可以把这种情况看作是一种内存泄漏(无用得垃圾数据占用了大量的内存),而服务器却不能自己去释放它们,这对于运行状态非常依赖于内存的Redis服务器来说(注:Redis是内存型数据库),肯定不是一个好消息

(3)定期删除:每隔一段时间,程序就对数据库进行一次检查,删除里面的过期键。至于删除多少过期键,以及检查多少个数据库,由算法实现

定期删除和惰性删除都存在明显的缺陷,定期删除策略是对前两种策略的一种整合和折中,定期删除策略每隔一段时间执行一次删除过期键操作,并通过限制删除操作执行的时长和频率来减少删除操作对CPU的影响,通过定期删除过期键,定期删除策略可以有效地减少了因为过期键而带来的内存浪费

定期删除策略的难点在于确定删除操作执行的时长和频率:

  • 如果删除操作执行过于频繁,或者执行时间过长,定期删除策略就会退化为定时删除策略,以至于CPU时间过多地消耗在删除过期键上面
  • 如果删除操作执行得太少,或者执行时间太多,定期删除策略又会和惰性删除策略一样,出现内存浪费得情况

因此,如果采用定期删除策略,服务器必须根据实际情况,合理地设置删除操作得执行时长和执行频率

在Redis中,键过期删除策略采用的键删除策略是惰性删除和定期删除

Redis中惰性删除实现

过期键的惰性删除策略由db.c/expireIfNeeded函数实现,所有读写数据库的Redis命令在执行之前都会调用expireIfNeeded函数对输入键进行检查

  • 如果输入见已经过期,那么expireIfNeeded函数将输入键从数据库中删除。
  • 如果输入键未过期,那么expireIfNeeded函数不做动作

expireIfNeeded函数类似于一个过滤器,它可以在命令真正执行之前,过滤掉过期的输入键,从而避免命令接触到过期键

redis 惰性删除 主动删除_Redis

Redis定期删除策略

过期键的定期删除策略由redis.c/activeExpireCycle函数实现,每当Redis的服务器周期性操作redis.c/serverCron函数时执行时,activeExpireCycle函数被调用,它在规定的时间内,分多次遍历服务器中的各个数据库,从数据库的expires字典中随机检查一部分键的过期时间,并删除其中的过期键。

简单理解:Redis内部维护一个定时任务,默认每秒运行10次(通过配置hz控制)。定时任务中删除过期键逻辑采用了自适应算法,根据键的过期比例,使用快慢两种速率模式回收键。

比如:

  1. 定时任务在每个数据库空间随机检查20个键,当发现过期时删除对应的键。
  2. 如果超过检查数25%的键过期,循环执行回收逻辑直到不足25%或运行超时为止,慢模式下超时时间为25ms。
  3. 如果之前回收键逻辑超时,则在Redis触发内部事件之前再次以快模式运行回收过期键任务,快模式下超时时间为1ms且2s内只能运行1次。
  4. 快慢两种模式内部删除逻辑相同,只是执行的超时时间不同。

Redis内存溢出淘汰控制策略

当然Redis中并不是所有键都会设置过期时间,那这些键会一直的保存在内存中?内存不会炸?

因此,当Redis所用内存达到maxmemory上限时会触发相应的溢出控制策略。具体策略受maxmemory-policy参数控制,Redis支持6种策略,如下所示:

  • noeviction:默认策略,当内存不足以容纳新写入数据时,新写入操作会报错。应该没人用吧。
  • allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的 Key。推荐使用,目前项目在用这种。
  • allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个 Key。应该也没人用吧,你不删最少使用Key,去随机删。
  • volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的 Key。这种情况一般是把 Redis 既当缓存,又做持久化存储的时候才用。不推荐。
  • volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个 Key。依然不推荐。
  • volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的 Key 优先移除。不推荐。如果没有对应的键,则回退到noeviction策略。