谷粒商城p141

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值