Reids( 缓存篇 ==> 缓存穿透解决方案

缓存穿透的常见解决方案有两种:

  • 缓存空值
  • 布隆过滤器

一、缓存空值

当客户端发送的请求在Reids缓存中和数据库中都查询失败,我们设置一个空值缓存起来,这样即使用户访问了不存在的数据,也不会让请求直接穿透的数据库上。

注意:缓存空值的过期时间不宜太长,假设我们之后真的添加了新数据,也不会造成过久的数据不一致问题。

                                 Reids( 缓存篇 ==> 缓存穿透解决方案_第1张图片

缓存空值方案的优点与缺点:

缺点:

  • 缓存空值需要消耗额外的内存
  • 短期内数据不一致

优点:

  • 实现简单,我们只需要在数据库查询失败后添加个空值缓存即可

实现思路:

1、从Redis中查询信息

2、击中缓存了直接返回即可

3、击中空值直接返回错误信息

4、缓存没击中,查询数据库

5、数据库查询失败,说明当前所查询信息不存在,缓存一个空值到Redis(同时设置较短的过期时间)

6、数据库查询成功,添加缓存并返回

核心代码如下:

Reids( 缓存篇 ==> 缓存穿透解决方案_第2张图片

 完整实现(带注释):

    //需求,通过ID查询店铺信息,并添加到缓存。需要保证数据一致性
    @Override
    public Result queryById(Long id) {
        //定义个cacheID作为key,后续方便使用
        String cacheId = CACHE_SHOP_KEY + id;

        //1、从Redis中查询商户信息
        String shopJson = stringRedisTemplate.opsForValue().get(cacheId);

        //2、击中了,转成对象直接返回
        if(StrUtil.isNotBlank(shopJson)){
            return Result.ok(JSONUtil.toBean(shopJson,Shop.class));
        }

        //3、击中了空值,直接返回
        //这里不能直接用shopJson.equals("")做为条件,因为第一次查询没有缓存任何数据,shopJson肯定是个null,null.equals()直接报错了
        //而上面的isNotBlank方法排除后只剩下null和""的两种情况,我们只需要再排除null的情况,剩下的就是""了。
        if(shopJson != null){
            return Result.fail(SHOP_ERROR);
        }

        //4、缓存没击中,去数据库查
        Shop shop = getById(id);

        //5、数据库里也没查到,返回空对象到Redis(防止缓存穿透)
        if(shop == null){
            stringRedisTemplate.opsForValue().set(cacheId,"");
            stringRedisTemplate.expire(cacheId,CACHE_NULL_TTL,TimeUnit.MINUTES);
            return Result.fail(SHOP_ERROR);
        }

        //6、添加数据到redis缓存
        stringRedisTemplate.opsForValue().set(cacheId,JSONUtil.toJsonStr(shop));

        //7、设置过期时间,30分钟
        stringRedisTemplate.expire(cacheId,CACHE_SHOP_TTL, TimeUnit.MINUTES);

        //8、返回数据
        return Result.ok(shop);

    }

你可能感兴趣的:(Redis,SpringBoot,缓存)