1.缓存雪崩
2.缓存击穿
3.缓存穿透
4.总结
1.缓存雪崩
缓存雪崩是如何发生的?
1)redis服务直接挂掉,redis全盘崩溃
2)redis中有大量缓存同时过期,导致大量查询直击mysql
解决
1.1)redis缓存集群实现高可用,主从+哨兵
1.2)ehcache本地缓存 + Hystrix或者阿里sentinel限流&降级
1.3)开启Redis持久化机制aof/rdb,尽快恢复缓存集群
2.1)设置缓存更新的时间都为固定时间+随机一定的时间,造成不会同时过期。
2.缓存击穿
.缓存击穿是如何发生的?
大量的请求正在访问同一个key, 此时这个key正好失效了,就会导致大量的请求到数据库上面。
危害:
会造成某一时刻的mysql压力过大,有宕机风险。
解决方案:
1)对于高热点key,设置永不过期
2)设置一个互斥锁,来防止缓存击穿:
public TUser findById(Integer id) {
TUser user = (TUser)redisTemplate.opsForValue().get(CACHE_KEY_USER + id);
if(user==null){
//小厂用
/* user = userMapper.selectById(id);
if(user!=null) {
redisTemplate.opsForValue().set(CACHE_KEY_USER + id, user);
}*/
//大厂用,对于高qps的优化,进来就先加锁,保证一个请求操作,让外面的redis等待一下,避免击穿mysql
synchronized (TUserServiceImpl.class){
//双端检索,再次查询redis
user = (TUser)redisTemplate.opsForValue().get(CACHE_KEY_USER + id);
if(user==null){
//还是为空,就去查mysql
user = userMapper.selectById(id);
if(user!=null){
redisTemplate.opsForValue().setIfAbsent(CACHE_KEY_USER + id, user,7L, TimeUnit.DAYS);
}
}
}
}
return user;
}
3)双缓存保存,定时轮询,互斥更新,差异失效时间
需求:假设我们要完成网页上的一个聚划算功能,每隔两个小时换一批商品。比如:8:000~10:00一批商品,10:00~12:00一批商品。如果按照定时任务的做法,8:00开始更新商品,假设数据量很大,mysql在很难查出来的情况下,到点了可能缓存已经过期了,但是mysql还没查询出商品,又会有大量查询攻击mysql,导致服务宕机。
思路:我们设置两个缓存,都保存相同的内容,两个缓存的过期时间不同。缓存B比缓存A过期时间更长一些,这样就可以保证缓存里永远有数据。
查询:先查询缓存A,如果A没有,查询缓存B。
//采用redis list数据结构的lrange命令实现分页查询
list = this.redisTemplate.opsForList().range(Constants.JHS_KEY_A, start, end);
if (CollectionUtils.isEmpty(list)) {
log.info("=========A缓存已经失效了,记得人工修补,B缓存自动延续5天");
//用户先查询缓存A(上面的代码),如果缓存A查询不到(例如,更新缓存的时候删除了),再查询缓存B
this.redisTemplate.opsForList().range(Constants.JHS_KEY_B, start, end);
更新:先更新缓存B,再更新缓存A。
//先更新B缓存
this.redisTemplate.delete(Constants.JHS_KEY_B);
this.redisTemplate.opsForList().leftPushAll(Constants.JHS_KEY_B,list);
this.redisTemplate.expire(Constants.JHS_KEY_B,20L,TimeUnit.DAYS);
//再更新A缓存
this.redisTemplate.delete(Constants.JHS_KEY_A);
this.redisTemplate.opsForList().leftPushAll(Constants.JHS_KEY_A,list);
this.redisTemplate.expire(Constants.JHS_KEY_A,15L,TimeUnit.DAYS);
注意:采用互斥更新和查询,保证两个key都有值。
3.缓存穿透
缓存穿透是什么:
请求查询一条记录,先去redis查询后查询不到数据,再去mysql查询后也查询不到数据,所以每次请求都会攻击mysql,导致数据库压力暴增。
危害:第一次来查询后,一般我们有回写redis机制,所以偶发一次没关系,但是频繁发生就有安全隐患。
解决方案:
1)空对象或者缺省值
一般来说这么做是没问题的,但是如果黑客大量拿未知的key来攻击我们的系统,每次都要去查询,且redis中无用key越写越多。
2)Redis布隆过滤器解决缓存穿透
深入理解redis——布隆过滤器BloomFilter
可以使用布隆过滤器来解决缓存穿透,布隆过滤器已经在这篇文章里讲解过了。
4.总结
今天学习了缓存雪崩/缓存击穿/缓存穿透的现象以及对应方案。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。