目录

Redis数据淘汰策略 概论 (一)Redis数据淘汰策略 LRU深入分析 (二)Redis数据淘汰策略 LFU深入分析 (三)

1.前言

上篇文章我们整体上介绍了redis的淘汰策略,而其中LRU和LFU两种算法又是极其重要的,接下来我们从底层原理开始逐一揭开它们的神秘面纱!

2. LRU

2.1 LRU

LRU(least recently used),从字面上理解是最近最少使用的,会淘汰掉最近最不曾使用的key;
有兴趣可查看redis官方文档关于这块的介绍,点击查看

实现LRU算法除了需要key/value字典外,还需要附加一个链表,聊表中元素按照一定顺序排列,当空间满的时候就剔除尾部的元素,当字典的某个元素被访问时,他在链表中的位置会被移动到表头,所以链表的元素排列顺序就是元素最近被访问的时间顺序

CSRedis 策略 redis的lru策略_redis


分析:

  • 优点:链表元素的排列顺序就是元素最近被访问的时间顺序,清晰明了
  • 缺点:每次需要把整个链表进行很大的变动,会消耗大量的额外内存;同时,加入有一个元素很久没有被删除,使用频率很低,最近使用一次,那么这个使用频率很低的元素也不会被淘汰掉,这也是后续出现更好的lfu算法的原因;

2.2 近似LRU

redis实际上采用的是近似LRU算法,从我们刚提到的LRU的缺点就能理解这一点;

CSRedis 策略 redis的lru策略_redis_02

如下图是随机LRU算法和严格LRU算法的效果对比图

CSRedis 策略 redis的lru策略_字段_03


图中绿色部分是新加的key,深灰色部分是老旧的key,浅灰色部分是通过LRU算法淘汰掉的key;从图中可以看出采样数量越大,近似LRU算法的效果越接近严格LRU算法,同时Redis3.0在算法中增加了淘汰池,进一步提升了近似LRU算法的效果;

拓展: 淘汰池其实是一个数组,它的大小是maxmemory_samples,在每次淘汰循环中,新的随机得到的key列表会和淘汰池中的key列表进行一次融合,即求并集后淘汰最旧的key,保留剩余较旧的key列表放入淘汰池中留待下一个循环。

2.3 redis请求头与 LRU

redis为了实现近似LRU,给每个key增加了一个额外的小字段,长度为24bit(默认存的是Unix时间戳对2^24取模的结果,大约97天清零一次),当某个key被访问一次,它的对象头结构中lru字段就会被更新为server.lruclock;
如果server。lruclock没有折返(对2^24取模),它就是一直递增的,意味着对象的lru字段不会超过server.lruclock的值,如果超过了,说明折返了,通过这个逻辑就可以计算出对象多长时间没有被访问-----即“对象的空闲时间”;

空闲时间计算如下

CSRedis 策略 redis的lru策略_redis_04

redis对象头结构如下

struct RedisObject{
	int4 type;
	int4 encoding;
	int24 lru;
	int32 refcount;
	void *ptr;
} robj ;

3.总结

redis实际上使用的是近似LRU算法

使用频率很低的元素如果最近被使用过一次,在很长的一段时间也不会被淘汰掉,这也是LFU算法出现的原因

4.思考

如果你是java用户,如何使用LinkedHashMap实现一个LRU字典?

最后,如果看完对你有所帮助,不要吝啬关注 / 点赞 / 收藏哟,感谢感谢~