谷粒商城p141

  • Post author:
  • Post category:其他




p141 压力测试

在这里插入图片描述

在这里插入图片描述



p142 jmeter

在这里插入图片描述

在这里插入图片描述



p143 jmeter在windows下的端口被占用bug

因为端口没有及时回收导致的,具体看视频



p144 堆内存与垃圾回收

在这里插入图片描述



p145jconsole与jvisualvm

jconsole简单试了下 还是主要以jvisualvm为主

都是直接cmd启动即可,敲jvisualvm

在这里插入图片描述



p146 测试中间件性能

docker stats查看容器内存

压测nginx

在这里插入图片描述

在这里插入图片描述

可以看出中间件越多,性能越低

在这里插入图片描述



p147优化吞吐量

添加索引

在这里插入图片描述

可以看到设置索引后可以增加吞吐量,没加索引之前只有2,现在是12

在这里插入图片描述

也可以设置开启thymelead缓存,提高日志打印级别,都可以增加吞吐量



p148 nginx动静分离

把静态文件从static中放到nginx目录下

在这里插入图片描述

在这里插入图片描述

nginx配置静态文件路径,使得html上的静态文件请求直接到nginx,不用再去访问tomcat

在这里插入图片描述

在这里插入图片描述



p149调整jvm参数

在这里插入图片描述



p150 性能优化 优化三级分类

优化后吞吐量到80个,之前只有2个,因为之前是双重循环查库

 private List<CategoryEntity> getLevelCategorys(List<CategoryEntity> categoryEntitys,Long parentId){
        List<CategoryEntity> categoryEntities = categoryEntitys.stream().filter(s -> {
            return s.getParentCid() == parentId;
        }).collect(Collectors.toList());

        return categoryEntities;
    }
//todo 这是优化后的分类查询
    @Override
    public Map<Long, List<Catelog2Vo>> getCatelogJson() {
        //为了构造相应的json数据 类似一个map key放一级分类id,value放下面的二级分类的集合,二级分类里面又包括三级分类
        //先获取所有分类
        List<CategoryEntity> categoryEntities = baseMapper.selectList(new QueryWrapper<CategoryEntity>());

        List<CategoryEntity> level1Categorys = getLevelCategorys(categoryEntities,0L);
        Map<Long, List<Catelog2Vo>> map = level1Categorys.stream().collect(Collectors.toMap(CategoryEntity::getCatId, cate1 -> {
                    //查询该一级分类下的二级分类
                    List<CategoryEntity> level2Cates = getLevelCategorys(categoryEntities,cate1.getCatId());
                    List<Catelog2Vo> catelog2Vos = level2Cates.stream().map(l2 -> {
                        Catelog2Vo catelog2Vo = new Catelog2Vo();
                        catelog2Vo.setId(l2.getCatId().toString());
                        catelog2Vo.setCatalog1Id(cate1.getCatId().toString());
                        catelog2Vo.setName(cate1.getName());
                        //二级分类下面又有三级分类
                        List<CategoryEntity> level3Cates = getLevelCategorys(categoryEntities,l2.getCatId());
                        List<Catalog3Vo> catalog3Vos = level3Cates.stream().map(l3 -> {
                            Catalog3Vo catalog3Vo = new Catalog3Vo();
                            catalog3Vo.setId(l3.getCatId().toString());
                            catalog3Vo.setCatalog2Id(l2.getCatId().toString());
                            catalog3Vo.setName(l3.getName());
                            return catalog3Vo;
                        }).collect(Collectors.toList());
                        catelog2Vo.setCatalog3List(catalog3Vos);
                        return catelog2Vo;
                    }).collect(Collectors.toList());
                    return catelog2Vos;
                }
        ));
        return map;
    }



p151本地缓存与redis缓存

使用redis分布式缓存



p152 springboot整合redis

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
spring:
  redis:
    host: 192.168.56.10
    port: 6379

RedisAutoConfiguration中配置了bean

在这里插入图片描述

在这里插入图片描述



p153 加上缓存->改造三级分类

