击穿:
单个key进行查询,在缓存中查询不到。所以只能打到数据库上去查询。如果访问量不大没什么问题,但是如果数据库数据量大或者高并发的情况下,就会造成数据库崩溃的情况
解决方案一:通过synchronized和双重检查机制,让单一key只允许一个线程查看,阻塞其他线程
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永不过期
这种方式是最可靠的,最安全的但是占用空间太大,内存消耗大,并且不能保证数据最新,这个需要根据具体的业务逻辑来做。 如果为了保证数据最新的话,可以起个定时任务做定时,每一段时间段将这些值进行数据库查询更新一次缓存,当然前提是不会给数据库造成太大的压力
解决方案三: 使用互斥锁(mutex key)
业界比较常用的做法,是使用mutex ,简单来说,就是在缓存失效的时候(判断拿出来的值为空),不是立即去load db ,而是先使用缓存工具的某些成功操作返回值的操作,(比如redis的setnx或者memcache的ADD) 去set一个mutex key,当操作返回成功时,在进行load db 的操作并回设缓存,否则就重试整个get缓存
setnx,是set if not exists 的缩写,只有在不存在的情况下才会设置。可以利用它来实现锁的效果 public String get(key) { String value = redis.get(key); if (value == null) { //代表缓存值过期 //设置3min的超时,防止del操作失败的时候,下次缓存过期一直不能load db if (redis.setnx(key_mutex, 1, 3 * 60) == 1) { //代表设置成功 value = db.get(key); redis.set(key, value, expire_secs); redis.del(key_mutex);
return value;
} else { //这个时候代表同时候的其他线程已经load db并回设到缓存了,这时候重试获取缓存值即可
sleep(10);
get(key); //重试
}
} else {
return value;
}
} 缺点:代码复杂度大,存在死锁风险,存在线程池阻塞问题
雪崩
雪崩指的是多个key查询并且出现高并发,缓存中失效或者查询不到,然后都去db查询,从而导致db压力瞬间飙升,从而崩溃
出现原因:多个key同时失效,或者redis本身崩溃
方案
1.在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量,比如对某个key只允许一个线程查询数据库和写缓存。其他线程等待。(跟击穿的方案一样,避免不了其他key去查询数据库,只能减少查询的次数)
2.可以通过缓存reload机制,预先去更新缓存,在即将发生大并发访问前手动触发加载缓存
3.不同的key,设置不同的过期时间。具体值可以根据业务决定的,让缓存失效的时间点尽量均匀
4.做二级缓存,或者双缓存策略,A1为原始缓存,A2为拷贝缓存,A1缓存失效时,可以访问A2。A1缓存失效时间设置为短期的,A2设置过期时间为长期。
击穿
一般是出现这种情况是因为恶意频繁查询才会对系统造成很大的问题,key缓存并且数据库不存在,所以每次查询都会查询数据库从而导致数据库崩溃
解决方案:
1>使用布隆克隆器:热点数据等场景
布隆过滤器:可以理解为一个不怎么准确的set结构,当你使用它的contains方法判断某个对象是否存在时,他可能会误判,但是布隆过滤器也不是特别不准确,只是参数设置的合理,它的准确度可以控制的相对足够准确。只会有小小的误判概率。 当布隆过滤器说某个值存在时,这个值可能不存在;当他说某个值不存在时,那这个值肯定不存在。打个比方。当它说不认识你时,肯定就是不认识;当它说见过你时,可能根本没有见过面,不过因为你的脸跟他认识的某个人的脸比较相似,,所以误认为见过你。
缺点:1.会存在一定的误判率
2.对新增加的数据无法进行布隆过滤
3.数据的key不会频繁的更改