1、缓存雪崩、缓存穿透、缓存击穿
不管是缓存雪崩、缓存穿透、缓存击穿。都在讲一件事,请求不经过缓存,直接打到数据库上。
只不过粒度或者程度不一样而已。
缓存雪崩 很明显这个最严重 从名词上来说 面积更大 当大量缓存集中在某一个时间段失效,这样在失效的时候,也会给后端系统(比如DB)带来很大压力。
缓存穿透和缓存击穿 概念上有点模糊
穿透代表的是如遇无人之境,指代的是缓存或者数据库 或者二者都不存在这个数据 一直不停的请求 从而拖垮数据库
击穿代表的是有屏障的 本来能顶住的 突然顶不住的(你想一个不经常访问的key就算失效过期了,会造成缓存击穿吗?不会!所以这种情况
基本针对爆点数据热点数据 比如电商的爆款啥的)
解放方案
缓存雪崩 缓存时间加随机值
做电商项目的时候,一般是采取不同分类商品,缓存不同周期。在同一分类中的商品,加上一个随机因子。这样能尽可能分散缓存过期时间,而且,热门类目的商品缓存时间长一些,冷门类目的商品缓存时间短一些,也能节省缓存服务的资源。
缓存穿透 对空值也进行缓存
缓存击穿 对爆点数据永不过期 互斥锁也可以但是大多数情况没必要
参考网址
https://baijiahao.baidu.com/s?id=1619572269435584821&wfr=spider&for=pc
互斥锁的解决方案
public List<String> getCacheSave2(String key,int retryCount) throws InterruptedException {
List<String> resultList = (List<String>)redisTemplate.opsForValue().get(key);
if(CollectionUtils.isEmpty(resultList)){
final String mutexKey = key + "_lock";
boolean isLock = (Boolean) redisTemplate.execute(new RedisCallback() {
@Override
public Object doInRedis(RedisConnection connection) throws DataAccessException {
//只在键key不存在的情况下,将键key的值设置为value,若键key已经存在,则 SETNX 命令不做任何动作
//命令在设置成功时返回 1 , 设置失败时返回 0
return connection.setNX(mutexKey.getBytes(),"1".getBytes());
}
});
if(isLock){
//设置成1秒过期
redisTemplate.expire(mutexKey, 1000, TimeUnit.MILLISECONDS);
resultList = getValueBySql(key);
redisTemplate.opsForValue().set(key, resultList, 1000, TimeUnit.SECONDS);
redisTemplate.delete(mutexKey);
}else{
//线程休息50毫秒后重试
Thread.sleep(50);
retryCount--;
System.out.println("=====进行重试,当前次数:" + retryCount);
if(retryCount == 0){
System.out.println("====这里发邮件或者记录下获取不到数据的日志,并为key设置一个空置防止重复获取");
List<String> list = Lists.newArrayList("no find");
redisTemplate.opsForValue().set(key, list, 1000, TimeUnit.SECONDS);
return list;
}
return getCacheSave2(key,retryCount);
}
}
return resultList;
}
参考网址
https://www.jianshu.com/p/1398cf205d14
附带布隆过滤器讲解
https://blog.csdn.net/maizi1045/article/details/80652351