缓存穿透
缓存穿透是指客户端请求的数据在缓存和数据库中都不存在,这样缓存永远不会命中,这些请求都会打到数据库。
缓存穿透常见的解决方案
缓存空对象
当请求完Redis和数据库之后,仍然没有数据,为了防止用户继续请求到数据库层面,当数据库返回为空时,Redis会对该请求参数建立一个value为“” 的键值对。那么在下次请求时,则直接返回“”。
优点:实现简单,维护方便
缺点:
- 额外的内存消耗
- 可能造成短期的数据不一致
布隆过滤
请求在到达Redis之前会经过布隆过滤器进行判断,如果不存在则直接返回。存在的话会继续往下进行到Redis和数据库层面。
优点:内存占用较小,没有多余的key 缺点:
- 实现复杂
- 存在误判的可能
使用缓存空对象方法解决缓存穿透问题
流程简述
前端提交商铺id,从Redis查找是否有相关的商铺信息缓存。
进行缓存是否命中的判断,如果Redis内无缓存,则进入数据库层面查找。数据库进行判断该id的信息是否存在,如果存在则返回到前端,同时将数据写入Redis。如果不存在则将“”写入到Redis中去。
如果Redis内有缓存,则直接把缓存内的数据返回,如果是空值,也对数据库进行了防护。
流程图
代码实现
private Shop queryWithPassThrough(Long id){
//尝试从redis查询缓存
String shopJson = stringRedisTemplate.opsForValue().get(CACHE_SHOP_KEY + id);
//判断是否存在
if (StrUtil.isNotBlank(shopJson)){
//存在,返回
return JSONUtil.toBean(shopJson, Shop.class);
}
//判断命中的是否是空值
if(shopJson != null){
//等效于shopJson == ""
//返回一个错误信息
return null;
}
//不存在,根据id查询数据库
Shop shop = getById(id);
//不存在,返回错误
if (shop == null) {
//将空值写入redis,解决缓存穿透问题
stringRedisTemplate.opsForValue().set(CACHE_SHOP_KEY + id, ""
, RedisConstants.CACHE_NULL_TTL, TimeUnit.MINUTES);
return null;
}
//存在, 写入redis
stringRedisTemplate.opsForValue().set(CACHE_SHOP_KEY+id,JSONUtil.toJsonStr(shop),
RedisConstants.CACHE_SHOP_TTL,TimeUnit.MINUTES);
//返回
return shop;
}