redis击穿,穿透,雪崩以及解决方案
1、击穿: 指的是单个key在缓存中查不到,去数据库查询,这样如果数据量不大或者并发不大的话是没有什么问题的。
如果数据库数据量大并且是高并发的情况下那么就可能会造成数据库压力过大而崩溃。
注意: 这里指的是单个key发生高并发!!!
解决方案:
-
通过synchronized+双重检查机制:某个key只让一个线程查询,阻塞其它线程
在同步块中,继续判断检查,保证不存在,才去查DB。
例如:
private static volaite Object lockHelp=new Object();
public String getValue(String key){
String value=redis.get(key,String.class);
if(value=="null"||value==null||StringUtils.isBlank(value){
synchronized(lockHelp){
value=redis.get(key,String.class);
if(value=="null"||value==null||StringUtils.isBlank(value){
value=db.query(key);
redis.set(key,value,1000);
}
}
}
return value;
}
缺点: 会阻塞其它线程。
-
设置value永不过期
这种方式可以说是最可靠的,最安全的但是占空间,内存消耗大,并且不能保持数据最新 这个需要根据具体的业务逻辑来做 。
个人觉得如果要保持数据最新不放这么试试,仅供参考:
起个定时任务或者利用TimerTask 做定时,每个一段时间多这些值进行数据库查询更新一次缓存,当然前提时不会给数据库造成压力过大(这个很重要)
2、穿透
一般是出现这种情况是因为恶意频繁查询才会对系统造成很大的问题: key缓存并且数据库不存在,所以每次查询都会查询数据库从而导致数据库崩溃。
解决方案:
- 使用布隆过滤器: 热点数据等场景(具体看使用场景)
3、雪崩
雪崩指的是多个key查询并且出现高并发,缓存中失效或者查不到,然后都去db查询,从而导致db压力突然飙升,从而崩溃。
出现原因:
1)key同时失效
2)redis本身崩溃了
方案:
在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。(跟击穿的第一个方案类似,但是这样是避免不了其它key去查数据库,只能减少查询的次数)
可以通过缓存reload机制,预先去更新缓存,再即将发生大并发访问前手动触发加载缓存。
不同的key,设置不同的过期时间,具体值可以根据业务决定,让缓存失效的时间点尽量均匀。
做二级缓存,或者双缓存策略。A1为原始缓存,A2为拷贝缓存,A1失效时,可以访问A2,A1缓存失效时间设置为短期,A2设置为长期。(这种方式复杂点)