• 谷粒商城 高级篇 (九) --------- 缓存失效问题



    先来解决大并发读情况下的缓存失效问题。

    一、缓存穿透

    缓存穿透是指查询一个一定不存在的数据,由于缓存是不命中,将去查询数据库,但是数据库也无此记录,我们没有将这次查询的 null 写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义。

    在流量大时,可能 DB 就挂掉了,要是有人利用不存在的 key 频繁攻击我们的应用,这就是漏洞。

    解决:

    缓存空结果、并且设置短的过期时间。

    在这里插入图片描述

    二、缓存雪崩

    缓存雪崩是指在我们设置缓存时采用了相同的过期时间,导致缓存在某一时刻同时失效,请求全部转发到 DB,DB 瞬时压力过重雪崩。

    解决:

    原有的失效时间基础上增加一个随机值,比如 1-5 分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。

    在这里插入图片描述

    三、缓存击穿

    对于一些设置了过期时间的 key,如果这些 key 可能会在某些时间点被超高并发地访问,是一种非常“热点”的数据。

    这个时候,需要考虑一个问题:如果这个 key 在大量请求同时进来前正好失效,那么所有对这个 key 的数据查询都落到 db,我们称为缓存击穿。

    解决:

    加锁

    在这里插入图片描述

    四、本地加锁解决缓存击穿问题

    // 1. 空结果缓存, 解决缓存穿透
    // 2. 设置过期时间(加随机值), 解决缓存雪崩
    // 3. 加锁, 解决缓存击穿
    @Override
    public Map<String, List<Catelog2Vo>> getCatelogJson() {
        // 给缓存中放 json 字符串, 拿出的 json 字符串 , 还能逆转为 能用的对象 [序列化与反序列化]
        // 1. 加入缓存逻辑 缓存中存的数据是 json 字符串
        // json 跨语言、跨平台兼容
        String catelogJSON = redisTemplate.opsForValue().get("catelogJSON");
        if (StringUtils.isEmpty(catelogJSON)) {
            // 2. 缓存中没有, 查询数据库
            Map<String, List<Catelog2Vo>> catelogJsonFromDB = getCatalogJsonFromDB();
    
            // 3. 查到的数据再放入缓存, 将对象转为 json 放在缓存中
            
    
            return catelogJsonFromDB;
        }
    
        // 转为指定的对象
        Map<String, List<Catelog2Vo>> result = JSON.parseObject(catelogJSON, new TypeReference<Map<String, List<Catelog2Vo>>>(){});
        return  result;
    }
    
    public  Map<String, List<Catelog2Vo>> getCatalogJsonFromDB() {
        
        
        // 只要是同一把锁, 就能锁住需要这个锁的所有线程
        // 1. synchronized (this) : SpringBoot 所有的组件在容器中都是单例的
        //1. 查出所有 1 级分类
       
       
       synchronized (this) {
           
           // 得到锁以后, 我们应该再去缓存中确定一次, 如果没有才需要继续查询
           // TODO 本地锁 synchronized , JUC(lock) 分布式场景下想要锁住所有 需要分布式锁
    
           String catelogJSON = redisTemplate.opsForValue().get("catelogJSON");
           if (!StringUtils.isEmpty(catelogJSON)) {
               // 缓存不为 null 直接返回
               Map<String, List<Catelog2Vo>> result = JSON.parseObject(catelogJSON, new TypeReference<Map<String, List<Catelog2Vo>>>(){});
               return  result;
           }
           List<CategoryEntity> level1Categorys = getLevel1Categorys();
    
           //2. 封装数据
           Map<String, List<Catelog2Vo>> parent_cid = level1Categorys.stream().collect(Collectors.toMap(k -> k.getCatId().toString(), v -> {
    
               // 1. 每一个一级分类, 查到这个一级分类的二级分类
               List<CategoryEntity> categoryEntities = baseMapper.selectList(new QueryWrapper<CategoryEntity>().eq("parent_cid", v.getCatId()));
    
               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());
                       // 找当前二级分类的三级分类封装成vo
                       List<CategoryEntity> level3Catelog = baseMapper.selectList(new QueryWrapper<CategoryEntity>().eq("parent_cid", l2.getParentCid()));
                       if (level3Catelog != null) {
                           List<Catelog2Vo.Category3Vo> collect = level3Catelog.stream().map(l3 -> {
                               // 封装成指定格式
                               Catelog2Vo.Category3Vo category3Vo = new Catelog2Vo.Category3Vo(l2.getCatId().toString(), l3.getCatId().toString(), l3.getName());
                               return category3Vo;
                           }).collect(Collectors.toList());
                           catelog2Vo.setCatalog3List(collect);
    
                       }
                       return catelog2Vo;
                   }).collect(Collectors.toList());
               }
               return catelog2Vos;
    
           }));
    
           String s = JSON.toJSONString(parent_cid);
           redisTemplate.opsForValue().set("catelogJSON", s, 1, TimeUnit.DAYS);
           return parent_cid;
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
  • 相关阅读:
    SpringBoot采用Dynamic-Datasource方式实现多JDBC数据源
    Elasticsearch 8.X 如何生成 TB 级的测试数据 ?
    5-羧基四甲基罗丹明TMR标记Polyacetal聚缩醛/HA透明质酸纳米载体TMR--Polyacetal|TMR-HA
    SQL语句,存储过程,触发器
    【PyTorch深度学习项目实战100例】—— 基于MnasNet实现垃圾分类任务 | 第47例
    java---贪心---货仓选址(每日一道算法2022.11.5)
    【PAT甲级】1146 Topological Order
    【图像检测】基于Itti模型实现图像显著性检测附matlab代码
    Qt 集成OSG
    vue3中vue-pdf-embed实现放大、缩小、上一页、下一页、滚动翻页功能(pdf文件预览)
  • 原文地址:https://blog.csdn.net/m0_51111980/article/details/126840126