缓存穿透
问题描述
缓存穿透是指查询一个一定不存在的数据,由于缓存时不命中的,则需要从数据库中查询。查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到数据库中去查询,进而增大了数据库的压力。如不断发起“id=-1”等不可能存在的数据的请求,此时缓存中没有就会向DB读取进而可能导致DB挂掉。
解决方案
1、增加接口层校验
2、每次查询数据库不存在的数据时缓存一个空对象,并设置过期时间(解决单个不存在的key多次查询,造成内存空间浪费)
3、使用布隆过滤器(注意:必须将所有的key都放到布隆过滤器和redis里,否则请求会被直接返回空数据)
缓存击穿
问题描述
对于设置了过期时间的key,缓存在某个时间点过期的时候,恰好这个时间点对这个key有大量的并发请求过来,这些请求发现缓存过期一般会从数据库中进行查询并添加到缓存中,这个时候突然大的并发进来会把数据库压垮。
解决方案
1、使用互斥锁:当缓存失效时,不立即去查询数据库,先使用例如redis的setnx命令去设置一个互斥锁,当操作成功返回时在进行load db并将查询的数据添加到缓存当中,否则重试获取缓存的方法,具体逻辑代码如下
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);
} else { //这个时候代表同时候的其他线程已经load db并回设到缓存了,这时候重试获取缓存值即可
sleep(50);
get(key); //重试
}
} else {
return value;
}
}
2、设置热点数据永不过期
缓存雪崩
问题描述
设置缓存时采用了相同的过期时间,导致缓存在某一时刻同时失效,请求全部打到数据库,数据库瞬间压力过重雪崩,与缓存击穿的区别是:雪崩是很多key过期,而击穿是单个key
解决方案
1、将缓存的过期时间分散开(在原有失效的失效时间基础上增加一个随机值,比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低,很难引起key集体失效的情况)
2、缓存永不过期(空间换效率)