黑马Redis项目 缓存穿透的解决思路和实现

158 阅读2分钟

缓存穿透

缓存穿透是指客户端请求的数据在缓存和数据库中都不存在,这样缓存永远不会命中,这些请求都会打到数据库。

image.png

缓存穿透常见的解决方案

缓存空对象

当请求完Redis和数据库之后,仍然没有数据,为了防止用户继续请求到数据库层面,当数据库返回为空时,Redis会对该请求参数建立一个value为“” 的键值对。那么在下次请求时,则直接返回“”。

优点:实现简单,维护方便

缺点:

  • 额外的内存消耗
  • 可能造成短期的数据不一致

布隆过滤

请求在到达Redis之前会经过布隆过滤器进行判断,如果不存在则直接返回。存在的话会继续往下进行到Redis和数据库层面。

image.png

优点:内存占用较小,没有多余的key 缺点:

  • 实现复杂
  • 存在误判的可能

使用缓存空对象方法解决缓存穿透问题

流程简述

前端提交商铺id,从Redis查找是否有相关的商铺信息缓存。

进行缓存是否命中的判断,如果Redis内无缓存,则进入数据库层面查找。数据库进行判断该id的信息是否存在,如果存在则返回到前端,同时将数据写入Redis。如果不存在则将“”写入到Redis中去。

如果Redis内有缓存,则直接把缓存内的数据返回,如果是空值,也对数据库进行了防护。

流程图

image.png

代码实现

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;
}