缓存污染:在一些场景下,有些数据被访问次数非常少,甚至只会被访问一次。当这些数据服务完访问请求后,如果继续留存在缓存中的话,只会白白占用缓存空间。缓存污染严重,大量不再访问的数据滞留在缓存中,数据会沾满整个空间,我们再往缓存中写入数据时候,就需要淘汰已有缓存,引入额外操作时间开销,进而影响应用的性能。 二.如何解决缓存污染问题
把不会再被访问的数据筛选出来并淘汰掉,这样就不会产生缓存被写满以后再淘汰旧数据的情况。
三.volatitle-random和allkeys-random策略采用随机挑选数据的方式,来筛选即将被淘汰的数据。既然是随机挑选,那么 Redis 就不会根据数据的访问情况来筛选数据。如果被淘汰的数据又被访问了,就会发生缓存缺失。也就是说,应用需要到后端数据库中访问这些数据,降低了应用的请求响应速度。所以,volatile-random 和 allkeys-random 策略,在避免缓存污染这个问题上的效果非常有限。 四. volatitle-ttl
volatitle-ttl策略是否能有效应对缓存污染。volatitle-ttl针对的是设置过期时间的数据,把这些数据中剩余存活时间最短的筛选出来并淘汰掉。 volatitle-ttl策略不再是随机选择淘汰数据,但是剩余存活时间并不能直接反映数据再次访问的情况 因此,按照volatile-ttl策略淘汰数据和按随机访问方式淘汰数据类似,也可能出现数据淘汰后,再次访问导致的缓存缺失问题。 如果业务应用给数据设置过期时间时,明确知道数据会再次访问情况,并根据访问情况设置过期时间。此时,Redis按照数据的剩余最短存活时间进行筛选,在明确知道数据被再次访问情况下,volatitle-ttl可以有效避免缓存污染。 五.LRU缓存策略
LRU策略的核心思想:一个数据刚刚被访问,那么这个数据肯定是热数据,还会再次被访问。
实现方案:
Redis中LRU策略,会在每个数据对应的RedisObject结构体中设置一个lru字段,用来记录访问时间戳。再进行数据淘汰时,LRU策略会在候选数据集中淘汰掉lru字段值最小的数据(也就是访问时间最久的数据)。
存在问题:
只看数据访问时间,使用LRU策略在处理扫描式单次查询操作时,无法解决缓存污染问题。
扫描式单次查询操作:应对大量的数据进行一次全体读取,每个数据都会被读取而且只会被读取一次,此时被查询的数据刚刚被访问过。LRU字段值都很大。这些数据会缓存很长一段时间,造成缓存污染。
扫描式单次查询会造成污染 六.LFU缓存策略优化LFU策略中会从两个维度来筛选并淘汰一批数据:
数据访问的时效性(访问时间)数据的被访问次数
为每个数据域增加一个计数器,来统计这个数据的访问次数。
当使用 LFU 策略筛选淘汰数据时,
首先会根据数据访问次数进行筛选,把访问最低的数据淘汰出缓存。如果两个数的访问次数相同,LFU策略再比较两个数据的访问时效性,把距离上一次访问时间更久的数据淘汰出缓存。 LFU策略的实现:
为了避免操作链表的开销,Redis在实现LRU策略时使用了两个相近的方法:
Redis是用RedisObject结构来保存数据的,RedisObject结构中设置了一个lru字段,用来记录数据访问时间戳;Redis并没有为所以的数据维护一个全局的链表,而是通过随机采用的方式选取一定数量的数据放入候选集合,后续在候选集合中根据lru字段值的大小进行筛选。
Redis在实现LFU策略的时候,把原来24bit的lru字段进一步拆分成两部分:
- ldt值 :lru字段的前16bit,表示数据访问时间戳 counter值:;ru字段的后8bit,表示数据访问次数。
当LFU策略筛选数据集时,Redis会在候选集合中,根据数据lru字段的后8bit选中访问次数最少的数据进行淘汰。当访问次数相同时候,再根据lru字段的前16bit值大小,选择访问时间的最久远的数据进行淘汰。
Redis只使用了8bit记录数据的访问次数,而8bit记录的最大值是255
在实现LFU策略时,Redis并没有采用数据没被访问一次,就给对应的counter值加1的计数规则,而是采用了更优化的计数规则。
七.应用场景:在实际业务应用中,LRU和LFU两个策略都有应用。 LRU更加关注数据的时效性,LFU策略更加关注数据的访问频次 实际应用的负载具有较好的时间局部性,所以LRU应用广泛 扫描式查询的应用场景可以优先使用LFU策略。
如果业务应用用有短时高频访问数据,除了 LFU 策略本身会对数据的访问次数进行自动衰减以外可以优先使用 volatile-lfu 策略,并根据这些数据的访问时限设置它们的过期时间。



