文章目录
- 1、步骤
- 2、具体过程
- 1、引入pom依赖
- 2、修改配置文件
- 3、单元测试
- 4、测试结果
- 3、redis运行情况
- 4、项目中实际应用
- 5、加锁解决缓存击穿问题
- 代码一(存在问题)
- 代码二(问题解决)
- 6、新问题
- 7、分布式锁
1、步骤
前提条件:已经安装了Redis
- 1、pom中引入依赖
- 2、配置文件中配置
- 3、项目中使用
2、具体过程
1、引入pom依赖
版本由父工程管理
<!--引入redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2、修改配置文件
spring:
redis:
host: 192.168.202.211
port: 6379
3、单元测试
这里有关stringRedisTemplate
的使用、请自行查阅
@Autowired
StringRedisTemplate stringRedisTemplate;
@Test
public void testRedis(){
ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
//保存
ops.set("hello", UUID.randomUUID().toString());
//查询
String hello = ops.get("hello");
System.out.println("之前保存的数据是:"+hello);
}
4、测试结果
3、redis运行情况
我这里用docker安装redis、查看容器运行情况
4、项目中实际应用
代码逻辑
测试
访问接口数据
redis可视化工具查看
5、加锁解决缓存击穿问题
代码一(存在问题)
@Override
@Cacheable(value = {"category"},key = "#root.methodName",sync = true)
public Map<String, List<Catalog2Vo>> getCatalogJsonDbWithSpringCache() {
//1、缓存中放入json字符串,拿出json字符串,需要逆转为能用的对象类型【序列化和反序列化】
String catalogJSON = stringRedisTemplate.opsForValue().get("catalogJSON");
if(StringUtils.isEmpty(catalogJSON)){
//2、缓存中没有,查询数据库
Map<String, List<Catalog2Vo>> calogJsonFromDb = getCategoriesDb();
//3、将查到的数据放入缓存,将对象转为json放在缓存中
String s = JSON.toJSONString(calogJsonFromDb);
stringRedisTemplate.opsForValue().set("catalogJSON",s,1, TimeUnit.DAYS);
}
System.out.println("直接取的缓存数据");
//转为指定的对象
Map<String, List<Catalog2Vo>> result = JSON.parseObject(catalogJSON,new TypeReference<Map<String, List<Catalog2Vo>>>(){});
return result;
}
//从数据库中查出三级分类
private Map<String, List<Catalog2Vo>> getCategoriesDb() {
synchronized (this){
//得到锁以后,应该再去缓存中确定一次,如果缓存中没有需要继续查询
String catalogJSON = stringRedisTemplate.opsForValue().get("catalogJSON");
if(!StringUtils.isEmpty(catalogJSON)){
//缓存中存在数据、直接返回
Map<String, List<Catalog2Vo>> result = JSON.parseObject(catalogJSON,new TypeReference<Map<String, List<Catalog2Vo>>>(){});
return result;
}
System.out.println("缓存中没有数据,查询了数据库");
//优化业务逻辑,仅查询一次数据库
List<CategoryEntity> categoryEntities = this.list();
//查出所有一级分类
List<CategoryEntity> level1Categories = getCategoryByParentCid(categoryEntities, 0L);
Map<String, List<Catalog2Vo>> listMap = level1Categories.stream().collect(Collectors.toMap(k->k.getCatId().toString(), v -> {
//遍历查找出二级分类
List<CategoryEntity> level2Categories = getCategoryByParentCid(categoryEntities, v.getCatId());
List<Catalog2Vo> catalog2Vos=null;
if (level2Categories!=null){
//封装二级分类到vo并且查出其中的三级分类
catalog2Vos = level2Categories.stream().map(cat -> {
//遍历查出三级分类并封装
List<CategoryEntity> level3Catagories = getCategoryByParentCid(categoryEntities, cat.getCatId());
List<Catalog2Vo.Catalog3Vo> catalog3Vos = null;
if (level3Catagories != null) {
catalog3Vos = level3Catagories.stream()
.map(level3 -> new Catalog2Vo.Catalog3Vo(level3.getParentCid().toString(), level3.getCatId().toString(), level3.getName()))
.collect(Collectors.toList());
}
Catalog2Vo catalog2Vo = new Catalog2Vo(v.getCatId().toString(), cat.getCatId().toString(), cat.getName(), catalog3Vos);
return catalog2Vo;
}).collect(Collectors.toList());
}
return catalog2Vos;
}));
return listMap;
}
}
使用jmeter对其进行压测
查看控制台情况,理想情况是数据库只查询一次。实际上查询了多次,出现这个问题的原因就是一个用户查询完数据后,就释放了锁。还未将数据写入缓存的时候,第二个用户又拿到了锁。这个时候缓存中还未进行数据缓存,导致再次查询数据库。需要优化代码逻辑
代码二(问题解决)
优化代码逻辑,压测过程同上。
6、新问题
本地锁在分布式情况下是锁不住的
7、分布式锁
待编辑