//todo 这是优化后的分类查询
    @Override
    public Map<Long, List<Catelog2Vo>> getCatelogJson() {
        //先从缓存获取
        String categoriesJson = redisTemplate.opsForValue().get("categoriesJson");
        if (StringUtils.isEmpty(categoriesJson)){
            Map<Long, List<Catelog2Vo>> catelogJsonFromDB = getCatelogJsonFromDB();
            String str = JSON.toJSONString(catelogJsonFromDB);
            redisTemplate.opsForValue().set("categoriesJson",str);
            return catelogJsonFromDB;
        }
        //有缓存 TypeReference是一个内部类,使用匿名内部类
        Map<Long, List<Catelog2Vo>> map = JSON.parseObject(categoriesJson, new TypeReference<Map<Long, List<Catelog2Vo>>>() {
        });
        return map;
    }



p154 redis客户端bug导致OutofDirectMemory

改造后 吞吐量加大了,

但是redis客户端lettuce有个bug

redisTemplate封转了redis底层客户端,包括jedis lettuce

所以我们改用redis另一个底层jedis

在这里插入图片描述

<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>



p155缓存穿透 雪崩 击穿

穿透:大量请求redis中不存在的key,导致缓存失效直接访问数据库,导致数据库崩溃

解决:缓存null数据,但是要记得设置短暂过期时间

雪崩:redis的大量key同时失效,大量请求访问数据库,导致数据库崩溃

解决:设置不同随机值,使缓存不在同一时间过期

在这里插入图片描述

击穿:大量请求访问某个热点key,在key失效时候,大量请求直接访问数据库,导致数据库崩溃

解决:加锁

在这里插入图片描述



p156 加锁(本地锁)解决缓存击穿问题

在这里插入图片描述

加锁,为了让在redis没有缓存的时候,只访问一次数据库,锁要注意原子性!!!

 //todo 这是优化后的分类查询
    @Override
    public Map<Long, List<Catelog2Vo>> getCatelogJson() {
        //先从缓存获取
        String categoriesJson = redisTemplate.opsForValue().get("categoriesJson");
        //没有缓存
        if (StringUtils.isEmpty(categoriesJson)){
            //查数据库
            Map<Long, List<Catelog2Vo>> catelogJsonFromDB = getCatelogJsonFromDB();

            return catelogJsonFromDB;
        }
        //有缓存 TypeReference是一个内部类,使用匿名内部类
        System.out.println("缓存命中!返回");
        Map<Long, List<Catelog2Vo>> map = JSON.parseObject(categoriesJson, new TypeReference<Map<Long, List<Catelog2Vo>>>() {
        });
        return map;
    }
private Map<Long, List<Catelog2Vo>> getCatelogJsonFromDB() {
        //为了构造相应的json数据 类似一个map key放一级分类id,value放下面的二级分类的集合,二级分类里面又包括三级分类
        //先获取所有分类
        synchronized (this){
            //再检查一次缓存是否有数据,可能上一个线程缓存了!!!!!!!!!!!!
            String categoriesJson = redisTemplate.opsForValue().get("categoriesJson");
            //不为空可以直接返回
            if (!StringUtils.isEmpty(categoriesJson)){
                System.out.println("缓存命中!返回");
                Map<Long, List<Catelog2Vo>> map = JSON.parseObject(categoriesJson, new TypeReference<Map<Long, List<Catelog2Vo>>>() {
                });
                return map;
            }
            //只有第一个线程会请求到数据库,前提是单体服务
            System.out.println("缓存不命中,查询数据库!");
            List<CategoryEntity> categoryEntities = baseMapper.selectList(new QueryWrapper<CategoryEntity>());

            List<CategoryEntity> level1Categorys = getLevelCategorys(categoryEntities,0L);
            Map<Long, List<Catelog2Vo>> map = level1Categorys.stream().collect(Collectors.toMap(CategoryEntity::getCatId, cate1 -> {
                        //查询该一级分类下的二级分类
                        List<CategoryEntity> level2Cates = getLevelCategorys(categoryEntities,cate1.getCatId());
                        List<Catelog2Vo> catelog2Vos = level2Cates.stream().map(l2 -> {
                            Catelog2Vo catelog2Vo = new Catelog2Vo();
                            catelog2Vo.setId(l2.getCatId().toString());
                            catelog2Vo.setCatalog1Id(cate1.getCatId().toString());
                            catelog2Vo.setName(cate1.getName());
                            //二级分类下面又有三级分类
                            List<CategoryEntity> level3Cates = getLevelCategorys(categoryEntities,l2.getCatId());
                            List<Catalog3Vo> catalog3Vos = level3Cates.stream().map(l3 -> {
                                Catalog3Vo catalog3Vo = new Catalog3Vo();
                                catalog3Vo.setId(l3.getCatId().toString());
                                catalog3Vo.setCatalog2Id(l2.getCatId().toString());
                                catalog3Vo.setName(l3.getName());
                                return catalog3Vo;
                            }).collect(Collectors.toList());
                            catelog2Vo.setCatalog3List(catalog3Vos);
                            return catelog2Vo;
                        }).collect(Collectors.toList());
                        return catelog2Vos;
                    }
            ));
            //这里一定要注意把redis缓存操作也放在同步代码块里!!!!!!!!!!!!
            String str = JSON.toJSONString(map);
            //给redis缓存一份
            redisTemplate.opsForValue().set("categoriesJson",str,1, TimeUnit.DAYS);

            return map;
        }

    }

