Redis缓存穿透、缓存雪崩问题分析
Redis缓存穿透、缓存雪崩问题分析
饭一碗
把redis作为缓存使用已经是司空见惯,当redis中的数据量起来了以后你就得考虑以下几个问题:
(一)缓存和数据库间数据一致性问题
分布式环境下(单机就不用说了)非常容易出现缓存和数据库间的数据一致性问题,针对这一点的话,只能说,如果你的项目对缓存的要求是强一致性的,那么请不要使用缓存。我们只能采取合适的策略来降低缓存和数据库间数据不一致的概率,而无法保证两者间的强一致性。合适的策略包括 合适的缓存更新策略,更新数据库后要及时更新缓存、缓存失败时增加重试机制,例如MQ模式的消息队列。
(二)缓存穿透问题
现象:用户大量并发请求的数据(key)对应的数据在redis和数据库中都不存在,导致尽管数据不存在但还是每次都会进行查DB。
为什么key对应数据在缓存和db中不存在还会每次都进行DB查询呢?因为很多开发同学写的代码写的逻辑都是先从redis缓存中查一把,如果缓存中为空则从DB中查,如果DB中查到的数据不为空则设置到缓存并返回给接口。那么问题来了,如果从DB中查询的数据为空呢??
解决方案:
从DB中查询出来数据为空,也进行空数据的缓存,避免DB数据为空也每次都进行数据库查询;
使用布隆过滤器,但是会增加一定的复杂度及存在一定的误判率;
bloomfilter就类似于一个hash set,用于快速判某个元素是否存在于集合中,其典型的应用场景就是快速判断一个key是否存在于某容器,不存在就直接返回。布隆过滤器的关键就在于hash算法和容器大小,下面先来简单的实现下看看效果,我这里用guava实现的布隆过滤器:
<dependencies>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>23.0</version>
</dependency>
</dependencies>
public class BloomFilterTest {
private static final int capacity = 1000000;
private static final int key = 999998;
private static BloomFilter<Integer> bloomFilter = BloomFilter.create(Funnels.integerFunnel(), capacity);
static {
for (int i = 0; i < capacity; i++) {
bloomFilter.put(i);
}
}
public static void main(String[] args) {
/*返回计算机最精确的时间,单位微妙*/
long start = System.nanoTime();
if (bloomFilter.mightContain(key)) {
System.out.println("成功过滤到" + key);
}
long end = System.nanoTime();
System.out.println("布隆过滤器消耗时间:" + (end - start));
int sum = 0;
for (int i = capacity + 20000; i < capacity + 30000; i++) {
if (bloomFilter.mightContain(i)) {
sum = sum + 1;
}
}
System.out.println("错判率为:" + sum);
}
}
成功过滤到999998
布隆过滤器消耗时间:215518
错判率为:318
可以看到,100w个数据中只消耗了约0.2毫秒就匹配到了key,速度足够快。然后模拟了1w个不存在于布隆过滤器中的key,匹配错误率为318/10000,也就是说,出错率大概为3%,跟踪下BloomFilter的源码发现默认的容错率就是0.03:
public static <T> BloomFilter<T> create(Funnel<T> funnel, int expectedInsertions /* n */) {
return create(funnel, expectedInsertions, 0.03); // FYI, for 3%, we always get 5 hash functions
}
我们可调用BloomFilter的这个方法显式的指定误判率:
private static BloomFilter<Integer> bloomFilter = BloomFilter.create(Funnels.integerFunnel(), capacity,0.01);
我们断点跟踪下,误判率为0.02和默认的0.03时候的区别:
对比两个出错率可以发现,误判率为0.02时数组大小为8142363,0.03时为7298440,误判率降低了0.01,BloomFilter维护的数组大小也减少了843923,可见BloomFilter默认的误判率0.03是设计者权衡系统性能后得出的值。要注意的是,布隆过滤器不支持删除操作。用在这边解决缓存穿透问题就是:
public String getByKey(String key) {
// 通过key获取value
String value = redisService.get(key);
if (StringUtil.isEmpty(value)) {
if (bloomFilter.mightContain(key)) {
value = userService.getById(key);
redisService.set(key, value);
return value;
} else {
return null;
}
}
return value;
}
(三)缓存雪崩问题
现象:大量key同一时间点失效,同时又有大量请求打进来,导致流量直接打在DB上,造成DB不可用。
解决方案:
设置key永不失效(热点数据);
设置key缓存失效时候尽可能错开;
使用多级缓存机制,比如同时使用redsi和memcache缓存,请求->redis->memcache->db;
购买第三方可靠性高的Redis云服务器;
引申阅读: 使用quartz实现高级定制化定时任务(包含管理界面)
推荐阅读:elastic search搜索引擎实战demo:https://github.com/simonsfan/springboot-quartz-demo,分支:feature_es
————————————————
版权声明:本文为CSDN博主「饭一碗」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/fanrenxiang/article/details/80542580
推荐阅读
-
JAVAEE——宜立方商城06:Redis安装、数据类型和持久化方案、Redis集群分析与搭建、实现缓存和同步
-
Redis之缓存雪崩、缓存穿透、缓存预热、缓存更新、缓存降级
-
[redis]关于 缓存穿透/缓存击穿/缓存雪崩 看这篇文章就够了
-
关于缓存穿透,缓存击穿,缓存雪崩,热点数据失效问题的解决方案(转)
-
Redis中几个简单的概念:缓存穿透/击穿/雪崩,别再被吓唬了
-
缓存穿透、缓存击穿、缓存雪崩概念及解决方案
-
.net core Redis分布式缓存客户端实现逻辑分析及示例demo
-
redis缓存穿透和缓存失效的预防和解决
-
带上问题来学redis,看到不吃亏(什么是redis?缓存问题、数据一致性、redis配置文件汉化版)
-
redis的缓存雪崩、缓存穿透和缓存击穿