缓存篇-Redis缓存失效以及解决方案


前言

提示:redis改造商品服务


一、缓存穿透

1.原因

缓存穿透是指缓存和数据库中都没有数据,用户不断发起请求,比如id卫负数或者id值特别大不存在的值,这时请求越过redis直接访问数据库,造成数据库压力过大。

2.解决方案

  • 缓存空对象
  • 布隆过滤器
  • mvc拦截

二、缓存雪崩

1.原因

缓存雪崩是指设置缓存时,redis中key设置了相同的过期时间,导致缓存在某一时刻同时失效,请求全部访问数据库,造成数据库短时间内压力过大,导致雪崩。

2.解决方案

  • 将缓存数据的过期时间设置为随机,防止同一时间大量数据过期。
  • 分布式部署的情况下,将热点数据均匀分布在不同的缓存数据库中
  • 设置热点数据永不过期
  • 出现雪崩进行服务降级、熔断

三、缓存击穿

1.缓存击穿和缓存雪崩的区别

  • 缓存击穿是指并发查同一条数据。指的是缓存中没有数据,但是数据库中有数据。这时由于并发用户特别多,同时读缓存没有读取到数据,又同时去数据库查询,引起数据库压力瞬间增大
  • 缓存雪崩是不同的数据都过期了,很多数据都查不到从而查数据库。

2.解决方案

  • 设置热点数据永不过期
  • 加锁。比较常用的做法是mutex。在缓存失效的时候,不立即去读取数据库。而是先使用缓存工具的某些带成功操作返回值的操作

1.加synchronized(this)锁的问题

会造成时序问题。

  • 时序问题:之前的逻辑是查缓存没有然后竞争锁查数据库,这样就会造成多次查库。

2.加synchronized(this)锁的解决方案

竞争到锁之后,再次确认缓存中有没有,再去查数据库
在这里插入图片描述

 public Map<String, List<Catelog2Vo>> getCatelogJson() {
     //加锁,只要是同一把锁就能锁住当前线程

   //从redis缓存中获取
      String catelogJson = stringRedisTemplate.opsForValue().get("catelogJson");
      if(StringUtils.isEmpty(catelogJson)){
          //如果不为空,就查库
          Map<String, List<Catelog2Vo>> catelogJsonFromDB = getCatelogJsonFromDB();
          return catelogJsonFromDB;
      }
      //否则 将redis中的json转换成Map类型进行返回
      return  JSON.parseObject(catelogJson,new TypeReference<Map<String, List<Catelog2Vo>>>(){});


 public Map<String, List<Catelog2Vo>> getCatelogJsonFromDB() {
    //加锁 锁住当前对象
    synchronized (this) {
        //线程进入之前,再次查缓存
        String catelogJson = stringRedisTemplate.opsForValue().get("catelogJson");
        if(StringUtils.isNotEmpty(catelogJson)){
            //如果缓存中有数据
            //解析成json返回
            return  JSON.parseObject(catelogJson,new TypeReference<Map<String, List<Catelog2Vo>>>(){});
        }
        //如果缓存中没有,就查库

        //查全部
        List<CategoryEntity> categoryEntities = baseMapper.selectList(null);
        //查询所有一级目录
        List<CategoryEntity> categoryLevel01 = getCategoryEntities(categoryEntities, 0L);
        Map<String, List<Catelog2Vo>> parent_cid = categoryLevel01.stream().collect(Collectors.toMap(k -> k.getCatId().toString(), v -> {
            //遍历 拿到每个一级分类的二级分类
            List<CategoryEntity> categoryLevel02 = getCategoryEntities(categoryEntities, v.getCatId());
            List<Catelog2Vo> catelog2Vos = null;
            if (categoryLevel02 != null) {
                catelog2Vos = (List<Catelog2Vo>) categoryLevel02.stream().map(l2 -> {
                    Catelog2Vo catelog2Vo = new Catelog2Vo(v.getCatId().toString(), l2.getName(), l2.getCatId().toString(), null);
                    //三级分类
                    List<CategoryEntity> categoryLevel03 = getCategoryEntities(categoryEntities, l2.getCatId());
                    if (categoryLevel03 != null) {
                        List<Catelog3Vo> catelog3Vo = categoryLevel03.stream().map(l3 -> new Catelog3Vo(l3.getCatId().toString(), l3.getName(), l2.getCatId().toString())).collect(Collectors.toList());
                        catelog2Vo.setCatelog3VoList(catelog3Vo);
                    }
                    return catelog2Vo;
                }).collect(Collectors.toList());
            }
            return catelog2Vos;
        }));

        //在返回数据之前,将数据放入缓存,保证原子性
        //将数据转换成json串保存在redis中
        stringRedisTemplate.opsForValue().set("catelogJson", JSON.toJSONString(parent_cid));
        return parent_cid;
    }
}
}

在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值