电商项目——缓存——第五章——中篇

电商项目——全文检索-ElasticSearch——第一章——中篇
电商项目——商城业务-商品上架——第二章——中篇
电商项目——商城业务-首页——第三章——中篇
电商项目——性能压测——第四章——中篇
电商项目——缓存——第五章——中篇
电商项目——商城业务-检索服务——第六章——中篇
电商项目——商城业务-异步——第七章——中篇
电商项目——商品详情——第八章——中篇
电商项目——认证服务——第九章——中篇
电商项目——购物车——第十章——中篇
电商项目——消息队列——第十一章——中篇
电商项目——订单服务——第十二章——中篇
电商项目——分布式事务——第十三章——中篇

1:缓存使用

1.1 本地缓存与分布式缓存

本地缓存与分布式缓存

1.2 整合redis测试

整合redis测试

1.3 改造三级分类业务

前面我们整合了redis,并且使用了stringRedisTemplate,现在我们来对如下的三级分类使用缓存进行业务优化
IndexController

    //以json格式返回
    @ResponseBody
    @GetMapping("index/json/catalog.json")
    public  Map<String, List<Catelog2Vo>> getCatalogJson(){
   
        Map<String, List<Catelog2Vo>> indexJson=categoryService.getCatalogJson();
        return indexJson;
    }

categoryServiceImpl

 @Override
    public Map<String, List<Catelog2Vo>> getCatalogJson() {
   
        //给缓存中放入json字符串,拿出json字符串,还要逆转为可用的对象类型【序列化,反序列化】
        /**
         * 由于网络传输,我们相当于给内存创建了一个对象catalogJsonFromDb,要存到redis里面,必须要整成一个可传输的流数据,从redis里面拿到这个串又的逆转为对象
         * 这就是序列化,反序列化
         */

        //1:加入缓存逻辑(缓存存放的数据是json字符串)
        //json跨语言,跨平台兼容
        //1.1判断redis中是否有catalogJSON数据
        String catalogJSON = stringRedisTemplate.opsForValue().get("catalogJSON");
        if (StringUtils.isEmpty(catalogJSON)){
   

            //1.2缓冲中没有,就查询数据库
            Map<String, List<Catelog2Vo>> catalogJsonFromDb = getCatalogJsonFromDb();
            //1.3 查到数据库在放入缓存
            String string = JSON.toJSONString(catalogJsonFromDb);
            stringRedisTemplate.opsForValue().set("catalogJSON",string);

            return catalogJsonFromDb;
        }

        Map<String, List<Catelog2Vo>> result = JSON.parseObject(catalogJSON, new TypeReference<Map<String, List<Catelog2Vo>>>() {
   
        });

        return result;
    }
  • json跨语言,跨平台兼容,比如做一个物流系统,使用php,它也要从缓存中获取数据,如果它拿到的是json数据,那么大家都一样都可以解析,如果存的是对象字节流,默认的是java序列化机制,php拿到的就是java序列化的流数据,那么php就不可以拿到这个流(没有java的系统),所以我们以后给redis都保存以json格式,全语言,全平台
  • 给缓存中放入json字符串,拿出json字符串,还要逆转为可用的对象类型【序列化,反序列化】
        /**
         * 由于网络传输,我们相当于给内存创建了一个对象catalogJsonFromDb,
         * 要存到redis里面,必须要整成一个可传输的流数据串,
         * 从redis里面拿到这个串又的逆转为对象
         * 这就是序列化,反序列化
         */

进行测试查看是否使用缓存性能会比,不使用缓存和直接每一次查询数据库的性能要好

查看是否可以成功获取数据
http://localhost:30000/index/json/catalog.json在这里插入图片描述
在这里插入图片描述

1.4 压力测试出现的内存泄露及解决

进行压力测试
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
产生如上的原因:如下
在这里插入图片描述
在这里插入图片描述
解决办法:
在这里插入图片描述
操作步骤:先排除掉redis里面的lettuce,在引入依赖jedis
mall-product

		<!--引入redis场景启动器-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-redis</artifactId>
			<exclusions>
				<exclusion>
					<groupId>io.lettuce</groupId>
					<artifactId>lettuce-core</artifactId>
				</exclusion>
			</exclusions>
		</dependency>

		<dependency>
			<groupId>redis.clients</groupId>
			<artifactId>jedis</artifactId>
		</dependency>

再次进行压力测试
得出如下对比数据
在这里插入图片描述
小知识:stringRedisTemplate和lettuce,jedis的关系
在这里插入图片描述
查看RedisAutoConfiguration 的源码,你会发现上面的@Import注解就是引入了Lettuce和Jedis

@Import({
    LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {
   
//.....

1.5 缓存击穿,穿透,雪崩

缓存击穿,穿透,雪崩

1.6 加锁解决缓存击穿问题

第一个方法:加如下代码块

     synchronized (){
   
            
        }

我们要去哪里加锁呢?我们要去查询数据库的时候加锁如下

   /**
         * 1:将数据库的多次查询变为1次
         * @return
         */
    //从数据库查询并且封装db的
    public Map<String, List<Catelog2Vo>> getCatalogJsonFromDb() {
   

        //只要是同一把锁,就可以锁住需要这个锁的所有线程
        /**
         * 1:synchronized (this){ Springboot所有组件在容器值都是单例的,加this可以
         */
        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>>>() {
   
                });
            }
            //将数据库的多次查询变为1次
            List<CategoryEntity> selectList = baseMapper.selectList(null);
            //1:查出所有1级分类
            List<CategoryEntity> level1Category = getParent_cid(selectList,0L);

            //2:封装数据
            Map<String, List<Catelog2Vo>> parent_cid = level1Category.stream().collect(Collectors.toMap(k -> k.getCatId().toString(), v -> {
   
                // 1.每一个的一级分类,查到这个一级分类的二级分类
                List<CategoryEntity> categoryEntities = getParent_cid(selectList,v.getCatId());
                //2:封装上面的结果
                List<Catelog2Vo> catelog2Vos = null;
                if (categoryEntities != null) {
   
                    catelog2Vos = categoryEntities.stream().map(item -> {
   
                        Catelog2Vo catelog2Vo = new Catelog2Vo(v.getCatId().toString(), null, item.getCatId().toString(), item.getName());

                        //1.给当前二级分类的三级分类封装成vo
                        List<CategoryEntity> level3Category = getParent_cid(selectList,item.getCatId());
                        //2:封装成指定格式

                        if (level3Category!=null){
   
                            List<Catelog2Vo.Catelog3Vo> collect = level3Category.stream().map(items -> {
   
                                Catelog2Vo.Catelog3Vo catelog3Vo = new Catelog2Vo.Catelog3Vo(item.getCatId().toString(),items.getCatId().toString(),items.getName());

                                return catelog3Vo;

                            }).collect(Collectors.toList());
                            catelog2Vo.setCatalog3List(collect);
                        }

                        return catelog2Vo;
                    }).collect(Collectors.toList());
                }

                return catelog2Vos;
            }));

            return parent_cid;
        }


    }

上面加锁如果是在单体应用的情况下是合适的,如果在分布式下该如何加锁呢?
在分布式下,我们会把商品服务放到好多台服务器,大并发100万个请求过来,由于负载均衡机制,每一个商品都接受了10000个请求,而我们加锁加的是this(当前实例对象,无论是给同步代码块,还是方法上加this),this只代表当前容器,如果有八台服务器就代表有八个实例,

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值