• 谷粒商城 高级篇 (八) --------- 缓存使用



    一、本地缓存与分布式缓存

    本地缓存模式:不同服务器有着不同的缓存,出现缓存一致性的问题

    在这里插入图片描述
    分布式缓存:缓存统一放到缓存中间件中

    在这里插入图片描述

    二、缓存使用

    为了系统性能的提升,我们一般都会将部分数据放入缓存中,加速访问。而 db 承担数据落盘工作。
    哪些数据适合放入缓存?

    • 即时性、数据一致性要求不高的
    • 访问量大且更新频率不高的数据 (读多,写少)

    举例:电商类应用,商品分类,商品列表等适合缓存并加一个失效时间 (根据数据更新频率来定),后台如果发布一个商品,买家需要 5 分钟才能看到新的商品一般还是可以接受的。

    在这里插入图片描述

    data = cache.load(id);//从缓存加载数据
    If(data == null){
    	data = db.load(id);//从数据库加载数据
    	cache.put(id,data);//保存到 cache 中
    }
    return data;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    注意:在开发中,凡是放入缓存中的数据我们都应该指定过期时间,使其可以在系统即使没有主动更新数据也能自动触发数据加载进缓存的流程。避免业务崩溃导致的数据永久不一致问题。

    三、整合 redis 作为缓存

    A、引入 redis-starter

    <dependency>
    	<groupId>org.springframework.bootgroupId>
    	<artifactId>spring-boot-starter-data-redisartifactId>
    dependency>
    
    • 1
    • 2
    • 3
    • 4

    B、配置 redis

    spring:
      redis:
    	host: 192.168.56.10	
    	port: 6379
    
    • 1
    • 2
    • 3
    • 4

    C、使用 RedisTemplate 操作 redis

    @Autowired
    StringRedisTemplate stringRedisTemplate;
    @Test
    public void testStringRedisTemplate(){
    	ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
    	ops.set("hello","world_"+ UUID.randomUUID().toString());
    	String hello = ops.get("hello");
    	System.out.println(hello);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

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

    四、改造三级分类的业务

    // TODO 产生堆外内存溢出 OutOfDirectMemoryError
    // 1) springboot 2.0 之后默认使用 lettuce 作为 操作 redis 的客户端, 他使用 netty 进行网络通信
    // 2) lettuce 的 bug 导致 netty 堆外内存溢出 -Xmx 300m netty 如果没有指定堆外内存 默认使用 -Xmx300m 作为堆外内存
    // 可以 通过 -Dio.netty.maxDirectMemory 进行设置
    // 解决方案:
    //    1、升级 lettuce 客户端
    //    2、切换使用 jedis
    @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 放在缓存中
            String s = JSON.toJSONString(catelogJsonFromDB);
            redisTemplate.opsForValue().set("catelogJSON", s);
    
            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. 查出所有 1 级分类
       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;
    
       }));
      return parent_cid;
    }
    
    @Override
    public List<CategoryEntity> getLevel1Categorys() {
        List<CategoryEntity> categoryEntities = baseMapper.selectList(new QueryWrapper<CategoryEntity>().eq("parent_cid", 0));
        return categoryEntities;
    }
    
    
    • 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

    五、切换使用 Jedis

    <dependency>
    	<groupId>org.springframework.bootgroupId>
    	<artifactId>spring-boot-starter-data-redisartifactId>
    	<exclusions>
    		<exclusion>
    			<groupId>io.lettucegroupId>
    			<artifactId>lettuce-coreartifactId>
    		exclusion>
    	exclusions>
    dependency>
    <dependency>
    	<groupId>redis.clientsgroupId>
    	<artifactId>jedisartifactId>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
  • 相关阅读:
    30分钟入门前端容器化
    【C语言从0到1之数据类型】
    C++:添加头文件时出错C3646和C4430
    LeetCode 20.有效的括号
    【观察】从广州白云精“绣”“智”理实践,看AI赋能智慧城市升级正当时
    快速上手Vue3
    Linux下c++串口编程
    量子计算的奥秘与魅力:开启未来科技的钥匙(详解)
    py12_[接口全网最通俗易懂的一篇] Python 真假接口/抽象类/抽象方法/多态/实例级别解析
    【Spring】DI依赖注入
  • 原文地址:https://blog.csdn.net/m0_51111980/article/details/126836533