Redis框架(七):大众点评项目 缓存穿透、缓存击穿、缓存雪崩

时间:2022-12-13 17:53:06

SpringCloud章节复习已经过去,新的章节Redis开始了,这个章节中将会回顾Redis实战项目 大众点评
主要依照以下几个原则

  1. 基础+实战的Demo和Coding上传到我的代码仓库
  2. 在原有基础上加入一些设计模式,stream+lamdba等新的糖
  3. 通过DeBug调试,进入组件源码去分析底层运行的规则和设计模式

代码会同步在我的gitee中去,觉得不错的同学记得一键三连求关注,感谢:
Redis优化-链接: RedisThreeStrategiesProject
Redis框架(七):大众点评项目 缓存穿透、缓存击穿、缓存雪崩

需求:缓存穿透、缓存击穿、缓存雪崩

Redis框架(七):大众点评项目 缓存穿透、缓存击穿、缓存雪崩
Redis框架(七):大众点评项目 缓存穿透、缓存击穿、缓存雪崩

Redis框架(七):大众点评项目 缓存穿透、缓存击穿、缓存雪崩

处理策略

缓存穿透处理

  1. 布隆过滤器:本质上是一个概率运算器,可以将存储的数据分解成二进制数据,来进行推测传入数据是否存在,有一定不确定性,但是如果判断为不存在,则一定不存在;
  2. 添加Null值
    Redis框架(七):大众点评项目 缓存穿透、缓存击穿、缓存雪崩
        //处理缓存穿透: id范围就在0-10000
        if (id <= 0 || id> 10000) {
            return Result.fail("店铺类型不存在!");
        }

添加Null值

            if (shop == null) {
                //处理缓存穿透
                stringRedisTemplate.opsForValue().set(key, "",  RedisConstants.CACHE_NULL_TTL, TimeUnit.MINUTES);
                return Result.fail("店铺类型不存在!");
            }

判断Null值

        //处理缓存穿透: 如果为空值
        if (shopStr != null){
            return Result.fail("店铺类型不存在!");
        }

缓存雪崩

  1. 可以设置随机时间
  2. 设置异地多活,主从架构等方式
        //处理缓存雪崩: 随机时间 time * RedisConstants.CACHE_NULL_TTL
        Random random = new Random();
        Long time = random.nextLong();
                stringRedisTemplate.opsForValue().set(key, "", time *  RedisConstants.CACHE_NULL_TTL, TimeUnit.MINUTES);

缓存击穿

如果当前热点信息失效,大量访问就落在DB上,我们可以通过

  1. 互斥锁(setnx)
  2. 逻辑过期机制
    Redis框架(七):大众点评项目 缓存穿透、缓存击穿、缓存雪崩
        //互斥锁处理缓存击穿
        String lockKey = RedisConstants.LOCK_SHOP_KEY + id;
        try {
            if (!tryLock(stringRedisTemplate.opsForValue().setIfAbsent(key, "1", RedisConstants.LOCK_SHOP_TTL, TimeUnit.SECONDS))) {
                Thread.sleep(50);
                return queryById(id);
            }
            log.debug("lockKey" + lockKey);
            shop = getById(id);

            stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(shop),   RedisConstants.CACHE_NULL_TTL, TimeUnit.HOURS);

        } catch (InterruptedException e) {
            throw new RuntimeException(e);
            /*e.printStackTrace();*/
        }finally {
                //删除暂时的key, 释放互斥锁
            stringRedisTemplate.delete(lockKey);
        }

所有代码

   @Override
    public Result queryById(Long id) {


        //处理缓存穿透: id范围就在0-10000
        if (id <= 0 || id> 10000) {
            return Result.fail("店铺类型不存在!");
        }

        String key = RedisConstants.CACHE_SHOP_KEY + id;
        String shopStr = stringRedisTemplate.opsForValue().get(key);
        if(StrUtil.isNotBlank(shopStr)){
            Shop shop = JSONUtil.toBean(shopStr, Shop.class);
            return Result.ok(shop);
        }

        //处理缓存穿透: 如果为空值
        if (shopStr != null){
            return Result.fail("店铺类型不存在!");
        }



        //处理缓存雪崩: 随机时间 time * RedisConstants.CACHE_NULL_TTL
        Random random = new Random();
        Long time = random.nextLong();


        Shop shop = null;

        //互斥锁处理缓存击穿
        String lockKey = RedisConstants.LOCK_SHOP_KEY + id;
        try {
            if (!tryLock(lockKey)) {
                Thread.sleep(50);
                return queryById(id);
            }
            log.debug("lockKey" + lockKey);
            shop = getById(id);

            if (shop == null) {
                //处理缓存穿透
                stringRedisTemplate.opsForValue().set(key, "",  RedisConstants.CACHE_NULL_TTL, TimeUnit.MINUTES);
                return Result.fail("店铺类型不存在!");
            }

            stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(shop),   RedisConstants.CACHE_NULL_TTL, TimeUnit.HOURS);


        } catch (InterruptedException e) {
            throw new RuntimeException(e);
            /*e.printStackTrace();*/
        }finally {
                //删除暂时的key, 释放互斥锁
            stringRedisTemplate.delete(lockKey);
        }
        return Result.ok(shop);
    }

    public boolean tryLock(String key){

        Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", RedisConstants.LOCK_SHOP_TTL, TimeUnit.SECONDS);
        //这里不可以直接返回flag,因为在拆箱过程中可能出现flag为null的情况;
        return BooleanUtil.isTrue(flag);
    }

总结

Redis框架(七):大众点评项目 缓存穿透、缓存击穿、缓存雪崩
Redis框架(七):大众点评项目 缓存穿透、缓存击穿、缓存雪崩
Redis框架(七):大众点评项目 缓存穿透、缓存击穿、缓存雪崩
Redis框架(七):大众点评项目 缓存穿透、缓存击穿、缓存雪崩
Redis框架(七):大众点评项目 缓存穿透、缓存击穿、缓存雪崩

Redis框架(七):大众点评项目 缓存穿透、缓存击穿、缓存雪崩
Redis框架(七):大众点评项目 缓存穿透、缓存击穿、缓存雪崩
Redis框架(七):大众点评项目 缓存穿透、缓存击穿、缓存雪崩

Redis框架(七):大众点评项目 缓存穿透、缓存击穿、缓存雪崩