27 缓存污染(缓存数据的淘汰策略)

缓存污染

定义:有些数据访问次数非常少,甚至只会被访问一次。当这些数据服务完访问请求之后,继续会留在内存中,占用缓存空间。

八种数据淘汰策略:
noeviction、volatile-random、volatile-ttl、volatile-lru、volatile-lfu、allkeys-lru、allkeys-random 和 allkeys-lfu 策略

volatile-random 和 allkeys-random

采用随机挑选数据的方式来筛选即将被淘汰的数据。
Redis不会根据数据的访问情况来筛选数据。如果被淘汰的数据又被访问,就会发生缓存缺失。

volatile-ttl 策略

volatile-ttl 策略针对的是设置了过期时间的数据,把这些数据中剩余存活时间最短的筛选出来并淘汰掉。
但是数据的剩余存活时间并不能直接反映数据再次访问的情况。
如果明确知道数据可以被访问的有效时长,volatile-ttl也可以有效避免缓存污染。

LRU缓存策略

LRU策略的核心:如果一个数据刚刚被访问,那么这个数据大概率还会被再次访问。

Redis中的LRU策略会在每个数据对应的RedisObject结构体中设置一个lru字段,用来记录数据的访问时间戳。在进行数据淘汰时,LRU策略会在候选数据集中淘汰LRU字段最小的数据(也就是访问时间最久的数据)。

因为只看数据的访问时间,Redis使用LRU策略在处理扫描式单次查询操作时,无法解决缓存污染。扫描式单次查询操作指的是应用对大量的数据进行一次全体读取,每个数据都会被读取,而且只会被读取一次。 此时,因为这些被查询的数据刚刚被访问过,所以LRU字段值都很大(即访问时间最近)。

在使用 LRU 策略淘汰数据时,这些数据会留存在缓存中很长一段时间,造成缓存污染。如果查询的数据量很大,这些数据占满了缓存空间,却又不会服务新的缓存请求,此时,再有新数据要写入缓存的话,还是需要先把这些旧数据替换出缓存才行,这会影响缓存的性能。

LFU缓存策略

两个维度:

  • 一是,数据访问的时效性(访问时间离当前时间的远近)
  • 二是,数据的被访问次数

LFU缓存策略是在LRU策略基础上,为每个数据增加了一个计数器,来统计这个数据的访问次数。当使用LFU策略筛选淘汰数据时,首先根据数据的访问次数进行筛选,把访问次数最少的数据淘汰。如果两个数据的访问次数相同,LFU策略再比较这两个数据的访问时效性,把距离上一次访问时间更久的数据淘汰出缓存。

和那些被频繁访问的数据相比,扫描式单次查询的数据因为不会被再次访问,所以它们的访问次数不会再增加。因此,LFU 策略会优先把这些访问次数低的数据淘汰出缓存。这样一来,LFU 策略就可以避免这些数据对缓存造成污染了。

为了避免操作链表的开销,Redis在实现LRU策略时使用了两个近似方法:

  • Redis是用RedisObject结构来保存数据的,RedisObject结构中设置了一个lru字段,用来记录数据的访问时间戳;
  • Redis并没有为所有的数据维护一个全局的链表,而是通过随机采样方式,选取一定数量(例如10个)的数据放入候选集合,后续在候选集合中根据lru字段值的大小进行筛选。

在此基础上,Redis在实现LFU策略的时候,只是把原来24bit大小的lru字段,又进一步拆分成了两部分:

  1. ldt值:lru字段的前16bit,表示数据的访问时间戳。
  2. counter值:lru字段的后8bit,表示数据的访问次数。

总结一下:当 LFU 策略筛选数据时,Redis 会在候选集合中,根据数据 lru 字段的后 8bit 选择访问次数最少的数据进行淘汰。当访问次数相同时,再根据 lru 字段的前 16bit 值大小,选择访问时间最久远的数据进行淘汰。

counter值只有8bit,最大值为255,Redis在实现Redis策略时,并没有采用数据每被访问一次就给对应的counter值加1的计数规则。

LFU策略实现的计数规则是:每当数据被访问一次时,首先用计数器当前的值乘以配置项lfu_log_factor再加1,再取其导数,得到一个p值;然后把这个p值和一个取值范围在(0,1)间的随机数 r值比较大小,只有p大于r时,计数器才加1.

1.0/(cur_value*lfu_log_factor+1)random(0,1)比较大小

当 lfu_log_factor 取不同值时,在不同的实际访问次数情况下,计数器的值变化情况。
在这里插入图片描述
一般可以将 lfu_log_factor 取值为 10。

在有些场景下,有些数据在短时间内会被大量访问,之后就不会再被访问了。那么再按照访问次数来筛选的话,这些数据会被留存在缓存中,但不会提升缓存命中率。Redis使用counter值的衰减机制来处理这个问题。

LFU策略使用衰减因子配置项lfu_decay_time来控制访问次数的衰减。LFU策略会计算当前时间和数据最近一次访问时间的差值,并把这个差值换算成以分钟为单位。然后LFU策略再把这个差值除以lfu_decay_time值,所得的结果就是counter要衰减的值。

LRU 策略更加关注数据的时效性,而 LFU 策略更加关注数据的访问频次。通常情况下,实际应用的负载具有较好的时间局部性,所以 LRU 策略的应用会更加广泛。但是,在扫描式查询的应用场景中,LFU 策略就可以很好地应对缓存污染问题了,建议你优先使用。

https://time.geekbang.org/column/article/297270

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值