在这里插入图片描述



p157本地锁在分布式环境下的问题

在这里插入图片描述

右键复制3个商品服务,模拟分布式环境
在这里插入图片描述

四个服务都查询了一次数据库,没有锁住

在这里插入图片描述

在这里插入图片描述



p158 分布式锁 原理与使用

原理,大家都去redis占坑 命令

set lock aaa nx

不存在这个key就能拿到锁,存在只能等待



第一阶段

在这里插入图片描述

在这里插入图片描述

解决:设置锁自动过期



第二阶段
在这里插入图片描述

在这里插入图片描述



阶段三


set lock aaa EX 300 NX

占坑同时指定过期时间

在这里插入图片描述

3



阶段四

删除锁也要保证原子性! 下面就是没有满足原子性,获取锁的时候,可能还是当前线程的锁,

但是准备去删除锁的时候,redis锁过期了,这时别的线程获取到了锁,当前线程就会把别的线程删除!!!

在这里插入图片描述

在这里插入图片描述



阶段五

在这里插入图片描述

判断是不是自己的锁和删除锁是原子性的

在这里插入图片描述


    //todo 这是优化后的分类查询
    @Override
    public Map<Long, List<Catelog2Vo>> getCatelogJson() {
        //先从缓存获取
        String categoriesJson = redisTemplate.opsForValue().get("categoriesJson");
        //没有缓存
        if (StringUtils.isEmpty(categoriesJson)){
            //查数据库
            Map<Long, List<Catelog2Vo>> catelogJsonFromDB = getCatalogJsonDbWithRedisLock();

            return catelogJsonFromDB;
        }
        //有缓存 TypeReference是一个内部类,使用匿名内部类
        System.out.println("缓存命中!返回");
        Map<Long, List<Catelog2Vo>> map = JSON.parseObject(categoriesJson, new TypeReference<Map<Long, List<Catelog2Vo>>>() {
        });
        return map;
    }

    /**
     * 分布式锁
     * @return
     */
    private Map<Long, List<Catelog2Vo>> getCatalogJsonDbWithRedisLock() {
        //为了构造相应的json数据 类似一个map key放一级分类id,value放下面的二级分类的集合,二级分类里面又包括三级分类
        //先获取所有分类
        //return getCateJsonFromDB();

        String uuid = UUID.randomUUID().toString();
        ValueOperations<String, String> ops = redisTemplate.opsForValue();
        //占锁
        Boolean lock = ops.setIfAbsent("lock", uuid,300, TimeUnit.SECONDS);
        if (lock) {
            System.out.println("获取到分布式锁");
            Map<Long, List<Catelog2Vo>> categoriesDb = getCateJsonFromDB();
            // get和delete原子操作
            String script = "if redis.call(\"get\",KEYS[1]) == ARGV[1] then\n" +
                    "    return redis.call(\"del\",KEYS[1])\n" +
                    "else\n" +
                    "    return 0\n" +
                    "end";
            //删锁
            redisTemplate.execute(
                    new DefaultRedisScript<Long>(script, Long.class), // 脚本和返回类型
                    Arrays.asList("lock"), // 参数
                    uuid); // 参数值,锁的值
            return categoriesDb;
        }else {
            System.out.println("没有获取到分布式锁,自旋等待...");
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 睡眠0.1s后,重新调用 //自旋
            return getCatalogJsonDbWithRedisLock();
        }
    }

    private Map<Long, List<Catelog2Vo>> getCateJsonFromDB() {
        //再检查一次缓存是否有数据,可能上一个线程缓存了!!!!!!!!!!!!
        String categoriesJson = redisTemplate.opsForValue().get("categoriesJson");
        //不为空可以直接返回
        if (!StringUtils.isEmpty(categoriesJson)) {
            System.out.println("缓存命中!返回");
            Map<Long, List<Catelog2Vo>> map = JSON.parseObject(categoriesJson, new TypeReference<Map<Long, List<Catelog2Vo>>>() {
            });
            return map;
        }
        //只有第一个线程会请求到数据库,前提是单体服务
        System.out.println("缓存不命中,查询数据库!");
        List<CategoryEntity> categoryEntities = baseMapper.selectList(new QueryWrapper<CategoryEntity>());

        List<CategoryEntity> level1Categorys = getLevelCategorys(categoryEntities, 0L);
        Map<Long, List<Catelog2Vo>> map = level1Categorys.stream().collect(Collectors.toMap(CategoryEntity::getCatId, cate1 -> {
                    //查询该一级分类下的二级分类
                    List<CategoryEntity> level2Cates = getLevelCategorys(categoryEntities, cate1.getCatId());
                    List<Catelog2Vo> catelog2Vos = level2Cates.stream().map(l2 -> {
                        Catelog2Vo catelog2Vo = new Catelog2Vo();
                        catelog2Vo.setId(l2.getCatId().toString());
                        catelog2Vo.setCatalog1Id(cate1.getCatId().toString());
                        catelog2Vo.setName(cate1.getName());
                        //二级分类下面又有三级分类
                        List<CategoryEntity> level3Cates = getLevelCategorys(categoryEntities, l2.getCatId());
                        List<Catalog3Vo> catalog3Vos = level3Cates.stream().map(l3 -> {
                            Catalog3Vo catalog3Vo = new Catalog3Vo();
                            catalog3Vo.setId(l3.getCatId().toString());
                            catalog3Vo.setCatalog2Id(l2.getCatId().toString());
                            catalog3Vo.setName(l3.getName());
                            return catalog3Vo;
                        }).collect(Collectors.toList());
                        catelog2Vo.setCatalog3List(catalog3Vos);
                        return catelog2Vo;
                    }).collect(Collectors.toList());
                    return catelog2Vos;
                }
        ));
        //这里一定要注意把redis缓存操作也放在同步代码块里!!!!!!!!!!!!
        String str = JSON.toJSONString(map);
        //给redis缓存一份
        redisTemplate.opsForValue().set("categoriesJson", str, 1, TimeUnit.DAYS);

        return map;
    }

