SpringBoot整合Redis、以及缓存穿透、缓存雪崩、缓存击穿的理解、如何添加锁解决缓存击穿问题?分布式情况下如何添加分布式锁
文章目录
-
- 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.211port: 6379
3、单元测试
这里有关stringRedisTemplate
的使用、请自行查阅
@AutowiredStringRedisTemplate stringRedisTemplate;@Testpublic 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、分布式锁
待编辑