《Redis 缓存污染问题全解析及淘汰策略深度探索》
摘要:本文将深入探讨 Redis 中的缓存污染问题,包括其定义、产生的影响以及解决方法。同时,详细介绍 Redis 支持的多种缓存淘汰策略,帮助读者更好地理解和应用 Redis 缓存,提升系统性能。读者将从本文中获得关于 Redis 缓存优化的实用知识和技巧。
关键词:Redis、缓存污染、缓存淘汰策略、LRU、LFU、noeviction、volatile-random、volatile-ttl、volatile-lru、volatile-lfu、allkeys-lru、allkeys-random、allkeys-lfu
一、缓存污染问题的定义与影响
- 定义
- 缓存污染问题说的是缓存中一些只会被访问一次或者几次的数据,被访问完后,再也不会被访问到,但这部分数据依然留存在缓存中,消耗缓存空间。
- 影响
- 缓存污染会随着数据的持续增加而逐渐显露,随着服务的不断运行,缓存中会存在大量的永远不会再次被访问的数据。
- 缓存空间是有限的,如果缓存空间满了,再往缓存里写数据时就会有额外开销,影响 Redis 性能。这部分额外开销主要是指写的时候判断淘汰策略,根据淘汰策略去选择要淘汰的数据,然后进行删除操作。
二、解决缓存污染问题的方法
解决缓存污染问题,实际上就是配置缓存淘汰策略,要把不会再被访问的数据筛选出来并删除掉,为后续写入的数据腾空间。
三、Redis 支持的缓存淘汰策略
(一)策略分类
Redis 支持的缓存淘汰策略可以分为两类:
- 以
volatile
开头的淘汰策略是在设置了过期时间的数据中进行淘汰,共有四种。 - 以
allkeys
开头的淘汰策略是在所有数据中进行淘汰,共有三种。
(二)具体策略详解
- noeviction
- 该策略是 Redis 的默认策略。在这种策略下,一旦缓存被写满了,再有写请求来时,Redis 不再提供服务,而是直接返回错误。
- 这种策略不会淘汰数据,所以无法解决缓存污染问题。一般生产环境不建议使用。其他七种规则都会根据自己相应的规则来选择数据进行删除操作。
- volatile-random
- 在设置了过期时间的键值对中,进行随机删除。
- 因为是随机删除,无法把不再访问的数据筛选出来,所以可能依然会存在缓存污染现象,无法解决缓存污染问题。
- volatile-ttl
- 这种算法在判断淘汰数据时参考的指标比随机删除时多进行一步过期时间的排序。
- Redis 在筛选需删除的数据时,越早过期的数据越优先被选择。
- volatile-lru
- 会使用 LRU 算法筛选设置了过期时间的键值对。
- LRU 算法的全称是 Least Recently Used,按照最近最久未被使用的原则来筛选数据。
- Redis 优化的 LRU 算法实现:Redis 会记录每个数据的最近一次被访问的时间戳。在 Redis 在决定淘汰的数据时,第一次会随机选出 N 个数据,把它们作为一个候选集合。接下来,Redis 会比较这 N 个数据的 lru 字段,把 lru 字段值最小的数据从缓存中淘汰出去。通过随机读取待删除集合,可以让 Redis 不用维护一个巨大的链表,也不用操作链表,进而提升性能。Redis 选出的数据个数 N,通过配置参数
maxmemory-samples
进行配置。个数 N 越大,则候选集合越大,选择到的最久未被使用的就更准确,N 越小,选择到最久未被使用的数据的概率也会随之减小。
- volatile-lfu
- 会使用 LFU 算法选择设置了过期时间的键值对。
- LFU 算法:LFU 缓存策略是在 LRU 策略基础上,为每个数据增加了一个计数器,来统计这个数据的访问次数。当使用 LFU 策略筛选淘汰数据时,首先会根据数据的访问次数进行筛选,把访问次数最低的数据淘汰出缓存。如果两个数据的访问次数相同,LFU 策略再比较这两个数据的访问时效性,把距离上一次访问时间更久的数据淘汰出缓存。
- Redis 的 LFU 算法实现:当 LFU 策略筛选数据时,Redis 会在候选集合中,根据数据 lru 字段的后 8bit 选择访问次数最少的数据进行淘汰。当访问次数相同时,再根据 lru 字段的前 16bit 值大小,选择访问时间最久远的数据进行淘汰。
- Redis 只使用了 8bit 记录数据的访问次数,而 8bit 记录的最大值是 255,这样在访问快速的情况下,如果每次被访问就将访问次数加一,很快某条数据就达到最大值 255,可能很多数据都是 255,那么退化成 LRU 算法了。所以 Redis 为了解决这个问题,实现了一个更优的计数规则,并可以通过配置项,来控制计数器增加的速度。
- 参数
lfu-log-factor
:用计数器当前的值乘以配置项lfu_log_factor
再加 1,再取其倒数,得到一个 p 值;然后,把这个 p 值和一个取值范围在(0,1)间的随机数 r 值比大小,只有 p 值大于 r 值时,计数器才加 1。 - 参数
lfu-decay-time
:控制访问次数衰减。LFU 策略会计算当前时间和数据最近一次访问时间的差值,并把这个差值换算成以分钟为单位。然后,LFU 策略再把这个差值除以lfu_decay_time
值,所得的结果就是数据 counter 要衰减的值。 lfu-log-factor
设置越大,递增概率越低,lfu-decay-time
设置越大,衰减速度会越慢。我们在应用 LFU 策略时,一般可以将lfu_log_factor
取值为 10。如果业务应用中有短时高频访问的数据的话,建议把lfu_decay_time
值设置为 1。可以快速衰减访问次数。volatile-lfu 策略是 Redis 4.0 后新增。
- 参数
- allkeys-lru
- 使用 LRU 算法在所有数据中进行筛选。
- 具体 LFU 算法跟上述 volatile-lru 中介绍的一致,只是筛选的数据范围是全部缓存。
- allkeys-random
- 从所有键值对中随机选择并删除数据。
- volatile-random 跟 allkeys-random 算法一样,随机删除就无法解决缓存污染问题。
- allkeys-lfu
- 使用 LFU 算法在所有数据中进行筛选。
- 具体 LFU 算法跟上述 volatile-lfu 中介绍的一致,只是筛选的数据范围是全部缓存。allkeys-lfu 策略是 Redis 4.0 后新增。
(三)策略对比表格
策略名称 | 淘汰范围 | 淘汰方式 | 能否解决缓存污染问题 |
---|---|---|---|
noeviction | 无特定范围 | 不淘汰数据 | 否 |
volatile-random | 设置了过期时间的数据 | 随机删除 | 可能无法解决 |
volatile-ttl | 设置了过期时间的数据 | 按过期时间排序淘汰 | 可能部分解决 |
volatile-lru | 设置了过期时间的数据 | LRU 算法 | 相对较好地解决 |
volatile-lfu | 设置了过期时间的数据 | LFU 算法 | 较好地解决 |
allkeys-lru | 所有数据 | LRU 算法 | 相对较好地解决 |
allkeys-random | 所有数据 | 随机删除 | 无法解决 |
allkeys-lfu | 所有数据 | LFU 算法 | 较好地解决 |
(四)Java 代码示例
以下是使用 Jedis 连接 Redis 并设置缓存淘汰策略为allkeys-lru
的示例代码:
java
import redis.clients.jedis.Jedis;
public class RedisExample {
public static void main(String[] args) {
Jedis jedis = new Jedis("localhost", 6379);
// 设置缓存淘汰策略为 allkeys-lru
jedis.configSet("maxmemory-policy", "allkeys-lru");
jedis.set("key1", "value1");
jedis.set("key2", "value2");
System.out.println(jedis.get("key1"));
System.out.println(jedis.get("key2"));
jedis.close();
}
}
(五)LRU 算法流程图
是 否 否 是 开始 数据访问 数据是否在缓存中 更新最近访问时间 判断缓存是否已满 将数据放入缓存 根据 LRU 算法选择要淘汰的数据 淘汰数据 结束
四、总结与建议
Redis 4.0 之前一共实现了 6 种内存淘汰策略分别是 noeviction、volatile-random、volatile-ttl、volatile-lru、allkeys-lru、allkeys-random,在 4.0 之后,又增加了 2 种策略,分别是 allkeys-lfu,volatile-lfu。建议优先使用 allkeys-lru 策略。使用 allkeys 开头的策略,在所有数据中进行选择,防止客户端因不规范等原因,随意设置的缓存数据,没配置超时时间。还可以利用 LRU 这一经典缓存算法的优势,把最近最常访问的数据留在缓存中,提升应用的访问性能。
内容 | 详情 |
---|---|
缓存污染问题定义 | 缓存中一些只会被访问一次或者几次的数据,被访问完后,再也不会被访问到,但仍留存在缓存中消耗空间。 |
缓存污染问题影响 | 数据持续增加时,缓存中存在大量不再被访问的数据,影响 Redis 性能。 |
解决方法 | 配置缓存淘汰策略,筛选并删除不会再被访问的数据。 |
Redis 淘汰策略分类 | volatile 开头在设置过期时间的数据中淘汰,allkeys 开头在所有数据中淘汰。 |
具体淘汰策略 | noeviction、volatile-random、volatile-ttl、volatile-lru、volatile-lfu、allkeys-lru、allkeys-random、allkeys-lfu。 |
策略对比 | 不同策略在淘汰范围、淘汰方式和解决缓存污染问题能力上有所不同。 |
Java 代码示例 | 使用 Jedis 设置缓存淘汰策略为 allkeys-lru。 |
LRU 算法流程图 | 展示 LRU 算法的数据访问和淘汰流程。 |
总结与建议 | 优先使用 allkeys-lru 策略,利用 LRU 算法优势提升性能。 |
嘿,小伙伴们!关于 Redis 的缓存污染问题和淘汰策略就介绍到这里啦。如果你有更好的见解或者经验,欢迎在评论区分享哦!让我们一起把 Redis 用得更溜吧!😉