1、概述
三者出现的根本原因:Redis命中率下降,请求落在数据库。
正常情况下,大量的资源请求都会被redis响应,在redis得不到响应的小部分请求才会去请求数据库,这样数据库的压力是非常小的,是可以正常工作的。
如果大量的请求在redis上得不到响应,那么就会导致这些请求会直接去访问数据库,导致数据库的压力瞬间变大而卡死或者宕机。如下图:
① 大量的高并发的请求打在redis上
② 这些请求发现redis上并没有需要请求的资源,redis命中率降低
③ 因此这些大量的高并发请求转向数据库请求对应的资源
④ 数据库压力瞬间增大,直接将数据库打垮,进而引发一系列“灾害”
为什么redis会没有需要访问的数据呢?通过分析大致可以总结为三种情况,也就对应着redis的雪崩、穿透和击穿
private Order getOrder(Long orderId){
Order order = null;
// 查redis
Map<Object, Object> entries = redisTemplate.opsForHash().entries("order:" + orderId);
if(entries==null||entries.size()==0){ // 检测
// 查数据库
order = orderService.getOrder(orderId);
if(Objects.nonNull(order)){
// 设置缓存
redisTemplate.opsForHash().putAll("order:" + orderId,JSON.parseObject(JSON.toJSONString(order)));
}
}else{
order = JSONObject.parseObject(JSON.toJSONString(entries), Order.class);
}
return order;
}
2、缓存穿透
请求根本不存在的资源(数据库本身就不存在,Redis更是不存在),这将导致这个不存在的数据每次请求都要到数据库去查询,可能导致数据库挂掉。
解决方案
1、布隆过滤器
布隆过滤器的核心思想是使用多个哈希函数来将元素映射到位数组中的多个位置上。当一个元素被加入到布隆过滤器中时,它会被多次哈希,并将对应的位数组位置设置为1。当需要判断一个元素是否在布隆过滤器中时,我们只需将该元素进行多次哈希,并检查对应的位数组位置是否都为1,如果其中有任意一位为0,则说明该元素不在集合中;如果所有位都为1,则说明该元素可能在集合中(因为有可能存在哈希冲突),需要进一步检查。总而言之布隆过滤器中存在的数据一定存在,不存在的数据可能存在(概率大小问题,概率可调节)。
2、缓存空值
Redis和数据都不存在数据将key对应value(null)存入redis,下次相同的key访问直接通过redis返回null
3、缓存击穿
Redis某个热点key过期,但是此时有大量的用户访问该过期key,这个时候大并发的请求可能会瞬间把数据库压垮。
解决方案
1、双重检测锁
只有一个请求可以获取到互斥锁,到数据库中将数据查询并返回到Redis,之后所有请求就可以从Redis中得到响应。
private Order getOrder(Long orderId){
Order order = null;
// 查redis
Map<Object, Object> entries = redisTemplate.opsForHash().entries("order:" + orderId);
if(entries==null||entries.size()==0){ // 检测
synchronized (lock){ // 上锁
entries = redisTemplate.opsForHash().entries("order:" + orderId);
if(entries==null||entries.size()==0){ // 检测
// 查数据库
order = orderService.getOrder(orderId);
if(Objects.nonNull(order)){
// 设置缓存
redisTemplate.opsForHash().putAll("order:" + orderId,JSON.parseObject(JSON.toJSONString(order)));
}
}
}
}else{
order = JSONObject.parseObject(JSON.toJSONString(entries), Order.class);
}
return order;
}
2、实时调整
监控哪些数据是热门数据,实时的调整key的过期时长(每次请求都重新设置超时时间)
4、缓存雪崩
Redis中大量的key集体过期,请求全部转发到数据库,数据库瞬时压力过重崩溃
与缓存击穿的区别
雪崩是很多key失效,击穿是某一个key失效
解决方案
将缓存失效时间分散开,比如可以在原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低