什么是缓存穿透和雪蹦?
缓存是为了减轻数据库的压力、提升数据的查询效率。但是按照一般逻辑,数据在缓存中没有取到就会去数据库中获取,如果大量的访问都没有在缓存中获取到数据,就有“穿透”的问题,进而带来缓存雪蹦的问题。
1、大并发环境下,第一次访问缓存,发现缓存中没有数据,然后所有线程都会去访问数据库,这样就造成缓存短暂失效,这就是“缓存穿透”。缓存穿透的危害:导致大量的请求直接到数据库,导致数据库的承载压力过大导致宕机。
2、数据库查询不到这个数据,在redis中保存的key所对应的value就为null。那代码逻辑也会出现穿透。先判断key是否在缓存中存在,只要存在key,不论value是否为null,都认为缓存中有 数据,主要有数据就不要再访问数据库。
解决缓存穿透问题
总结:
1、双重检测机制(在锁的内外都进行缓存查询,没有查到在进行数据库查询)。
2、不论查询到的数据是否为null,都将它写入缓存。
3、避免缓存中的null键值撑满整个内存,所以需要给null键值设置过期时间。
4、提前分析项目中可能出现并发的数据,提前将这些数据写入缓存。(在tomcat容器启动的时候写入缓存)
5、key的设计,要唯一标识一条数据,不能被其他数据覆盖。
Object obj = null;
//第一层:解决缓存穿透:通过数据库没有的数据进行穿透
if(redisTemplate.hasKey(key)){
//1、读取缓存
obj = getStringCache(key);
return obj;
}else {
//线程开始排队
synchronized (this) {
//第二层:解决缓存穿透:通过数据库没有的数据进行穿透
if (redisTemplate.hasKey(key)) {
//双重检测锁机制,第二层避免排队的线程再次查询数据库,导致数据库的压力。需要在锁内部再次查询缓存
obj = getStringCache(key);
return obj;
} else {
//2、缓存中没有数据,读取数据库数据库
obj = goodsMapper.queryGoods();
Gson gson = new Gson();
String json = gson.toJson(obj);
if(obj == null){
//如果是空key,避免空key(无效数据)将内存消耗完,设置过期时间,定期删除。
//注意:时间也不宜过长,时间过长会导致无效数据短期内也会撑满整个内存。
addStringCacheEX(key,json,5);
}else {
//3、将数据库取出的数据写入缓存,方便第二次从缓存中取数据
addStringCache(key,json);
}
obj = json;
}
}
}
return obj;
}