在这里插入图片描述



p159 分布式锁 redisson

<dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.13.4</version>
        </dependency>
@Configuration
public class MyRedisConfig {

    @Bean(destroyMethod = "shutdown")
    public RedissonClient redison() {
        Config config = new Config();
        config.useSingleServer().setAddress("redis://192.168.56.101:6379");
        RedissonClient redissonClient = Redisson.create(config);
        return redissonClient;
    }
}
@Autowired
    RedissonClient redissonClient;

    @Test
    public void testredissonClient(){
        System.out.println(redissonClient);
    }



p160 Redisson-lock锁测试

redis有默认30s的过期时间,业务执行时间长时锁会自动续期

@RequestMapping("hello")
	@ResponseBody
	public String hello() {
		// 获取一级分类
        RLock lock = redissonClient.getLock("my-lock");
        lock.lock();
        try {
            System.out.println("获取到锁,执行业务..."+Thread.currentThread().getId());
            Thread.sleep(30000);
        }catch (Exception e){

        }finally {
            System.out.println("释放锁..."+Thread.currentThread().getId());
            lock.unlock();
        }
        return "hello";
	}



p161 redisson看门狗

在这里插入图片描述

虽然有看门狗原理,我们还是使用指定时间释放锁的方法



p162 163读写锁

最多等待一定时间的锁

在这里插入图片描述

公平锁,线程按顺序抢站锁

在这里插入图片描述

/**
     * 读写锁 读
     *
     * @return
     */
    @RequestMapping("readLock")
    @ResponseBody
    public String readLock() {
        RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("rw-lock");
        RLock rLock = readWriteLock.readLock();
        String writeValue = "";
        try {
            rLock.lock();
            writeValue = stringRedisTemplate.opsForValue().get("writeValue");
            Thread.sleep(30000);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            rLock.unlock();
        }

        return writeValue;
    }

    /**
     * 读写锁 写
     *
     * @return
     */
    @RequestMapping("writeLock")
    @ResponseBody
    public String writeLock() {
        RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("rw-lock");
        RLock rLock = readWriteLock.writeLock();
        try {
            rLock.lock();
            UUID uuid = UUID.randomUUID();
            stringRedisTemplate.opsForValue().set("writeValue", uuid.toString());
            Thread.sleep(30000);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            rLock.unlock();
        }

        return null;
    }

