Redis并发场景 以及 解决方法

现在 有 一些 分类的id 这个分类下有一些商品,这些商品又是经常不改动的,做一下缓存

这个店铺可能会有很多的分类,但是 比较火的的一些,我门希望尽量的长时间在缓存中,有热门的,就一定会有冷门的,冷门的建议就是 尽量时间短一些

1.可以使用冷热分离

怎么判断一个是热的还是冷的,和访问的频率相关,因此一行代码就可以解决,在获得缓存后,对缓存的数据进行(过期时间重置),expire        

2.可能 存在 缓存失效(击穿)
仅仅代表 缓存失效了,但是mysql还在,一般都是这样的情况,当一批数据 同时缓存到 redis,缓存时间都一样,在高峰时,这些数据 同时过期了,就会导致大量的请求到 mysql
这个 解决 很好解决,就是 在批量增加缓存时,设置过期时间为 基本时间+随机的过期时间

完美 解决

3.可能存在缓存 穿透(一个透==》整个后端都被 透了 ==》缓存和mysql兜底的都没了)

        例如,当前12点,很多人来买饭,都想点1号分类下的,但是不巧的是,后台管理把1号分类直接删除了(连带缓存),缓存直接透了,请求全都打到mysql上,可是mysql也没有这个数据,就会给mysql带来很大的压力(其实也没多大,毕竟加个索引,嘎嘎快),但是问题还得解决

        又或者,黑客找到了一个你mysql里面一定没有id,直接来个-1,疯狂发请求(直接100w次,应该没有这么离谱的),这个就和上面的一样了

         解决:  id是1 是吧,没是,请求一次后,我也把你缓存起来(嘿嘿),没有数据缓存个什么好呢,null感觉不太好,”{}“这个非常合适,我们存的就是json,这个太合适了,但是过期时间怎么说,要和哪些 正常的一样吗,(如果黑客不讲伍德 准备了一个小本本 写了10w个 奇奇怪 怪的id,来攻击),难道要将这10w个都缓存那么长时间,那可不得了,所以缓存时间短一点,这样感觉就很完美了,但是(黑客 忘记 关了 , 直接攻击了一下午,emmmm),无奈,只能在加个冷热分离了,呜呜呜

4. 疯狂小杨哥 说一个 冷门商品 ,直播间的人都疯狂 请求这个商品

       

if(缓存中有没有){
返回
}
// 这里 可能阻塞了 很多来构建的 线程
加锁
//为啥要再 判断 ,因为 有 一个线程构建完成后 ,剩余的来构建的线程 直接返回
    if(缓存中有没有){
    返回
    }
    构建缓存
解锁

        当然还有一些问题: DCL(双检查锁),就是建立缓存的时候,由于并发很高,很多个线程都来建立缓存,欧玛噶,明明我的意思是只想要一个线程来构建缓存,其它的线程先别急,如果是单机版的 synchronized 这个老大哥必须上场啊,synchronized (this),this是单例的,这不是很简单吗,但是这样真的可以吗,试想一下,显然不可以,怎么解决呐---》先学分布式锁吧^_^

       
        

        当然了大佬们肯定都会: 通过redison来 获得一个lock

RLock key = redisson.getLock("lockKey"+id);

每一个 商品的 id 都有一个对应的锁 这样就 差不多

    public List<DishVO> listWithFlavor(Dish dish) {
        if (dish.getCategoryId() == null) {
            return null;
        }
        
        //加入缓存
        ListOperations<String, String> stringStringListOperations = stringRedisTemplate.opsForList();
        List<String> range = stringStringListOperations.range(CATEGORY_SHOP + dish.getCategoryId(), 0, -1);
        List<DishVO> collect = range.stream().map(s -> JSONObject.parseObject(s, DishVO.class)).collect(Collectors.toList());

        if (range.size() > 0 && !range.get(0).equals(REDIS_CACHE_NULL)) {
            //实现冷热分离
            stringRedisTemplate.expire(CATEGORY_SHOP + dish.getCategoryId(), 24, TimeUnit.HOURS);
            return collect;
        } else if (range.size() > 0 && range.get(0).equals(REDIS_CACHE_NULL)) {
            //去get了一个空的数据
            //防止黑客的长时间 攻击
            stringRedisTemplate.expire(CATEGORY_SHOP + dish.getCategoryId(), 1, TimeUnit.HOURS);
            return null;
        } else {
            List<Dish> dishList = dishMapper.list(dish);

            List<DishVO> dishVOList = new ArrayList<>();

            for (Dish d : dishList) {
                DishVO dishVO = new DishVO();
                BeanUtils.copyProperties(d, dishVO);

                //根据菜品id查询对应的口味
                List<DishFlavor> flavors = dishFlavorMapper.getFlavorByDishId(d.getId());

                dishVO.setFlavors(flavors);
                dishVOList.add(dishVO);
            }

            if (dishVOList.size() == 0) {
                //当前 分类下 就没有商品
                stringStringListOperations.leftPushAll(CATEGORY_SHOP + dish.getCategoryId(), REDIS_CACHE_NULL);
                stringRedisTemplate.expire(CATEGORY_SHOP + dish.getCategoryId(), 1, TimeUnit.HOURS);
                return null;
            } else {
                List<String> collect1 = dishVOList.stream().map(dishVO -> JSONObject.toJSONString(dishVO)).collect(Collectors.toList());
                //存入缓存
                stringStringListOperations.leftPushAll(CATEGORY_SHOP + dish.getCategoryId(), collect1);
                stringRedisTemplate.expire(CATEGORY_SHOP + dish.getCategoryId(), 24, TimeUnit.HOURS);
                return dishVOList;
            }

        }


    }

双写不一致问题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值