Redis高可用高性能缓存的应用系列的第3篇,主要介绍Redis缓存过期淘汰策略的知识点。
Redis设置key时,都会设置一个过期时间,那么当过期时间到了都是怎么处理的?
Redis同时使用了惰性过期和定期过期两种方式的缓存淘汰策略。
Redis默认每秒进行10次过期扫描:
1.从过期字典中随机选择20个key
2.删除这20个key中已过期的
3.如果超过25%的key过期,则重复第一步
同时,为了保证业务不受影响,Redis还设置了扫描的时间上限,默认不会超过25ms
1.假如内存不足时,Redis会根据设置的淘汰策略,删除一些不常用的数据,保证Redis的正常使用,所有的前提都是加入键的时候如果超过Redis内存设定的限制后,Redis采用的服务。
1.noeviction: 不会在写入,写入会报错。
2.allkeys-lru:首先通过LRU算法驱逐最久没有使用的键
3.volatile-lru:首先从设置了过期时间的键集合中驱逐没有最久使用的键
4.allkeys-random:从所有过期字典中的key随机删除
5.volatile-random:从过期键的集合中随机驱逐
6.volatile-ttl:从配置了过期时间的键中,驱逐马上就要过期的键
7.volatile-lfu:从配置了过期时间的键中驱逐使用频率最少得键
8.allkeys-lfu:从所有键中使用频率最少的键
根据最近被使用的时间,距离当前最远的数据优化被淘汰,当有新增key 或者是被访问时,元素会被追加在队尾,需要淘汰时从头部开始淘汰,这个是LRU的思想。
在Redis redisObject 中,维护了一个24位的时钟(有点类似于Cpu的频率),可以简单理解为Cpu对内存使用的时间戳,每个Key对应的也维护了同样24位的时间戳。
比如现在要进行LRU,首先拿到当前系统的时间钟,和Redis redisObject 内存的LRU时间钟对差值计算,差最大的进行淘汰,这里需要注意的是,全局时钟只有24位,按秒计算的话,最多可以存储194天,所以可能出现key时钟大于全局时钟的情况,但是Redis的LRU不会对全局的时钟进行比对,他会从设置了过期时间的key中进行比对。
struct redisObject {
unsigned lru:LRU_BITS; /* LRU time (relative to global lru_clock) or
* LFU data (least significant 8 bits frequency
* and most significant 16 bits access time). */
};
LRU只考虑了使用的时间,但是没有考虑Key使用的次数,Redis4.0 以后,新增了LFU的淘汰策略,根据使用时间和次数最为淘汰的权重。
LFU把之前LRU的24bit拆分成两部分,16bit的时间钟和8it的访问频率,8bit比较小,在源码的evict文件中给出了数据。
uint8_t LFULogIncr(uint8_t counter) {
if (counter == 255) return 255;
double r = (double)rand()/RAND_MAX;
double baseval = counter - LFU_INIT_VAL;
if (baseval < 0) baseval = 0;
double p = 1.0/(baseval*server.lfu_log_factor+1);
if (r < p) counter++;
return counter;
}