读锁是共享锁,写锁是排他锁

在这里插入图片描述

在这里插入图片描述



p164 信号量

信号量为0时候,park.acquire();会阻塞等待

可以改为

boolean b = park.tryAcquire();
 //信号量 获取一个信号量
    @RequestMapping("/park")
    @ResponseBody
    public String park() throws InterruptedException {
        RSemaphore park = redissonClient.getSemaphore("park");
        park.acquire();
        return "ok";
    }

    //信号量 释放一个信号量
    @RequestMapping("/go")
    @ResponseBody
    public String go() throws InterruptedException {
        RSemaphore park = redissonClient.getSemaphore("park");
        park.release();
        return "ok";
    }



p165 闭锁

数量减为0之后,await等待完成

 @RequestMapping("/lockDoor")
    @ResponseBody
    public String lockDoor() throws InterruptedException {
        RCountDownLatch door = redissonClient.getCountDownLatch("door");
        door.trySetCount(5);
        door.await();
        return "锁门了!";
    }

    @RequestMapping("/gogogo")
    @ResponseBody
    public String gogogo() throws InterruptedException {
        RCountDownLatch door = redissonClient.getCountDownLatch("door");
       door.countDown();//计数减一
        return "某班人走了!";
    }



p166缓存一致性问题

当修改数据库数据后,redis中的缓存怎么处理?有2种模式

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

如果想要及时获取最新数据,可以使用canal,canal是mysql的一个从服务器

在这里插入图片描述

在这里插入图片描述



获取分类数据 使用redisson分布式锁之后的代码,比我们之前自己用redis锁的简洁

