缓存穿透、缓存雪崩和缓存击穿
缓存穿透
缓存穿透指的是查询一个在数据库中不存在的对象,而又没有缓存这个key导致请求都会查询redis后查询数据库
伪代码:
public Object queryObjectByKey(String key) {
Object obj = redisTemplate.opsForValue().get(key);
if(null != obj) {
//有缓存数据 直接返回
return obj;
}
//没有缓存 查询数据库
Object result = mapper.selectByKey(key);
if(null != result) {
//如果数据库中查询的内容不为空就存入缓存
redisTemplate.opsForValue().set(key,result,60,TimeUtil.MINUTES);
}
//返回结果
return result;
}
但是如果传入的这个key在数据库中就不存在,那么在缓存中也不会存在,那么就可以重复访问进行攻击,每一次请求都会进行查库。
解决方案: 可以在查询到的对象为空的情况下也进行缓存,只不过设置较低的缓存过期时间
缓存雪崩
问题原因: 在很短的时间内对大量数据进行缓存,并且缓存过期时间一致,导致大批量缓存在短时间内过期对数据库造成很大的压力。
解决方案: 可以区分很火爆的数据和偏冷的数据设置不同的缓存过期时间,并设置缓存过期时间时加上随机因子,这样尽可能的分散缓存过期时间。
缓存击穿
缓存击穿指的是当一个key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就击穿了缓存,直接请求数据库,给数据库带来不可预知的压力,很有可能瞬间就把数据库压垮。
解决方案一: 最简单的方式就是将热点key的缓存时间设置的特别长或者临时设置为永不过期key。 缺点:这个value相当于是静态的,一直不会改变
解决方案二:(优) 优化上一种解决方案,可以在value中设置一个timeout值,这个timeout的值小于缓存的过期时间,当取到缓存时异步判断value中的timeout是否过期了,如过期了重新获取一次新值。
解决方案三: 使用互斥锁(mutex key):构建缓存时通过分布式锁只让一个线程进行查库,其余线程阻塞直到查库的线程获取到数据后从缓存中拿取。
参考: