16 | LFU算法和其他算法相比有优势吗?
LFU 算法的基本原理
- 深入了解
- 翻译
- 解释
- 总结
Redis 4.0引入了LFU算法作为一种缓存淘汰策略,与LRU算法相比,LFU算法根据数据的访问频率来选择被淘汰的数据。LFU算法记录每个数据的访问次数,并根据访问频率来淘汰数据,而不仅仅是记录访问次数。通过设置maxmemory和maxmemory-policy来启用LFU算法,其中maxmemory表示Redis使用的最大内存容量,而maxmemory-policy可以设置为allkeys-lfu或volatile-lfu,表示淘汰的键值对会分别从所有键值对或是设置了过期时间的键值对中筛选。LFU算法的实现包括键值对访问频率记录、初始化和更新,以及LFU算法淘汰数据。LFU算法的引入为Redis缓存设置淘汰策略提供了更多选择,能够更好地满足实际场景的需求。Redis在更新键值对访问频率时,先对访问次数进行衰减,然后按照一定概率增加访问次数,同时获取最新访问时间戳并更新到lru变量中。LFU算法的实现为Redis缓存设置淘汰策略提供了更多选择,能够更好地满足实际场景的需求。 在实现使用LFU算法淘汰数据时,Redis采用了和实现近似LRU算法相同的方法。Redis会使用一个全局数组EvictionPoolLRU,来保存待淘汰候选键值对集合。然后,在处理每个命令时,会调用freeMemoryIfNeededAndSafe函数和freeMemoryIfNeeded函数,来执行具体的数据淘汰流程。LFU算法在淘汰数据时,使用了不同的方法来计算每个待淘汰键值对的空闲时间。具体来说,LFU算法是用255减去键值对的访问次数来计算EvictionPoolLRU数组中每个元素的idle变量值的。LFU算法在待淘汰键值对集合中,是按照键值对的访问频率大小来排序和选择淘汰数据的,这也符合LFU算法本身的要求。LFU算法相比其他算法更容易把低频访问的冷数据尽早淘汰掉,适用于这种场景。 如果LFU_INIT_VAL设置为1,LFU算法会导致访问次数很容易就达到最大值,无法区分不同的访问频率,从而影响LFU算法的淘汰效果。 总的来说,LFU算法的引入为Redis缓存设置淘汰策略提供了更多选择,能够更好地满足实际场景的需求,而LFU算法的实现细节也为其他软件模块的设计提供了参考。
《Redis 源码剖析与实战》,新⼈⾸单¥59
全部留言(8)
- 最新
- 精选
- Kaito1、LFU 是在 Redis 4.0 新增的淘汰策略,它涉及的巧妙之处在于,其复用了 redisObject 结构的 lru 字段,把这个字段「一分为二」,保存最后访问时间和访问次数 2、key 的访问次数不能只增不减,它需要根据时间间隔来做衰减,才能达到 LFU 的目的 3、每次在访问一个 key 时,会「懒惰」更新这个 key 的访问次数:先衰减访问次数,再更新访问次数 3、衰减访问次数,会根据时间间隔计算,间隔时间越久,衰减越厉害 4、因为 redisObject lru 字段宽度限制,这个访问次数是有上限的(8 bit 最大值 255),所以递增访问次数时,会根据「当前」访问次数和「概率」的方式做递增,访问次数越大,递增因子越大,递增概率越低 5、Redis 实现的 LFU 算法也是「近似」LFU,是在性能和内存方面平衡的结果 课后题:LFU 算法在初始化键值对的访问次数时,会将访问次数设置为 LFU_INIT_VAL,默认值是 5 次。如果 LFU_INIT_VAL 设置为 1,会发生什么情况? 如果开启了 LFU,那在写入一个新 key 时,需要初始化访问时间、访问次数(createObject 函数),如果访问次数初始值太小,那这些新 key 的访问次数,很有可能在短时间内就被「衰减」为 0,那就会面临马上被淘汰的风险。 新 key 初始访问次数 LFU_INIT_VAL = 5,就是为了避免一个 key 在创建后,不会面临被立即淘汰的情况发生。2021-08-3136
- 曾轼麟回答老师的问题: LFU_INIT_VAL的初始值为5主要是避免,刚刚创建的对象被立马淘汰,而需要经历一个衰减的过程后才会被淘汰。 LFU算法和LRU算法的不同就是,存粹的LFU算法会累计历史的访问次数,然而在高QPS的情况下可能会出现以下几个问题: 1、运行横跨高峰期和低峰期,不同时期存储的数据不一致,可能会导致部分高峰期产生的数据不容易被淘汰,甚至可能永远淘汰不掉(因为在高峰获得一个较高的count值,在计算淘汰的时候仍然存在) 2、需要long乃至更大的值去存储count。对于高频访问的数据如果需要统计每一次的调用,可能需要使用更大的空间去存储,还需要考虑溢出的问题。 3、 可能存在,每次淘汰掉的几乎是刚刚创建的新数据。 为了解决这些问题,Redis实现了一个近似LFU算法,并做出了以下改进: 1、count有上限值255。(避免高频数据获得一个较大的count值,还能节省空间) 2、count值是会随着时间衰减。(不再访问的数据更加容易被淘汰,高16位记录上一次访问时间戳-分钟,低8位记录count) 3、刚刚创建的数据count值不为0。(避免刚刚创建的数据被淘汰) 4、count值累加是概率随机的。(避免高峰期数据都能一下就能累加到255,其中概率能人为调整)2021-09-0710
- 可怜大灰狼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; } 通过源码可以发现:如果LFU_INIT_VAL太小,会导致baseval变大,从而导致p变小,导致counter加1比较困难。结果就是很容易导致刚set进去的数据,很快就会被淘汰。2021-08-316
- 风轻扬回答一下课后问题。如果LFU_INIT_VAL设置为1。会有两方面影响 1、数据访问次数的增加概率会变大,导致很多数据都会达到255这个值,最终导致不容易淘汰数据 2、新创建出来的数据,访问频率过小。很容易刚刚创建就被淘汰2023-11-14归属地:北京
- 飞鱼提问:上一节中,哪里有说在实际淘汰数据的时候更新 redisObject对象中的 lru变量的值,只看到了 创建 和 访问更新 这2种情况会更新 lru变量值。2022-09-27归属地:北京
- leetcoderedis6.0以后server.c文件中都没有lookupKey函数了呀2022-01-111
- 或许第一张图里面,lookupKey函数是在db.c里面,老师这里是不是有问题啊?2021-11-30
- Geek_197c21如果 LFU_INIT_VAL 设置为 1,那么容易一个key刚刚被set进去就被删除。 麻烦问下老师,如果lfu算法要替换成lru算法的话,那么怎么处理呢?将key都对 255-次数呢?或者啥都不管,继续运行呢2021-08-312