缓存淘汰策略
![](https://i-blog.csdnimg.cn/blog_migrate/d7f3006b62409428e89ccc7c683a72a3.png)
不进行淘汰的:noeviction
淘汰设置过期时间的:volatile-random、volatile-ttl、volatile-lru、volatile-lfu(4.0后新加的)
volatile-ttl 在筛选时,会针对设置了过期时间的键值对,根据过期时间的先后进行删除,越早过期的越先被删除。
volatile-random 就像它的名称一样,在设置了过期时间的键值对中,进行随机删除。
volatile-lru 会使用 LRU 算法筛选设置了过期时间的键值对。
volatile-lfu 会使用 LFU 算法选择设置了过期时间的键值对。
淘汰所有的:allkeys-lru、allkeys-random、allkeys-lfu(4.0后新加的)
allkeys-random 策略,从所有键值对中随机选择并删除数据;
allkeys-lru 策略,使用 LRU 算法在所有数据中进行筛选。
allkeys-lfu 策略,使用 LFU 算法在所有数据中进行筛选。
LRU算法
按照最近最少使用的原则来筛选数据,最不常用的数据会被筛选出来,而最近频繁使用的数据会留在缓存中。
LRU 会把所有的数据组织成一个链表,链表的头和尾分别表示 MRU 端和 LRU 端,分别代表最近最常使用的数据和最近最不常用的数据。
![](https://i-blog.csdnimg.cn/blog_migrate/3ba6f66adab322a4b9f17a5e0d7a282b.png)
LRU 算法选择删除数据时,都是从 LRU 端开始,所以把刚刚被访问的数据移到 MRU 端,就可以让它们尽可能地留在缓存中
但是lru算法是链表结果的,会带来大量的空间移动会消耗大量资源。
Redis LRU算法淘汰原理
Redis会通过配置的参数取值N(CONFIG SET maxmemory-samples 100),在这里就是100个。
Redis 默认会记录每个数据的最近一次访问的时间戳(由键值对数据结构 RedisObject 中的 lru 字段记录)。Redis 在决定淘汰的数据时,第一次会随机选出 N 个数据,把它们作为一个候选集合。接下来,Redis 会比较这 N 个数据的 lru 字段,把 lru 字段值最小的数据从缓存中淘汰出去。
当需要再次淘汰数据时,Redis 需要挑选数据进入第一次淘汰时创建的候选集合。这儿的挑选标准是:**能进入候选集合的数据的 lru 字段值必须小于候选集合中最小的 lru 值。**当有新数据进入候选数据集后,如果候选数据集中的数据个数达到了 maxmemory-samples,Redis 就把候选数据集中 lru 字段值最小的数据淘汰出去。
LFU内存淘汰
LFU算法是在LRU算法基础上进行了优化,正常LRU算法是根据Redisobject对象中的lru字段时间戳来判断的,但是他有一定的局限性,数据刚进来就被淘汰或者不常用的数据一直不被淘汰。所以我们引出了LFU:
LFU将lru字段24字节转成了lru字段(16字节)+counter字段(8字节),lru字段记录时间戳,counter记录被访问的次数。根据访问次数进行淘汰,当访问次数相同时再根据lru时间戳进行淘汰。因为counter字段只有8字节,那么他的最大值是255,这样我们访问超出限制的也会出现问题,所以我们增加对counter字段复制的算法:当数据被访问一次,取他的counter值*lfu_log_factor(默认是为5,LFU_INIT_VAL) 加1,取他的倒数p(100的倒数100分之一),在(0,1)区间中取任意数值r,当数值p大于r的时候counter+1.这样的话counter就不那么容易超限。下面是lfu_log_factor取值之后的counter变化趋势:
![](https://i-blog.csdnimg.cn/blog_migrate/cd6bbf4bcb12b06d3d73a022d6010d60.png)
上面这些都是counter增加的情况,那么如果数据被访问上万次之后,不再被访问那么他的counter数值一直很大,我们也需要衰减情况。所以LUF又增加了衰减方案:根据当前时间减去lru字段得到差值,然后转化成N分钟,再除以lfu_decay_time,得到数值A,counter-A。
将lfu_decay_time设置为1,那么就是过去N分钟,衰减N。