redis的LRU算法(二)

前文再续,书接上一回。上次讲到redis的LRU算法,文章实在精妙,最近可能有机会用到其中的技巧,顺便将下半部翻译出来,实现的时候参考下。

搏击俱乐部的第一法则:用裸眼观测你的算法

Redis2.8的LRU实现已经上线了,在不同的负载环境下经过测试,用户没有抱怨Redis的清理机制。为了继续改进,我希望能观察到算法的性能,同时不会浪费大量CPU,不增加1比特空间占用。

我设计了一个测试用例。导入指定数量的key,然后顺序访问他们,好让他们的最近访问时间顺序递减。再添加50%的key,那么之前的key有50%就会被淘汰掉。理想情况下,被淘汰的应该是前50%的。如下图:

绿色的是新添加的key,灰色的是第一次添加的key,白色表示被移除的。

LRU v2:不要丢掉重要信息

当采用了N-key取样时,默认会建立16个key的池,将里面的key按空闲时间排序。新key只会在池不满或者空闲时间大于池里最小的,才能进池。

这个实现极大的提升了性能,实现又简单,没有大bug。只要一点点性能监控和一些memmove()就完成了。

同时,新的redis-cli模式(-lru-test)支持测试LRU精度,可以更接近真实的负载来看新算法的工况,尝试不同算法的时候,至少可以发现明显的速度退化。

最近最少访问(LFU)

我最近又部分重构了Redis cache的换页算法。这些工作源于一个issue:当你在Redis 3.2有多个数据库的时候,算法总是做局部选择。比如DB 0的所有key都用的很频繁,DB 1的所有key都用的很少。Redis会从每个DB里丢弃一个key。理性的选择应该是先丢弃DB 1的key,丢完以后再丢DB 0的

Redis用作cache的时候,通常不会跟不同DB混合用,但我还是开始着手改进,最后将db的id包括在池里,然后所有DB都共用一个池,这个实现比原始先快20%

这次改进激起了我对Redis这块子系统的好奇心。我花了好些天进行优化,如果我用一个大点的池子,会好点吗?如果选择key的时候考虑了流逝的时间,效果会不会更好?

最后,我终于明白到,LRU算法会受到取样数量限制,只要数量足够,效果就很好,很难再改进。正如上图所示,每次取样10个键,已经和理论上的LRU几乎一样准确了。

因为原始算法难以改进,我开始想其他办法。回顾前文,其实我们真正想要的,是保留未来最有可能访问的key,即是最常访问的key,而不是最新访问的key。这就是LFU算法。理论上LFU的实现很简单,只要给每个key挂一个计数器,我们就可以知道给定的key是不是比另一个key访问更多了。

当然,LFU的实现上有几个通用的难点:

1. LFU里没法使用链表法转移到头部的技巧了。因为完美LFU需要key严格按访问量排序。当访问量一致时,排序算法可能劣化为O(N),即使计数器只变了一点点

2. LFU没法简单的只在访问时对计数器加一。因为访问模式会随着时间发生变化,所以一个高分的key需要随着时间流逝而分数递减。

在Redis里第一个问题不是问题,我们可以沿用LRU的随机取样方法。第二个问题仍然存在,我们需要一个方法来递减分数,或者随着时间流逝将计数器折半。

24bit空间实现的LFU

在Redis里,我们可以用的就是LRU的24bit空间,需要一些奇技淫巧来实现。

在24bit空间里,需要塞下:

1. 某种类型的访问计数器

2. 足够的信息来决定何时折半计数器

我的解决方案如下:

           16 bits      8 bits
      +----------------+--------+
      + Last decr time | LOG_C  |
      +----------------+--------+

8bit用来计数,16bit用来记录上次递减的时间

你可能会认为,8bit计数器很快就会溢出了吧?这就是技巧所在:我用的是对数计数器。具体代码如下:

  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;
  }

计数器的值越大,真正加一的概率越小。上述代码算出一个概率p,介乎0到1之间,计数器越大,p越小。然后生成0-1之间的随机数r,只有r<p的时候,计数器才会加一。

现在我们来看看计数器折半的问题。转成分钟为单位的unix时间,低16位会存在上面保留的16位空间内。当Redis进行随机取样,扫描key空间的时候,所有遇到的key都会被检查是否应该递减。如果上次递减实在N分钟之前(N是可配置的),并且计数器的值是高分值,那计数器就会被折半。如果计数器是低分值,则只会递减。(希望我们可以更好的分辨少访问量的key,因为我们的计数器精度比较低)

还有一个问题,新的key需要一个生存的机会。Redis里新key会从5分开始。上面的递减算法已经考虑到这个分数,如果key分数低于5分,更容易被丢弃(一般是长时间没访问的非活跃key)。

原文地址:https://www.cnblogs.com/Lifehacker/p/redis_lru.html

