缓存使用[新人创作礼]

146 阅读3分钟

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

那些数据适合放入缓存?

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

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

本地缓存:

本地缓存可以在单体应用中使用,如果分布式的就会出现问题。

分布式缓存-本地模式在分布式下的问题

本地缓存模式下的分布式有问题,会造成缓存不统一,所以,不应该再使用本地模式的缓存

分布式缓存: file

分布式模式下,所有的微服务都共用同一个缓存中间件。

三、整合redis#

1、引入redis#

gulimall-product/pom.xml中引入redis

 <!-- 引入redis -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>

gulimall-product/src/main/resources/application.yml 文件中配置redis:

spring:
  datasource:
    username: root
    password: root
    url: jdbc:mysql://192.168.10.10:3306/gulimall_pms
    driver-class-name: com.mysql.cj.jdbc.Driver
  #  配置nacos注册中心
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
  jackson:
    date-format: yyyy-MM-dd HH:mm:ss
  thymeleaf:
    cache: false  # 调试期间,关闭缓存
  redis:
    host: 192.168.10.10
    prot: 6379

单元测试:

@RunWith(SpringRunner.class)
@SpringBootTest
public class GulimallProductApplicationTests {
  @Autowired
  StringRedisTemplate stringRedisTemplate;
​
    @Test
  public void redisTest(){
    ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
    ops.set("hello", "lily");
​
    String key = "hello";
    System.out.println(ops.get(key));
  }
    }

gulimall/product/service/impl/CategoryServiceImpl.java /** * 从数据库查询并封装数据::分布式锁 * @return */ public Map<String, List> getCatalogJsonFromDbWithRedisLock() {

    //1、占分布式锁。去redis占坑      设置过期时间必须和加锁是同步的,保证原子性(避免死锁)
    String uuid = UUID.randomUUID().toString();
    Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent("lock", uuid,300,TimeUnit.SECONDS);
    if (lock) {
        System.out.println("获取分布式锁成功...");
        Map<String, List<Catelog2Vo>> dataFromDb = null;
        try {
            //加锁成功...执行业务
            dataFromDb = getDataFromDb();
        } finally {
            String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";

            //删除锁
            stringRedisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class), Arrays.asList("lock"), uuid);

        }
        //先去redis查询下保证当前的锁是自己的
        //获取值对比,对比成功删除=原子性 lua脚本解锁
        // String lockValue = stringRedisTemplate.opsForValue().get("lock");
        // if (uuid.equals(lockValue)) {
        //     //删除我自己的锁
        //     stringRedisTemplate.delete("lock");
        // }

        return dataFromDb;
    } else {
        System.out.println("获取分布式锁失败...等待重试...");
        //加锁失败...重试机制
        //休眠一百毫秒
        try { TimeUnit.MILLISECONDS.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); }
        return getCatalogJsonFromDbWithRedisLock();     //自旋的方式
    }
}

private Map<String, List<Catelog2Vo>> getDataFromDb() {
    //得到锁以后,我们应该再去缓存中确定一次,如果没有才需要继续查询
    String catalogJson = stringRedisTemplate.opsForValue().get("catalogJson");
    if (!StringUtils.isEmpty(catalogJson)) {
        //缓存不为空直接返回
        Map<String, List<Catelog2Vo>> result = JSON.parseObject(catalogJson, new TypeReference<Map<String, List<Catelog2Vo>>>() {
        });

        return result;
    }

    System.out.println("查询了数据库");

    /**
     * 将数据库的多次查询变为一次
     */
    List<CategoryEntity> selectList = this.baseMapper.selectList(null);

    //1、查出所有分类
    //1、1)查出所有一级分类
    List<CategoryEntity> level1Categorys = getParent_cid(selectList, 0L);

    //封装数据
    Map<String, List<Catelog2Vo>> parentCid = level1Categorys.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(l2 -> {
                Catelog2Vo catelog2Vo = new Catelog2Vo(v.getCatId().toString(), null, l2.getCatId().toString(), l2.getName().toString());

                //1、找当前二级分类的三级分类封装成vo
                List<CategoryEntity> level3Catelog = getParent_cid(selectList, l2.getCatId());

                if (level3Catelog != null) {
                    List<Catelog2Vo.Category3Vo> category3Vos = level3Catelog.stream().map(l3 -> {
                        //2、封装成指定格式
                        Catelog2Vo.Category3Vo category3Vo = new Catelog2Vo.Category3Vo(l2.getCatId().toString(), l3.getCatId().toString(), l3.getName());

                        return category3Vo;
                    }).collect(Collectors.toList());
                    catelog2Vo.setCatalog3List(category3Vos);
                }

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

        return catelog2Vos;
    }));

    //3、将查到的数据放入缓存,将对象转为json
    String valueJson = JSON.toJSONString(parentCid);
    stringRedisTemplate.opsForValue().set("catalogJson", valueJson, 1, TimeUnit.DAYS);

    return parentCid;
}