
Redis 是可以对 key 设置过期时间的,因此需要有相应的机制将已过期的键值对删除,而做这个工作的就是过期键值删除策略。
每当我们对一个 key 设置了过期时间时,Redis 会把该 key 带上过期时间存储到一个过期字典(expires dict)中,也就是说「过期字典」保存了数据库中所有 key 的过期时间。
过期字典的key是指向某个键对象的指针,value是保存key的过期时间。
字典实际上是一个哈希表,能够在O1下查找。当我们查询一个 key 时,Redis 首先检查该 key 是否存在于过期字典中:
常见的三种过期删除策略:
定时删除策略的做法是,在设置 key 的过期时间时,同时创建一个定时事件,当时间到达时,由事件处理器自动执行 key 的删除操作。
定时删除策略的优点:
定时删除策略的缺点:
惰性删除策略的做法是,不主动删除过期键,每次从数据库访问 key 时,都检测 key 是否过期,如果过期则删除该 key。
惰性删除策略的优点:
惰性删除策略的缺点:
每隔一段时间「随机」从数据库中取出一定数量的 key 进行检查,并删除其中的过期key。
定期删除策略的优点:
定期删除策略的缺点:
Redis 选择「惰性删除+定期删除」这两种策略配和使用,以求在合理使用 CPU 时间和避免内存浪费之间取得平衡。
Redis 在访问或者修改 key 之前,都会调用 expireIfNeeded 函数对其进行检查,检查 key 是否过期:
lazyfree_lazy_expire
参数配置决定(Redis 4.0版本开始提供参数),然后返回 null 客户端;默认每秒进行 10 次过期检查一次数据库,每次抽查20个(写死)。
Redis 为了保证定期删除不会出现循环过度,导致线程卡死现象,为此增加了定期删除循环流程的时间上限,默认不会超过 25ms。
Redis 的运行内存已经超过 Redis 设置的最大内存之后,则会使用内存淘汰策略删除符合条件的 key,以此来保障 Redis 高效的运行。
Redis是基于内存存储的,在64位机器默认不会限制内存的使用,在 32 位操作系统中,maxmemory 的默认值是 3G。
实际中通过maxmemory
来设置,超过这个配置值,会触发Redis内存淘汰。
当内存不足时,会腾出部分内存给新数据,因此Redis支持多种淘汰策略:
不进行数据淘汰的策略,默认是noeviction,如果内存达到了maxmemory,写入操作会失败,但是不会淘汰已有的数据。
针对「进行数据淘汰」这一类策略,又可以细分为「在设置了过期时间的数据中进行淘汰」和「在所有数据范围内进行淘汰」这两类策略。
在设置了过期时间的数据中进行淘汰:
在所有数据范围内进行淘汰:
实际当中根据业务选择淘汰算法:
淘汰时机:
每次运行读写命令时,都会调用processCommand函数,其又会调用freeMemoryIfNeed,这时候会尝试去释放一定的内存,根据上述的策略。
LRU尝试回收最长时间未使用的数据。因此会记录每个Key的最近访问时间,维护一个访问时间的数据。
redisObject对象中lru字段存储的是key被访问时Redis的时钟server.lruclock,当key被访问时,更新lru字段。
在redis当中缓存了Unix操作系统时钟,避免了每次调用函数获取。
近似LRU算法,采用随机采样的方法来淘汰元素,但内存不足时,执行一次近似LRU算法。
具体步骤是随机采样n个key,这个采样个数默认为5,然后根据时间戳淘汰掉最旧的那个key,如果淘汰后内存还是不足,就继续随机采样来淘汰。
采样范围:
淘汰池优化:
近似LRU算法不能保证每次随机采样得到的结果,是全局真正最久未访问的,尤其在数据量大的情况下。
准备一个大小为16的候选池,池中根据访问时间进行排序。
这样能保证在不断加活性低的加入池中,活性高的移除。
- 如果池子没满,不管活性大小,都会继续填充,没有位置了就挤走一个
- 池子当中始终保持有序的
- 最后淘汰的时候,固定从池子里淘汰一个活性最小的
- 内存占用还超,继续重复操作。
6.2版本之后加了渐进式驱逐,超过一定时间会返回。
优先淘汰使用频率最低的数据。
使用LFU时,实际当中是复用了redisObject当中的lru字段。将其24位拆解为高16bit存储上一次访问的时间戳,低8bit存储访问次数。
这里访问时间精度是1分钟。如果上一次访问时间很久,访问次数就会衰减。
默认情况下新写的key的后8位计数器的值位5,防止因为访问频率过低被直接删除。
这里r值是随机的,p值得设定决定了访问得次数增加的难度。
p的大小取决于当前访问次数和宏定义 LFU_INIT_VAL 的差值 baseval,另一个是 redis.conf 文件中定义的配置项 lfu-log-factor。
为什么使用一定概率增加:
- 避免热点数据占据过多的缓存空间,而其他非热门的数据很快淘汰出缓存。通过使用一定概率增加访问次数,可以让其他不那么热门但仍然有访问可能性的数据项有机会留在缓存中,提高整体缓存的命中率。
- 平衡不同数据项的访问次数,为不同的数据项提供更公平的缓存替换机会。
- 避免过度关注最近访问的数据项,可以更好地综合考虑历史访问情况和最近访问的权衡,更准确地反映数据项的访问频率。