时间: 2024-10-10 20:20:35

redis的LRU算法(二)的相关文章

redis的LRU算法(一)

最近加班比较累,完全不想写作了.. 刚看到一篇有趣的文章,是redis的作者antirez对redis的LRU算法的回顾.LRU算法是Least Recently Used的意思,将最近最少使用的资源丢掉.Redis经常被用作cache,如果能够将不常用的key移除,尽量保留常用的,那内存的利用率就相当高了.当然,LRU也有弱点,考虑下面一种情况: ~~~~~A~~~~~A~~~~~A~~~~A~~~~~A~~~~~A~~| ~~B~~B~~B~~B~~B~~B~~B~~B~~B~~B~~B~~

Redis的LRU算法

Redis的LRU算法 LRU算法背后的的思想在计算机科学中无处不在,它与程序的"局部性原理"很相似.在生产环境中,虽然有Redis内存使用告警,但是了解一下Redis的缓存使用策略还是很有好处的.下面是生产环境下Redis使用策略:最大可用内存限制为4GB,采用 allkeys-lru 删除策略.所谓删除策略:当redis使用已经达到了最大内存,比如4GB时,如果这时候再往redis里面添加新的Key,那么Redis将选择一个Key删除.那如何选择合适的Key删除呢? CONFIG

探究redis和memcached的 LRU算法--------redis的LRU的实现

一直对这redis和memcached的两个开源缓存系统的LRU算法感兴趣.今天就打算总结一下这两个LRU算法的实现和区别. 首先要知道什么是LRU算法:LRU是Least Recently Used 近期最少使用算法.相关的资料网上一大堆.http://en.wikipedia.org/wiki/Cache_algorithms#LRU   redis的六种策略 rewriteConfigEnumOption(state,"maxmemory-policy",server.maxme

Android探索之图片缓存&lt;Lru算法&gt;(二)

前言: 上篇我们总结了Bitmap的处理,同时对比了各种处理的效率以及对内存占用大小.我们得知一个应用如果使用大量图片就会导致OOM(out of memory),那该如何处理才能近可能的降低oom发生的概率呢?之前我们一直在使用SoftReference软引用,SoftReference是一种现在已经不再推荐使用的方式,因为从 Android 2.3 (API Level 9)开始,垃圾回收器会更倾向于回收持有软引用或弱引用的对象,这让软引用变得不再可靠,所以今天我们来认识一种新的缓存处理算法

LRU算法的Python实现

LRU:least recently used,最近最少使用算法.它的使用场景是:在有限的空间中存储对象时,当空间满时,会一定的原则删除原有的对象,常用的原则(算法)有LRU,FIFO,LFU等.在计算机的Cache硬件,以及主存到虚拟内存的页面置换,还有Redis缓存系统中都用到了该算法.我在一次面试和一个笔试时,都遇到过这个问题. LRU的算法是比较简单的,当对key进行访问时(一般有查询,更新,增加,在get()和set()两个方法中实现即可)时,将该key放到队列的最前端(或最后端)就行

Redis详解(二)

一.redis认证二.redis事务三.Connection&server相关命令四.Redis的发布与订阅//充当mq的功能五.Redis持久化六.redis复制七.master-slave实现八.sentinal哨兵九.redis限制十.redis性能测试        一.redis认证redis认证:    vim /etc/redis.conf         requirepass redispass //密码    [[email protected] ~]# systemctl

redis(7)LRU缓存

一.LRU简介 LRU是Least Recently Used的缩写,即:最近最少使用. 它是内存管理中的一种页面置换算法,对于在内存中但是又不用的数据块,操作系统会根据哪些数据属于LRU而将其移除内存而腾出空间来加载另外的数据. 二.redis LRU 官方文章:https://redis.io/topics/lru-cache#using-redis-as-an-lru-cache redis常常被用作缓存,当有新的数据进来的时候会自动驱逐旧的数据.LRU是memcached 默认的驱逐策略

Redis的LRU机制(转)

原文:Redis的LRU机制 在Redis中,如果设置的maxmemory,那就要配置key的回收机制参数maxmemory-policy,默认volatile-lru,参阅Redis作者的原博客:antirez weblog >> Redis as an LRU cache 原文中写得很清楚: Another way to use Redis as a cache is the maxmemory directive, a feature that allows specifying a m

关于LRU算法(转载)

原文地址: http://flychao88.iteye.com/blog/1977653 http://blog.csdn.net/cjfeii/article/details/47259519 LRU(Least recently used,最近最少使用)算法根据数据的历史访问记录来进行淘汰数据,其核心思想是"如果数据最近被访问过,那么将来被访问的几率也更高". 1.2.实现 最常见的实现是使用一个链表保存缓存数据,详细算法实现如下: 1. 新数据插入到链表头部: 2. 每当缓存命