设为首页 加入收藏

TOP

Redis中的LFU算法
2019-08-04 00:11:39 】 浏览:40
Tags:Redis LFU 算法

会将数据D误认为将来最有可能被访问到的数据。


Redis作者曾想改进LRU算法,但发现RedisLRU算法受制于随机采样数maxmemory_samples,在maxmemory_samples等于10的情况下已经很接近于理想的LRU算法性能,也就是说,LRU算法本身已经很难再进一步了。


于是,将思路回到原点,淘汰算法的本意是保留那些将来最有可能被再次访问的数据,而LRU算法只是预测最近被访问的数据将来最有可能被访问到。我们可以转变思路,采用一种LFU(Least Frequently Used)算法,也就是最频繁被访问的数据将来最有可能被访问到。在上面的情况中,根据访问频繁情况,可以确定保留优先级:B>A>C=D。


LFU算法中,可以为每个key维护一个计数器。每次key被访问的时候,计数器增大。计数器越大,可以约等于访问越频繁。


上述简单算法存在两个问题:


第一个问题很好解决,可以借鉴LRU实现的经验,维护一个待淘汰key的pool。第二个问题的解决办法是,记录key最后一个被访问的时间,然后随着时间推移,降低计数器。


Redis对象的结构如下:


LRU算法中,24 bits的lru是用来记录LRU time的,在LFU中也可以使用这个字段,不过是分成16 bits与8 bits使用:


高16 bits用来记录最近一次计数器降低的时间ldt,单位是分钟,低8 bits记录计数器数值counter


Redis4.0之后为maxmemory_policy淘汰策略添加了两个LFU模式:


还有2个配置可以调整LFU算法:


lfu-log-factor可以调整计数器counter的增长速度,lfu-log-factor越大,counter增长的越慢。


lfu-decay-time是一个以分钟为单位的数值,可以调整counter的减少速度


lookupKey中:


当采用LFU策略时,updateLFU更新lru


首先,LFUDecrAndReturncounter进行减少操作:


函数首先取得高16 bits的最近降低时间ldt与低8 bits的计数器counter,然后根据配置的lfu_decay_time计算应该降低多少。


LFUTimeElapsed用来计算当前时间与ldt的差值:


具体是当前时间转化成分钟数后取低16 bits,然后计算与ldt的差值now-ldt。当ldt > now时,默认为过了一个周期(16 bits,最大65535),取值65535-ldt+now


然后用差值与配置lfu_decay_time相除,LFUTimeElapsed(ldt) / server.lfu_decay_time,已过去n个lfu_decay_time,则将counter减少n,counter - num_periods


增长函数LFULogIncr如下:


counter并不是简单的访问一次就+1,而是采用了一个0-1之间的p因子控制增长。counter最大值为255。取一个0-1之间的随机数r与p比较,当r<p时,才增加counter,这和比特币中控制产出的策略类似。p取决于当前counter值与lfu_log_factor因子,counter值与lfu_log_factor因子越大,p越小,r<p的概率也越小,counter增长的概率也就越小。增长情况如下:


可见counter增长与访问次数呈现对数增长的趋势,随着访问次数越来越大,counter增长的越来越慢。


另外一个问题是,当创建新对象的时候,对象的counter如果为0,很容易就会被淘汰掉,还需要为新生key设置一个初始countercreateObject:


counter会被初始化为LFU_INIT_VAL,默认5。


pool算法就与LRU算法一致了:


计算idle时有所不同:


使用了255-LFUDecrAndReturn(o)当做排序的依据。


】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇Redis中的LRU淘汰策略分析 下一篇Python Web开发中的WSGI协议简介

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目