//todo 这是优化后的分类查询
    @Override
    public Map<Long, List<Catelog2Vo>> getCatelogJson() {
        //先从缓存获取
        String categoriesJson = redisTemplate.opsForValue().get("categoriesJson");
        //没有缓存
        if (StringUtils.isEmpty(categoriesJson)){
            //查数据库
            Map<Long, List<Catelog2Vo>> catelogJsonFromDB = getCatalogJsonDbWithRedission();

            return catelogJsonFromDB;
        }
        //有缓存 TypeReference是一个内部类,使用匿名内部类
        System.out.println("缓存命中!返回");
        Map<Long, List<Catelog2Vo>> map = JSON.parseObject(categoriesJson, new TypeReference<Map<Long, List<Catelog2Vo>>>() {
        });
        return map;
    }


    /**
     * redisson分布式锁
     * @return
     */
    private Map<Long, List<Catelog2Vo>> getCatalogJsonDbWithRedission() {
        //为了构造相应的json数据 类似一个map key放一级分类id,value放下面的二级分类的集合,二级分类里面又包括三级分类
        //先获取所有分类

        RLock lock = redissonClient.getLock("categoriesJson-lock");
        lock.lock();
        Map<Long, List<Catelog2Vo>> categoriesDb = null;
        try {
            System.out.println("获取到分布式锁");
            categoriesDb = getCateJsonFromDB();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
        return categoriesDb;
    }

 private Map<Long, List<Catelog2Vo>> getCateJsonFromDB() {
        //再检查一次缓存是否有数据,可能上一个线程缓存了!!!!!!!!!!!!
        String categoriesJson = redisTemplate.opsForValue().get("categoriesJson");
        //不为空可以直接返回
        if (!StringUtils.isEmpty(categoriesJson)) {
            System.out.println("缓存命中!返回");
            Map<Long, List<Catelog2Vo>> map = JSON.parseObject(categoriesJson, new TypeReference<Map<Long, List<Catelog2Vo>>>() {
            });
            return map;
        }
        //只有第一个线程会请求到数据库,前提是单体服务
        System.out.println("缓存不命中,查询数据库!");
        List<CategoryEntity> categoryEntities = baseMapper.selectList(new QueryWrapper<CategoryEntity>());

        List<CategoryEntity> level1Categorys = getLevelCategorys(categoryEntities, 0L);
        Map<Long, List<Catelog2Vo>> map = level1Categorys.stream().collect(Collectors.toMap(CategoryEntity::getCatId, cate1 -> {
                    //查询该一级分类下的二级分类
                    List<CategoryEntity> level2Cates = getLevelCategorys(categoryEntities, cate1.getCatId());
                    List<Catelog2Vo> catelog2Vos = level2Cates.stream().map(l2 -> {
                        Catelog2Vo catelog2Vo = new Catelog2Vo();
                        catelog2Vo.setId(l2.getCatId().toString());
                        catelog2Vo.setCatalog1Id(cate1.getCatId().toString());
                        catelog2Vo.setName(cate1.getName());
                        //二级分类下面又有三级分类
                        List<CategoryEntity> level3Cates = getLevelCategorys(categoryEntities, l2.getCatId());
                        List<Catalog3Vo> catalog3Vos = level3Cates.stream().map(l3 -> {
                            Catalog3Vo catalog3Vo = new Catalog3Vo();
                            catalog3Vo.setId(l3.getCatId().toString());
                            catalog3Vo.setCatalog2Id(l2.getCatId().toString());
                            catalog3Vo.setName(l3.getName());
                            return catalog3Vo;
                        }).collect(Collectors.toList());
                        catelog2Vo.setCatalog3List(catalog3Vos);
                        return catelog2Vo;
                    }).collect(Collectors.toList());
                    return catelog2Vos;
                }
        ));
        //这里一定要注意把redis缓存操作也放在同步代码块里!!!!!!!!!!!!
        String str = JSON.toJSONString(map);
        //给redis缓存一份
        redisTemplate.opsForValue().set("categoriesJson", str, 1, TimeUnit.DAYS);

        return map;
    }



p167 SpringCache

简介

在这里插入图片描述

在这里插入图片描述



p168 整合springcache

<dependency>
            <groupId>org.springframework.b oot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>

配置文件

spring.cache.type=redis

启动类上加上注解

@EnableCaching

在这里插入图片描述

	@Override
    @Cacheable("category")
    public List<CategoryEntity> getLevel1Categorys() {
        System.out.println("获取分类数据");
        return baseMapper.selectList(new QueryWrapper<CategoryEntity>().eq("parent_cid",0));
    }

redis中有缓存了数据

在这里插入图片描述



p169 @Cacheable细节设置

设置缓存失效时间

spring.cache.redis.time-to-live=3600000

key可以为字符串

@Cacheable(value = "category",key = "'level1categories'")

key也可以用spel表达式 用当前方法名作为key

 @Cacheable(value = "category",key = "#root.method.name")

存储缓存的名字为value::key的格式

在这里插入图片描述



p170 自定义缓存配置类

主要是要熟悉自动配置原理,关于springboot的注解的使用,如@configrationProperties

@EnableConfigurationProperties(CacheProperties.class)//开启CacheProperties类的读取配置文件

spring.cache.type=redis
spring.cache.redis.time-to-live=3600000
#是否缓存空值
spring.cache.redis.cache-null-values=true
#指定key的前缀
spring.cache.redis.key-prefix=CACHR_
#是否使用前缀
spring.cache.redis.use-key-prefix=true

在这里插入图片描述

在这里插入图片描述


原理:我们定义一个配置类.把RedisCacheConfiguration 加入容器,在自动配置类里面就会使用我们配置的这个

RedisCacheConfiguration


配置类

@EnableConfigurationProperties(CacheProperties.class)//开启CacheProperties类的读取配置文件
@EnableCaching//启用缓存
@Configuration//这是一个配置类
public class MyCacheConfig {
    /**
     * 配置文件中 TTL设置没用上
     *
     * 原来:
     * @ConfigurationProperties(prefix = "spring.cache")
     * public class CacheProperties
     *
     * 现在要让这个配置文件生效	: @EnableConfigurationProperties(CacheProperties.class)
     *
     */
    @Bean
    RedisCacheConfiguration redisCacheConfiguration(CacheProperties cacheProperties){

        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();

        // 设置kv的序列化机制
        config = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()));
        //配置value为json形式
        config = config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
        CacheProperties.Redis redisproperties = cacheProperties.getRedis();

        // 设置配置
        if(redisproperties.getTimeToLive() != null){
            config = config.entryTtl(redisproperties.getTimeToLive());
        }
        if(redisproperties.getKeyPrefix() != null){
            config = config.prefixKeysWith(redisproperties.getKeyPrefix());
        }
        if(!redisproperties.isCacheNullValues()){
            config = config.disableCachingNullValues();
        }
        if(!redisproperties.isUseKeyPrefix()){
            config = config.disableKeyPrefix();
        }
        return config;
    }
}

