缓存穿透

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

Untitled

常见的解决方案有两种:

1. 缓存空对象 优点:实现简单,维护方便 缺点:额外内存消耗,可能造成短期的不一致 步骤:对于不存在key,我们缓存2分钟空对象

2. 布隆过滤 优点:内存占用较少,没有多余key 缺点:实现复杂,存在误判可能 步骤:通过布隆过滤器,确定对象是否存在

Untitled

缓存空对象流程图:

@GetMapping("/{id}")
public Result queryShopById(@PathVariable("id") Long id) {
    Shop shop;
    // 1. 查询redis缓存
    String str_shop = stringRedisTemplate.opsForValue().get(CACHE_SHOP_KEY + id);
    // 2. 判断是否存在,如果存在走缓存
    if (StrUtil.isNotBlank(str_shop)) {
        System.out.println("go cache " + id);
        shop = JSONUtil.toBean(str_shop, Shop.class);
        return Result.ok(shop);
    }
    // 6. 不存在,但是如果是空字符串,防止缓存穿透
    if("".equals(str_shop)) {
        return Result.fail("店铺不存在");
    }

    // 3. 不存在从数据库查询
    shop = shopService.getById(id);
    if(shop == null) {
        // 5. 防止缓存穿透
        stringRedisTemplate.opsForValue().set(CACHE_SHOP_KEY + id, "", CACHE_NULL_TTL, TimeUnit.MINUTES);
        return Result.fail("店铺不存在");
    }
    // 4. 在放到缓存中去
    str_shop = JSONUtil.toJsonStr(shop);
    stringRedisTemplate.opsForValue().set(CACHE_SHOP_KEY + id, str_shop, CACHE_SHOP_TTL, TimeUnit.MINUTES);
    return Result.ok(shop);
}

<aside> 💡 如果数据库不存在,防止缓存穿透设置对应的value为空 查找时,如果value是空,直接返回数据

</aside>

缓存穿透产生的原因是什么?

用户请求的数据在缓存中和数据库中都不存在,不断发起这样的请求,给数据库带来巨大压力

其他解决方案

缓存null值 布隆过滤 增强id的复杂度,避免被猜测id规律 做好数据的基础格式校验 加强用户权限校验 做好热点参数的限流

缓存雪崩

缓存雪崩是指在同一时段大量的缓存key同时失效或者Redis服务宕机,导致大量请求到达数据库,带来巨大压力。

Untitled

解决方案:

  1. 给不同的Key的TTL添加随机值
  2. 利用Redis集群提高服务的可用性
  3. 给缓存业务添加降级限流策略
  4. 给业务添加多级缓存

缓存击穿