Redis在内存空间限定一定大小的情况下,Redis具体要缓存什么数据,就相比比较重要了

如果有些数据只被访问一两次,访问完成还继续留在缓存中的话,就会白白的占用缓存空间,这就是缓存污染

对于缓存污染,首先要明确选择的缓存替换策略,然后本章的重点就是针对部分的缓存替换策略的底层实现进行讲解学习

在Redis中,我们有着8种淘汰策略,分别是noeviction,volatile-random,volatile-ttl,volatile-lru,volatile-lfu,allkeys-lru,allkeys-random,allkeys-lfu

首先排除noeviction策略,因为并不会进行数据淘汰的,除此外,都会按照一定的规则来淘汰数据

首先看一下random,这个策略也并不适合,因为会进行随机淘汰

万一淘汰了原本访问次数多,需要缓存的数据呢,那不就发生了缓存缺失

所以并不适合实际生产,然后是ttl相关策略,能否对应缓存污染,ttl针对的是过期时间中达到的数据进行淘汰,因为并不是随机,所以可以应用到实际生产中,但是需要注意,为了避免污染,需要在录入数据的时候设置对应的过期时间

然后是lru和lfu两种过期策略

LRU缓存是经典的传统缓存侧罗,按照这个思想,Redis的LRU策略,会在每个数据对应的RedisObject中构建lru字段,用来记录数据的访问时间戳,数据淘汰的时候,LRU策略会在筛选的时淘汰lru字段值最小的数据,距离上次访问过去最久的key

这种策略的确能够有效的保留访问时间最近的数据,并提升业务应用访问速度

但是对于一些多次的大范围扫描的时候,可能会出现一大批冷数据读入,所以简单的LRU算法并不能解决冷数据读取的问题,后来Redis提供了LFU淘汰策略

LRU从两个维度来淘汰数据,一个是数据访问的时效性,一个是数据的访问次数

LRU为每个数据增加了一个计数器,表示访问次数,使用LFU策略筛选的时候,会首先根据数据次数进行比较,如果数据访问次数相同,再次比较距上次访问时间久的数据

LFU策略在使用的时候,将Redis头中的LRU字段,拆分为了两个部分

ldt值:lru字段的前16bit,表示数据的访问时间戳

counter值,后8bit,表示数据的访问次数

但是8比特实在太小了,最大的记录数也只能是255

如果直接以255作为访问上限,会有一个问题,就是热点key很有可能都达到了255,导致Redis并不好区分具体淘汰哪个数据

所以Redis针对这个8bit,优化了自身的计数算法

LFU的计数实际规则,是每当数据都被访问一次的时候,使用计数器当前的值乘以配置项 lfu_log_factor加1,

然后取其倒数,得到一个p值,讲p值和区直范围在(0,1)的一个随机数比大小,只有p值大于r值得时候,计数器才加1

这样,我们就可以利用设置配置中的lfu_log_factor配置项来控制计数器增加的速度,避免过快的达到255

Redis官网上也提供了,不同的lfu_log_factor配置下,计数器的值如何变化的

图片

lfu_log_factor越大,counter增长越慢

而且为了避免短时间热点数据导致的缓存丢失的话,Redis还设计了一个counter值的衰减机制

lfu_decay_time配置项可以控制访问次数的衰减,LFU策略会计算当前时间和最近一次时间的差值,然后换算为分钟,并处以lfu_decay_time,得到Counter在比较时要减去的值

比如,lfu_decay_time的值为1,如果N分钟没有访问,访问次数就要减去N,如果lfu_decay_time越大,必然衰减值会变小

那么总结一下我们说的缓存污染问题

首先我们根据不同的缓存淘汰策略,分析了不同缓存策略下,缓存污染的存在

最后详细讲解了LRU和LFU的策略实现及其优缺点

最后一个小问题,LFU策略如果使用之后,缓存还会被污染吗?

LFU的使用的关键是取决于fu_log_factor配置项的配置,如果配置过小,可能出现counter达到255,而淘汰失误的.如果配置过大,可能会还处于counter为0的阶段导致淘汰

发表评论

邮箱地址不会被公开。 必填项已用*标注