电商项目——全文检索-ElasticSearch——第一章——中篇
电商项目——商城业务-商品上架——第二章——中篇
电商项目——商城业务-首页——第三章——中篇
电商项目——性能压测——第四章——中篇
电商项目——缓存——第五章——中篇
电商项目——商城业务-检索服务——第六章——中篇
电商项目——商城业务-异步——第七章——中篇
电商项目——商品详情——第八章——中篇
电商项目——认证服务——第九章——中篇
电商项目——购物车——第十章——中篇
电商项目——消息队列——第十一章——中篇
电商项目——订单服务——第十二章——中篇
电商项目——分布式事务——第十三章——中篇
本地缓存与分布式缓存
整合redis测试
前面我们整合了redis,并且使用了stringRedisTemplate,现在我们来对如下的三级分类使用缓存进行业务优化
IndexController
//以json格式返回
@ResponseBody
@GetMapping("index/json/catalog.json")
public Map<String, List<Catelog2Vo>> getCatalogJson(){
Map<String, List<Catelog2Vo>> indexJson=categoryService.getCatalogJson();
return indexJson;
}
categoryServiceImpl
@Override
public Map<String, List<Catelog2Vo>> getCatalogJson() {
//给缓存中放入json字符串,拿出json字符串,还要逆转为可用的对象类型【序列化,反序列化】
/**
* 由于网络传输,我们相当于给内存创建了一个对象catalogJsonFromDb,要存到redis里面,必须要整成一个可传输的流数据,从redis里面拿到这个串又的逆转为对象
* 这就是序列化,反序列化
*/
//1:加入缓存逻辑(缓存存放的数据是json字符串)
//json跨语言,跨平台兼容
//1.1判断redis中是否有catalogJSON数据
String catalogJSON = stringRedisTemplate.opsForValue().get("catalogJSON");
if (StringUtils.isEmpty(catalogJSON)){
//1.2缓冲中没有,就查询数据库
Map<String, List<Catelog2Vo>> catalogJsonFromDb = getCatalogJsonFromDb();
//1.3 查到数据库在放入缓存
String string = JSON.toJSONString(catalogJsonFromDb);
stringRedisTemplate.opsForValue().set("catalogJSON",string);
return catalogJsonFromDb;
}
Map<String, List<Catelog2Vo>> result = JSON.parseObject(catalogJSON, new TypeReference<Map<String, List<Catelog2Vo>>>() {
});
return result;
}
/**
* 由于网络传输,我们相当于给内存创建了一个对象catalogJsonFromDb,
* 要存到redis里面,必须要整成一个可传输的流数据串,
* 从redis里面拿到这个串又的逆转为对象
* 这就是序列化,反序列化
*/
进行测试查看是否使用缓存性能会比,不使用缓存和直接每一次查询数据库的性能要好
查看是否可以成功获取数据
http://localhost:30000/index/json/catalog.json
进行压力测试
产生如上的原因:如下
解决办法:
操作步骤:先排除掉redis里面的lettuce,在引入依赖jedis
mall-product
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
<exclusions>
<exclusion>
<groupId>io.lettucegroupId>
<artifactId>lettuce-coreartifactId>
exclusion>
exclusions>
dependency>
<dependency>
<groupId>redis.clientsgroupId>
<artifactId>jedisartifactId>
dependency>
再次进行压力测试
得出如下对比数据
小知识:stringRedisTemplate和lettuce,jedis的关系
查看RedisAutoConfiguration 的源码,你会发现上面的@Import注解就是引入了Lettuce和Jedis
@Import({
LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {
//.....
缓存击穿,穿透,雪崩
第一个方法:加如下代码块
synchronized (){
}
我们要去哪里加锁呢?我们要去查询数据库的时候加锁如下
/**
* 1:将数据库的多次查询变为1次
* @return
*/
//从数据库查询并且封装db的
public Map<String, List<Catelog2Vo>> getCatalogJsonFromDb() {
//只要是同一把锁,就可以锁住需要这个锁的所有线程
/**
* 1:synchronized (this){ Springboot所有组件在容器值都是单例的,加this可以
*/
synchronized (this){
//第二个人得到锁以后就要去缓存中确定一次,如果第一个人已经在数据库中查询到数据放入缓存中,第二个人就不用在去查询数据库了
String catalogJSON = stringRedisTemplate.opsForValue().get("catalogJSON");
if (!StringUtils.isEmpty(catalogJSON)){
Map<String, List<Catelog2Vo>> result = JSON.parseObject(catalogJSON, new TypeReference<Map<String, List<Catelog2Vo>>>() {
});
}
//将数据库的多次查询变为1次
List<CategoryEntity> selectList = baseMapper.selectList(null);
//1:查出所有1级分类
List<CategoryEntity> level1Category = getParent_cid(selectList,0L);
//2:封装数据
Map<String, List<Catelog2Vo>> parent_cid = level1Category.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(item -> {
Catelog2Vo catelog2Vo = new Catelog2Vo(v.getCatId().toString(), null, item.getCatId().toString(), item.getName());
//1.给当前二级分类的三级分类封装成vo
List<CategoryEntity> level3Category = getParent_cid(selectList,item.getCatId());
//2:封装成指定格式
if (level3Category!=null){
List<Catelog2Vo.Catelog3Vo> collect = level3Category.stream().map(items -> {
Catelog2Vo.Catelog3Vo catelog3Vo = new Catelog2Vo.Catelog3Vo(item.getCatId().toString(),items.getCatId().toString(),items.getName());
return catelog3Vo;
}).collect(Collectors.toList());
catelog2Vo.setCatalog3List(collect