053 性能压测 单机锁 setnx
文章目录
- 性能压测-压力测试
- 索引
- thymeleaf
- nginx
- 减少数据库查询(代码有bug)
- 缓存
- 安全
- 单机锁(防止缓存击穿)
- setnx
- pom.xml
性能压测-压力测试
1 响应时间(Response Time: RT):响应时间指用户从客户端发起一个请求开始,到客户端接收到从服务器端返回的响应结束,整个过程所耗费的时间。
HPS(Hits Per Second):每秒点击次数,单位是次/秒
TPS(Transaction per Second):系统图每秒处理交易数,单位是笔/秒
OPS(Query per Second):系统每秒处理查询次数,单位是次/秒
2 性能测试主要关注如下三个指标:
吞吐量:每秒钟系统能够处理的请求数、任务数
响应时间:服务处理一个请求或一个任务的耗时
错误率:一批请求中结果出错的请求所占比例
https://jmeter.apache.org/download_jmeter.cgi
索引
thymeleaf
spring:
thymeleaf:
cache: true
nginx
conf.d
location /static {
root /usr/share/nginx/html;
}
location / {
proxy_set_header Host $host;
proxy_pass http://cubemall;
}
减少数据库查询(代码有bug)
/**
* 获取三级分类
* @return
*/
@Override
public List<CategoryVo> getLevel1Categorys() {
//改为查询所有分类
List<CategoryEntity> selectList = baseMapper.selectList(null);
List<CategoryEntity> categoryEntities = getParentId(selectList,0);
List<CategoryVo> CategoryVoList = categoryEntities.stream().map(categoryEntity -> {
CategoryVo categoryVo = new CategoryVo();
BeanUtils.copyProperties(categoryEntity, categoryVo);
//查询一级分类下的二级分类
List<CategoryEntity> level2Categorys = getParentId(selectList,categoryEntity.getId());
//将当前一级分类下的二级分类封装成Vo
if (level2Categorys != null) {
List<Category2Vo> category2VoList = level2Categorys.stream().map(level2Category -> {
Category2Vo category2Vo = new Category2Vo();
category2Vo.setId(level2Category.getId().toString());
category2Vo.setName(level2Category.getName());
category2Vo.setCategory1Id(categoryEntity.getId().toString());
//查询二级分类下的三级分类
List<CategoryEntity> level3Categorys = getParentId(selectList,level2Category.getId());
//将当前二级分类下的三级分类封装到Vo
if (level3Categorys != null) {
List<Category3Vo> category3VoList = level3Categorys.stream().map(level3Category -> {
Category3Vo category3Vo = new Category3Vo();
category3Vo.setId(level3Category.getId().toString());
category3Vo.setName(level3Category.getName());
category3Vo.setCategory2Id(category2Vo.getId());
return category3Vo;
}).collect(Collectors.toList());
category2Vo.setCategory3VoList(category3VoList);
}
return category2Vo;
}).collect(Collectors.toList());
categoryVo.setCategory2VoList(category2VoList);
}
return categoryVo;
}).collect(Collectors.toList());
return CategoryVoList;
}
private List<CategoryEntity> getParentId(List<CategoryEntity> selectList, Integer parentId) {
List<CategoryEntity> collect = selectList.stream().filter(item -> {
return item.getParentId() == parentId;
}).collect(Collectors.toList());
return collect;
}
缓存
/**
* 使用redis改造三级分类
* @return
*/
public List<CategoryVo> getLevel1Categorys() {
//1.从缓存中查询数据
String categoryJSON = redisTemplate.opsForValue().get("categoryJSON");
if (StringUtils.isEmpty(categoryJSON)) {
//2.缓存中没有数据,查询数据库,从数据库查询分类数据
List<CategoryVo> categoryJsonFromDb = getCategoryJsonFromDb();
//3.查询的数据放入缓存中,将对象转换为json传入
redisTemplate.opsForValue().set("categoryJSON", JSON.toJSONString(categoryJsonFromDb));
return categoryJsonFromDb;
}
//4.如果缓存中有数据,将查询出的数据转换为java对象,指明转为的对象类型
List<CategoryVo> categoryVos = JSON.parseObject(categoryJSON, new TypeReference<List<CategoryVo>>() {
});
return categoryVos;
}
安全
单机锁(防止缓存击穿)
/**
* 使用redis改造三级分类
* @return
*/
public List<CategoryVo> getLevel1Categorys() {
//1.从缓存中查询数据
String categoryJSON = redisTemplate.opsForValue().get("categoryJSON");
if (StringUtils.isEmpty(categoryJSON)) {
System.out.println("缓存不命中,查询数据库。。。");
//2.缓存中没有数据,查询数据库,从数据库查询分类数据
List<CategoryVo> categoryJsonFromDb = getCategoryJsonFromDb();
return categoryJsonFromDb;
}
System.out.println("缓存命中。。。");
//4.如果缓存中有数据,将查询出的数据转换为java对象,指明转为的对象类型
List<CategoryVo> categoryVos = JSON.parseObject(categoryJSON, new TypeReference<List<CategoryVo>>() {
});
return categoryVos;
}
/**
* 获取三级分类
* @return
*/
public List<CategoryVo> getCategoryJsonFromDb() {
synchronized (this){
//得到锁之后,去查看缓存中是否有数据,如果没有数据,继续查询数据库
String categoryJSON = redisTemplate.opsForValue().get("categoryJSON");
if(!StringUtils.isEmpty(categoryJSON)){
List<CategoryVo> categoryVos = JSON.parseObject(categoryJSON, new TypeReference<List<CategoryVo>>() {
});
return categoryVos;
}
System.out.println("查询了数据库。。。");
//改为查询所有分类
List<CategoryEntity> selectList = baseMapper.selectList(null);
List<CategoryEntity> categoryEntities = getParentId(selectList,0);
List<CategoryVo> CategoryVoList = categoryEntities.stream().map(categoryEntity -> {
CategoryVo categoryVo = new CategoryVo();
BeanUtils.copyProperties(categoryEntity, categoryVo);
//查询一级分类下的二级分类
List<CategoryEntity> level2Categorys = getParentId(selectList,categoryEntity.getId());
//将当前一级分类下的二级分类封装成Vo
if (level2Categorys != null) {
List<Category2Vo> category2VoList = level2Categorys.stream().map(level2Category -> {
Category2Vo category2Vo = new Category2Vo();
category2Vo.setId(level2Category.getId().toString());
category2Vo.setName(level2Category.getName());
category2Vo.setCategory1Id(categoryEntity.getId().toString());
//查询二级分类下的三级分类
List<CategoryEntity> level3Categorys = getParentId(selectList,level2Category.getId());
//将当前二级分类下的三级分类封装到Vo
if (level3Categorys != null) {
List<Category3Vo> category3VoList = level3Categorys.stream().map(level3Category -> {
Category3Vo category3Vo = new Category3Vo();
category3Vo.setId(level3Category.getId().toString());
category3Vo.setName(level3Category.getName());
category3Vo.setCategory2Id(category2Vo.getId());
return category3Vo;
}).collect(Collectors.toList());
category2Vo.setCategory3VoList(category3VoList);
}
return category2Vo;
}).collect(Collectors.toList());
categoryVo.setCategory2VoList(category2VoList);
}
return categoryVo;
}).collect(Collectors.toList());
//3.查询的数据放入缓存中,将对象转换为json传入
redisTemplate.opsForValue().set("categoryJSON", JSON.toJSONString(CategoryVoList),1, TimeUnit.DAYS);
return CategoryVoList;
}
}
private List<CategoryEntity> getParentId(List<CategoryEntity> selectList, Integer parentId) {
List<CategoryEntity> collect = selectList.stream().filter(item -> {
return item.getParentId() == parentId;
}).collect(Collectors.toList());
return collect;
}
setnx
/**
* 获取三级分类(redis分布式锁)
* @return
*/
public List<CategoryVo> getCategoryJsonFromWithRedisLock() {
String uuid = UUID.randomUUID().toString();
//1.占分布式
Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", uuid,30,TimeUnit.SECONDS);
if (lock){
//加锁成功,执行业务
//设置过期时间
//redisTemplate.expire("lock",30,TimeUnit.SECONDS);
//List<CategoryVo> dataFromDb = getDataFromDb();
//删除锁
// String lockValue = redisTemplate.opsForValue().get("lock");
// if (uuid.equals(lockValue)) {
// //删除自己的锁
// redisTemplate.delete("lock");
// }
List<CategoryVo> 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";
redisTemplate.execute(new DefaultRedisScript<Integer>(script, Integer.class), Arrays.asList("lock"), uuid);
}
return dataFromDb;
} else {
//加锁失败,重试
return getCategoryJsonFromWithRedisLock();//自旋
}
}
private List<CategoryVo> getDataFromDb() {
//得到锁之后,去查看缓存中是否有数据,如果没有数据,继续查询数据库
String categoryJSON = redisTemplate.opsForValue().get("categoryJSON");
if(!StringUtils.isEmpty(categoryJSON)){
List<CategoryVo> categoryVos = JSON.parseObject(categoryJSON, new TypeReference<List<CategoryVo>>() {
});
return categoryVos;
}
System.out.println("查询了数据库。。。");
//改为查询所有分类
List<CategoryEntity> selectList = baseMapper.selectList(null);
List<CategoryEntity> categoryEntities = getParentId(selectList,0);
List<CategoryVo> CategoryVoList = categoryEntities.stream().map(categoryEntity -> {
CategoryVo categoryVo = new CategoryVo();
BeanUtils.copyProperties(categoryEntity, categoryVo);
//查询一级分类下的二级分类
List<CategoryEntity> level2Categorys = getParentId(selectList,categoryEntity.getId());
//将当前一级分类下的二级分类封装成Vo
if (level2Categorys != null) {
List<Category2Vo> category2VoList = level2Categorys.stream().map(level2Category -> {
Category2Vo category2Vo = new Category2Vo();
category2Vo.setId(level2Category.getId().toString());
category2Vo.setName(level2Category.getName());
category2Vo.setCategory1Id(categoryEntity.getId().toString());
//查询二级分类下的三级分类
List<CategoryEntity> level3Categorys = getParentId(selectList,level2Category.getId());
//将当前二级分类下的三级分类封装到Vo
if (level3Categorys != null) {
List<Category3Vo> category3VoList = level3Categorys.stream().map(level3Category -> {
Category3Vo category3Vo = new Category3Vo();
category3Vo.setId(level3Category.getId().toString());
category3Vo.setName(level3Category.getName());
category3Vo.setCategory2Id(category2Vo.getId());
return category3Vo;
}).collect(Collectors.toList());
category2Vo.setCategory3VoList(category3VoList);
}
return category2Vo;
}).collect(Collectors.toList());
categoryVo.setCategory2VoList(category2VoList);
}
return categoryVo;
}).collect(Collectors.toList());
//3.查询的数据放入缓存中,将对象转换为json传入
redisTemplate.opsForValue().set("categoryJSON", JSON.toJSONString(CategoryVoList),1, TimeUnit.DAYS);
return CategoryVoList;
}
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.18.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.xd.cubemall</groupId>
<artifactId>cubemall-product</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>cubemall-product</name>
<description>cubemall-product</description>
<url/>
<licenses>
<license/>
</licenses>
<developers>
<developer/>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.SR3</spring-cloud.version>
</properties>
<dependencies>
<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>
<!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
<!--当修改页面后不需要再重启项目-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<!--引入common公共模块-->
<dependency>
<groupId>com.xd.cubemall</groupId>
<artifactId>cubemall-common</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<!--添加模板技术渲染页面-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--阿里云OSS-->
<!-- <dependency>-->
<!-- <groupId>com.aliyun.oss</groupId>-->
<!-- <artifactId>aliyun-sdk-oss</artifactId>-->
<!-- <version>3.17.4</version>-->
<!-- </dependency>-->
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.1.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>