在这里插入图片描述

在这里插入图片描述

redis自动配置源码:简要流程

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

补充上图的
在这里插入图片描述

在这里插入图片描述



p171 @CacheEvict修改数据后删除缓存

@Override
    @Cacheable(value = "category",key = "#root.method.name")
    public List<CategoryEntity> getLevel1Categorys() {
        System.out.println("获取一级分类数据");
        return baseMapper.selectList(new QueryWrapper<CategoryEntity>().eq("parent_cid",0));
    }

使用spring

@Cacheable

之后的查询分类数据,之前是我们手动缓存,但是之前我们有分布式锁,

使用注解后,默认不会给我们加锁同步查询数据库,想要使用加锁查询,解决缓存击穿的问题,要开启这个注解

sync=true

(这个锁用的是本地锁)

//todo 使用springCache缓存之后的三级分类查询
    @Override
    //todo 使用注解后,默认不会给我们加锁同步查询数据库,想要使用加锁查询,解决缓存击穿的问题,要开启这个注解 sync=true
    @Cacheable(value = "category",key = "#root.methodName",sync = true)
    public Map<Long, List<Catelog2Vo>> getCatelogJson() {
        System.out.println("缓存不命中,查询数据库!");
        List<CategoryEntity> categoryEntities = baseMapper.selectList(new QueryWrapper<CategoryEntity>());

        List<CategoryEntity> level1Categorys = getLevelCategorys(categoryEntities, 0L);
        Map<Long, List<Catelog2Vo>> map = level1Categorys.stream().collect(Collectors.toMap(CategoryEntity::getCatId, cate1 -> {
                    //查询该一级分类下的二级分类
                    List<CategoryEntity> level2Cates = getLevelCategorys(categoryEntities, cate1.getCatId());
                    List<Catelog2Vo> catelog2Vos = level2Cates.stream().map(l2 -> {
                        Catelog2Vo catelog2Vo = new Catelog2Vo();
                        catelog2Vo.setId(l2.getCatId().toString());
                        catelog2Vo.setCatalog1Id(cate1.getCatId().toString());
                        catelog2Vo.setName(cate1.getName());
                        //二级分类下面又有三级分类
                        List<CategoryEntity> level3Cates = getLevelCategorys(categoryEntities, l2.getCatId());
                        List<Catalog3Vo> catalog3Vos = level3Cates.stream().map(l3 -> {
                            Catalog3Vo catalog3Vo = new Catalog3Vo();
                            catalog3Vo.setId(l3.getCatId().toString());
                            catalog3Vo.setCatalog2Id(l2.getCatId().toString());
                            catalog3Vo.setName(l3.getName());
                            return catalog3Vo;
                        }).collect(Collectors.toList());
                        catelog2Vo.setCatalog3List(catalog3Vos);
                        return catelog2Vo;
                    }).collect(Collectors.toList());
                    return catelog2Vos;
                }
        ));

        return map;
    }

修改分类后,我们要删除缓存中分类的数据

@CacheEvict


    @Transactional
    @Override
    //修改分类后,把缓存中的所有分类分区的数据删除
    @CacheEvict(value = "category",allEntries = true)
    //或者使用Caching这个注解
//    @Caching(evict = {
//            @CacheEvict(value = "category",key = "'getLevel1Categorys'"),
//            @CacheEvict(value = "category",key = "'getCatelogJson'")
//    })
    public void updateDetail(CategoryEntity category) {
        this.updateById(category);
        if (StringUtils.isNotEmpty(category.getName())){
            categoryBrandRelationService.updateCate(category.getCatId(),category.getName());
        }
        //todo
        //其他关联
    }



p172 springcache不足

在这里插入图片描述



p173检索服务 搭建页面环境

静态文件放入nginx. index.html放入项目

   <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

在这里插入图片描述

在这里插入图片描述



174调整页面跳转

热启动

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

页面和js都有错误,需要修改



p175 检索服务 检索条件分析

在这里插入图片描述



p176返回结果分析



p177



版权声明:本文为weixin_44229645原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。