Redis 应用场景-> 缓存三大问题、分布式锁和本地锁

缓存Redis

当前的电商平台访问量高,导致请求常常超时
为了提升系统响应和用户体验,防止大量请求直接落到SQL服务器
选择Redis分布式缓存
这里需要注意:

  1. 缓存击穿
  2. 缓存雪崩
  3. 缓存穿透
    讨论了对应不同场景的各类解决方案,并将分布式锁和本地锁进行对比
    最终Redis展示如下:

缓存穿透

在这里插入图片描述

  /*
            对于缓存穿透,如果catalogJson为null,可以设置stringRedisTemplate.opsForValue().set("catalogJSON", null);
            */

缓存雪崩

在这里插入图片描述

            /*
            randomTime = Math.Random(0,1)
            对于缓存雪崩,可以设置stringRedisTemplate.opsForValue().set("catalogJSON", s, randomTime, TimeUnit.DAYS);
            */
    public Map<String, List<Catelog2Vo>> getCatelogJson2() {
        //加入缓存逻辑,缓存中存的数据是JSON字符串
        //JSON跨语言,跨平台兼容
        //从缓存中取出的数据要逆转为能用的对象类型,序列化与发序列化
        String catalogJSON = stringRedisTemplate.opsForValue().get("catalogJSON");
        if (StringUtils.isEmpty(catalogJSON)) {
            //缓存中没有数据,查询数据库 
            //这里使用了redis锁,先锁住
            Map<String, List<Catelog2Vo>> catelogJsonFromDb = getCatelogJsonFromDbWithRedisLock();
            //查到的数据再放入缓存,将对象转为JSON放入缓存中
            String s = JSON.toJSONString(catelogJsonFromDb);
            /*
            对于缓存穿透,如果catalogJson为null,可以设置stringRedisTemplate.opsForValue().set("catalogJSON", null);
            */
            /*
            randomTime = Math.Random(0,1)
            对于缓存击穿,可以设置stringRedisTemplate.opsForValue().set("catalogJSON", s, randomTime, TimeUnit.DAYS);
            */
            stringRedisTemplate.opsForValue().set("catalogJSON", s, 1, TimeUnit.DAYS);
            return catelogJsonFromDb;
        }
        Map<String, List<Catelog2Vo>> result = JSON.parseObject(catalogJSON, new TypeReference<Map<String, List<Catelog2Vo>>>() {
        });
        return result;
    }

缓存击穿

在这里插入图片描述

本地进程锁

    //本地进程锁
    public Map<String, List<Catelog2Vo>> getCatelogJsonFromDbWithLocalLock() {
        synchronized (this) {
            String catalogJSON = stringRedisTemplate.opsForValue().get("catalogJSON");
            if (StringUtils.isEmpty(catalogJSON)) {
                Map<String, List<Catelog2Vo>> result = JSON.parseObject(catalogJSON, new TypeReference<Map<String, List<Catelog2Vo>>>() {
                });
                return result;
            }
        }
        /**
         * 优化:将数据库中的多次查询变为一次,存至缓存selectList,需要的数据从list取出,避免频繁的数据库交互
         */
        List<CategoryEntity> selectList = baseMapper.selectList(null);
        //1.查出所有1级分类
        List<CategoryEntity> level1 = getParent_cid(selectList, 0L);
        //2.封装数据
        Map<String, List<Catelog2Vo>> parent_cid = level1.stream().collect(Collectors.toMap(k -> k.getCatId().toString(), v -> {
                    //1.查出1级分类中所有2级分类
                    List<CategoryEntity> categoryEntities = getParent_cid(selectList, v.getCatId());
                    //2.封装上面的结果
                    List<Catelog2Vo> catelog2Vos = null;
                    if (categoryEntities != null) {
                        catelog2Vos = categoryEntities.stream().map(l2 -> {
                            Catelog2Vo catelog2Vo = new Catelog2Vo(v.getCatId().toString(), null, l2.getCatId().toString(), l2.getName());
                            //查询当前2级分类的3级分类
                            List<CategoryEntity> level3 = getParent_cid(selectList, l2.getCatId());
                            if (level3 != null) {
                                List<Catelog2Vo.Catelog3Vo> collect = level3.stream().map(l3 -> {
                                    //封装指定格式
                                    Catelog2Vo.Catelog3Vo catelog3Vo = new Catelog2Vo.Catelog3Vo(l2.getCatId().toString(), l3.getCatId().toString(), l3.getName());
                                    return catelog3Vo;
                                }).collect(Collectors.toList());
                                catelog2Vo.setCatalog3List(collect);
                            }
                            return catelog2Vo;
                        }).collect(Collectors.toList());
                    }
                    return catelog2Vos;
                }
        ));
        String s = JSON.toJSONString(parent_cid);
        stringRedisTemplate.opsForValue().set("catalogJSON", s, 1, TimeUnit.DAYS);
        return parent_cid;
    }

分布式程锁Redis

    //分布式程锁Redis
    public Map<String, List<Catelog2Vo>> getCatelogJsonFromDbWithRedisLock() {
        //占分布式锁,同时设置锁过期时间,必须和加锁同步原子操作
        String uuid = UUID.randomUUID().toString();
        Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent("lock", "111", 300, TimeUnit.SECONDS);
        if (lock) {
            //加锁成功,执行业务
            Map<String, List<Catelog2Vo>> dataFromDB;
            try {
                dataFromDB = getDataFromDB();
            } finally {
                String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1])else return 0 end";
                //删除锁,Lua脚本
                Long lock1 = stringRedisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class), Arrays.asList("lock", uuid));
            }
            return dataFromDB;
        } else {
            //加锁失败,休眠2秒,重试
            try {
                Thread.sleep(2000);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return getCatelogJsonFromDbWithRedisLock();//自旋
        }
    }

    //分布式锁Redisson
    public Map<String, List<Catelog2Vo>> getCatelogJsonFromDbWithRedissonLock() {
        RLock lock = redisson.getLock("catalogJson-lock");
        lock.lock();
        //加锁成功,执行业务
        Map<String, List<Catelog2Vo>> dataFromDB;
        try {
            dataFromDB = getDataFromDB();
        } finally {
           lock.unlock();
        }
        return dataFromDB;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值