缓存穿透 :缓存穿透是指客户端请求的数据在缓存中和数据库中都不存在,这样缓存永远不会生效,这些请求都会打到数据库。
常见的解决方案有两种:
-
缓存空对象
-
优点:实现简单,维护方便
-
缺点:
-
额外的内存消耗
-
可能造成短期的不一致
-
-
-
布隆过滤
-
优点:内存占用较少,没有多余key
-
缺点:
-
实现复杂
-
存在误判可能
-
-
缓存空对象思路分析:当我们客户端访问不存在的数据时,先请求redis,但是此时redis中没有数据,此时会访问到数据库,但是数据库中也没有数据,这个数据穿透了缓存,直击数据库,我们都知道数据库能够承载的并发不如redis这么高,如果大量的请求同时过来访问这种不存在的数据,这些请求就都会访问到数据库,简单的解决方案就是哪怕这个数据在数据库中也不存在,我们也把这个数据存入到redis中去,这样,下次用户过来访问这个不存在的数据,那么在redis中也能找到这个数据就不会进入到缓存了
代码如下:
@Override
public Shop findByid(Long id) {
//根据id查询redis,,
String shop = stringRedisTemplate.opsForValue().get(SHOP_ID + id);
//如果查询不到则查询数据库
if (StrUtil.isNotBlank(shop)){
Shop shop1 = JSONUtil.toBean(shop,Shop.class);
return shop1;
}
//为空值,从redis返回,防止缓存穿透
if (shop!=null){
return null;
}
//根据id查询数据库
Shop one = getById(id);
String jsonStr = JSONUtil.toJsonStr(one);
if (StrUtil.isBlank(jsonStr)){
//查询不到记录将空值写入数据库,防止缓存穿透
stringRedisTemplate.opsForValue().set(SHOP_ID+id,"",LOGIN_CODE_TTL,TimeUnit.MINUTES);
return null;
}
//将查到的记录存入redis中
stringRedisTemplate.opsForValue().set(SHOP_ID+id,jsonStr,SHOP_TTL, TimeUnit.MINUTES);
return one;
}
1)核心代码1:
if (StrUtil.isBlank(jsonStr)){
//查询不到记录将空值写入数据库,防止缓存穿透
stringRedisTemplate.opsForValue().set(SHOP_ID+id,"",LOGIN_CODE_TTL,TimeUnit.MINUTES);
return null;
}
当数据库查询不到该条数据时,redis存入空字符串,防止缓存穿透
2)核心代码2:
//为空值,从redis返回,防止缓存穿透
if (shop!=null){
return null;
}
从redis中查询该条数据,发现是空字符串,返回null