# 《高性能SQL引擎》性能测试-第02节:高性能SQL引擎压力性能测试
作者:冰河
星球:http://m6z.cn/6aeFbs (opens new window)
博客:https://binghe.gitcode.host (opens new window)
文章汇总:https://binghe.gitcode.host/md/all/all.html (opens new window)
源码获取地址:https://t.zsxq.com/0dhvFs5oR (opens new window)
沉淀,成长,突破,帮助他人,成就自我。
- 本节难度:★★☆☆☆
- 本节重点:对高性能SQL引擎进行进行JMeter压力测试,从全局视角了解高性能SQL引擎的设计和架构思想,并能够将其灵活应用到自身实际项目中。
大家好,我是冰河~~
完成高性能SQL引擎核心功能的设计与实现,并基于通用模型和通用模板分别给出10种典型的SQL场景后,已经对高性能SQL引擎进行了JMH基准性能测试,接下来,我们再通过JMeter对高性能SQL引擎进行压力测试。
# 一、背景
截止到目前,我们已经设计和实现了高性能SQL引擎的核心功能,包括:梳理了高性能SQL引擎的需求和流程、制定了高性能SQL引擎的方案目标和架构设计、制定了通用化落地方案、设计和实现通用数据模板和数据模型、设计和实现SQL构建器和驱动引擎等。
在SQL引擎实战篇章,我们已经基于高性能SQL引擎的通用模型和通用模板动态生成SQL。并且已经对高性能SQL引擎进行了基准性能测试。接下来,就对高性能SQL引擎进行JMeter压力测试。
# 二、本节诉求
对高性能SQL引擎进行进行JMeter压力测试,从全局视角了解高性能SQL引擎的设计和架构思想,并能够将其灵活应用到自身实际项目中。
# 三、压力测试
通过JMeter分别对高性能SQL引擎基于通用模型和通用模板动态生成SQL进行压力测试,最终给出压力测试结果。这里,我们以生成较为复杂的SQL为例,进行JMeter压力测试。
# 3.1 搭建压力测试工程
为了更方便的进行JMeter压力测试,这里,我们基于sql-plugin项目快速搭建SpringBoot环境,具体步骤如下所示。
(1)引用SpringBoot依赖
在项目的pom.xml文件中引入SpringBoot相关的依赖。
源码详见:sql-plugin/pom.xml。
<properties>
<spring-boot.version>2.7.3</spring-boot.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<!-- Import dependency management from Spring Boot -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
<exclusions>
<exclusion>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
(2)实现SQL模型创建类
SQL模型创建类主要的作用就是生成通用数据模型和通用数据模板。
源码详见:io.binghe.sql.plugin.springboot.model.SQLPluginModel。
public class SQLPluginModel {
private static final SqlParams SQL_PARAMS;
private static final String TEMPLATE;
static {
//SQL数据表
Table table = Table.builder().tableName("user_analysis").alias("user_analysis").build();
// 表1聚合条件
Aggregation agg1Table1 = Aggregation.builder().field("register_count").alias("register_count")
.aggregationMode(AggregationEnum.SUM.getName()).build();
Aggregation agg1Table2 = Aggregation.builder().field("login_count").alias("login_count")
.aggregationMode(AggregationEnum.SUM.getName()).build();
// 表1查询条件
Condition condition1 = Condition.builder().aboveConditions(Constants.AND).field("analysis_date")
.middleConditions(Constants.GE).fieldValue("2000-01-01").build();
// 表1关联条件
Join join1 = Join.builder().table(table).aggregation(Arrays.asList(agg1Table1, agg1Table2))
.condition(Arrays.asList(condition1)).fields(Arrays.asList("analysis_date", "analysis_type", "platform_id"))
.groupBy(Arrays.asList("analysis_date", "analysis_type", "platform_id"))
.joinType(JoinEnum.QUERY_SUBSYSTEM.toString()).build();
// 表2
Table table2 = Table.builder().tableName("pay_analysis").alias("pay_analysis").build();
// 表2聚合条件
Aggregation agg2Table1 = Aggregation.builder().field("pay_count").alias("pay_count")
.aggregationMode(AggregationEnum.SUM.getName()).build();
Aggregation agg2Table2 = Aggregation.builder().field("change_count").alias("change_count")
.aggregationMode(AggregationEnum.SUM.getName()).build();
// 表2查询条件
Condition condition2 = Condition.builder().aboveConditions(Constants.AND).field("analysis_date")
.middleConditions(Constants.GE).fieldValue("2000-01-01").build();
// 连接表条件
Condition joinCondition1 = Condition.builder().aboveConditions(Constants.AND).field("user_analysis.analysis_date")
.middleConditions(Constants.EQ).fieldValue("pay_analysis.analysis_date").build();
Condition joinCondition2 = Condition.builder().aboveConditions(Constants.AND).field("user_analysis.analysis_type")
.middleConditions(Constants.EQ).fieldValue("pay_analysis.analysis_type").build();
Condition joinCondition3 = Condition.builder().aboveConditions(Constants.AND).field("user_analysis.platform_id")
.middleConditions(Constants.EQ).fieldValue("pay_analysis.platform_id").build();
// 表2关联条件
Join join2 = Join.builder().table(table2).aggregation(Arrays.asList(agg2Table1, agg2Table2))
.condition(Arrays.asList(condition2)).fields(Arrays.asList("analysis_date", "analysis_type", "platform_id"))
.groupBy(Arrays.asList("analysis_date", "analysis_type", "platform_id")).joinType(JoinEnum.LEFT.toString())
.joinCondition(Arrays.asList(joinCondition1, joinCondition2, joinCondition3)).build();
Aggregation agg1 = Aggregation.builder().field("register_count").alias("registerCount")
.aggregationMode(AggregationEnum.SUM.getName()).build();
Aggregation agg2 = Aggregation.builder().field("login_count").alias("loginCount")
.aggregationMode(AggregationEnum.SUM.getName()).build();
Aggregation agg3 = Aggregation.builder().field("pay_count").alias("payCount")
.aggregationMode(AggregationEnum.SUM.getName()).build();
Aggregation agg4 = Aggregation.builder().field("change_count").alias("changeCount")
.aggregationMode(AggregationEnum.SUM.getName()).build();
// 构造通用模型
SQL_PARAMS = SqlParams.builder().table(table)
.aggregation(Arrays.asList(agg1, agg2, agg3, agg4))
.joins(Arrays.asList(join1, join2))
.groupBy(Arrays.asList(
"user_analysis.analysis_date",
"user_analysis.analysis_type",
"user_analysis.platform_id",
"pay_analysis.analysis_date",
"pay_analysis.analysis_type",
"pay_analysis.platform_id")).build();
TEMPLATE = getJson("join_query_sql.json");
}
private static String getJson(String fileName){
ClassLoader classLoader = BaseTest.class.getClassLoader();
InputStream inputStream = classLoader.getResourceAsStream(fileName);
if (inputStream == null){
throw new IllegalArgumentException("file not found: " + fileName);
}
try(BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))){
return reader.lines().collect(Collectors.joining(System.lineSeparator()));
}catch (IOException e){
throw new UncheckedIOException(e);
}
}
public static SqlParams getSqlParams(){
return SQL_PARAMS;
}
public static String getTemplate(){
return TEMPLATE;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
可以看到,在SQLPluginModel类中,分别定义了两个常量SQL_PARAMS和TEMPLATE,分别代表通用数据模型和通用数据模板。会在静态代码快中生成通用数据模型和通用数据模板,并将其分别赋值给SQL_PARAMS和TEMPLATE。最终对外提供getSqlParams()方法和getTemplate()方法分别返回SQL_PARAMS和TEMPLATE。
(3)实现接口类
创建SQLPluginController类,提供接口实现类。
源码详见:io.binghe.sql.plugin.springboot.controller.SQLPluginController。
@RestController
public class SQLPluginController {
@RequestMapping(value = "/model-sql")
public String modelSql(){
return SqlEngine.getSql(SQLPluginModel.getSqlParams());
}
@RequestMapping(value = "/template-sql")
public String templateSql(){
return SqlEngine.getSql(SQLPluginModel.getTemplate());
}
}
2
3
4
5
6
7
8
9
10
11
12
13
(4)实现启动类
实现测试工程的启动类。
源码详见:io.binghe.sql.plugin.springboot.SQLPluginStarter。
@SpringBootApplication
public class SQLPluginStarter {
public static void main(String[] args) {
SpringApplication.run(SQLPluginStarter.class, args);
}
}
2
3
4
5
6
上述工程中,基于通用模板生成SQL时,使用到的模板如下所示。
{
"table": {
"tableName": "user_analysis",
"alias": "user_analysis"
},
"joins": [{
"joinType": "QUERY_SUBSYSTEM",
"table": {
"tableName": "user_analysis",
"alias": "user_analysis"
},
"fields": ["analysis_date", "analysis_type", "platform_id"],
"aggregation": [{
"field": "register_count",
"aggregationMode": "SUM",
"alias": "register_count"
}, {
"field": "login_count",
"aggregationMode": "SUM",
"alias": "login_count"
}],
"condition": [{
"field": "analysis_date",
"aboveConditions": "and",
"middleConditions": ">=",
"fieldValue": "2000-01-01"
}],
"groupBy": ["analysis_date", "analysis_type", "platform_id"]
}, {
"joinType": "LEFT",
"table": {
"tableName": "pay_analysis",
"alias": "pay_analysis"
},
"fields": ["analysis_date", "analysis_type", "platform_id"],
"aggregation": [{
"field": "pay_count",
"aggregationMode": "SUM",
"alias": "pay_count"
}, {
"field": "change_count",
"aggregationMode": "SUM",
"alias": "change_count"
}],
"condition": [{
"field": "analysis_date",
"aboveConditions": "and",
"middleConditions": ">=",
"fieldValue": "2000-01-01"
}],
"joinCondition": [{
"field": "user_analysis.analysis_date",
"aboveConditions": "and",
"middleConditions": "=",
"fieldValue": "pay_analysis.analysis_date"
}, {
"field": "user_analysis.analysis_type",
"aboveConditions": "and",
"middleConditions": "=",
"fieldValue": "pay_analysis.analysis_type"
}, {
"field": "user_analysis.platform_id",
"aboveConditions": "and",
"middleConditions": "=",
"fieldValue": "pay_analysis.platform_id"
}],
"groupBy": ["analysis_date", "analysis_type", "platform_id"]
}],
"fields": ["user_analysis.analysis_date", "user_analysis.analysis_type", "user_analysis.platform_id", "pay_analysis.analysis_date", "pay_analysis.analysis_type", "pay_analysis.platform_id"],
"aggregation": [{
"field": "register_count",
"aggregationMode": "SUM",
"alias": "registerCount"
}, {
"field": "login_count",
"aggregationMode": "SUM",
"alias": "loginCount"
}, {
"field": "pay_count",
"aggregationMode": "SUM",
"alias": "payCount"
}, {
"field": "change_count",
"aggregationMode": "SUM",
"alias": "changeCount"
}],
"groupBy": ["user_analysis.analysis_date", "user_analysis.analysis_type", "user_analysis.platform_id", "pay_analysis.analysis_date", "pay_analysis.analysis_type", "pay_analysis.platform_id"]
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
基于通用模型和通用模板生成的SQL如下所示。
SELECT
user_analysis.analysis_date,
user_analysis.analysis_type,
user_analysis.platform_id,
pay_analysis.analysis_date,
pay_analysis.analysis_type,
pay_analysis.platform_id,
sum( register_count ) AS registerCount,
sum( login_count ) AS loginCount,
sum( pay_count ) AS payCount,
sum( change_count ) AS changeCount
FROM
(
SELECT
analysis_date,
analysis_type,
platform_id,
sum( register_count ) AS register_count,
sum( login_count ) AS login_count
FROM
user_analysis AS user_analysis
WHERE
analysis_date >= '2000-01-01'
GROUP BY
analysis_date,
analysis_type,
platform_id
) AS user_analysis
LEFT JOIN (
SELECT
analysis_date,
analysis_type,
platform_id,
sum( pay_count ) AS pay_count,
sum( change_count ) AS change_count
FROM
pay_analysis AS pay_analysis
WHERE
analysis_date >= '2000-01-01'
GROUP BY
analysis_date,
analysis_type,
platform_id
) AS pay_analysis ON user_analysis.analysis_date = pay_analysis.analysis_date
AND user_analysis.analysis_type = pay_analysis.analysis_type
AND user_analysis.platform_id = pay_analysis.platform_id
GROUP BY
user_analysis.analysis_date,
user_analysis.analysis_type,
user_analysis.platform_id,
pay_analysis.analysis_date,
pay_analysis.analysis_type,
pay_analysis.platform_id
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# 3.2 压力测试准备
(1)打开JMeter新建setUp线程池,进行如下配置:
- 线程数:200
- Ramp-Up时间(秒):1
- 循环次数:10000
(2)新建基于通用模型生成SQL的HTTP请求,进行如下配置:
- 协议:http
- 服务器名称或IP:127.0.0.1
- 端口号:8080
- 方法:GET
- 路径:/model-sql
- 内容编码:UTF-8
(3)新建基于通用模板生成SQL的HTTP请求,进行如下配置:
- 协议:http
- 服务器名称或IP:127.0.0.1
- 端口号:8080
- 方法:GET
- 路径:/template-sql
- 内容编码:UTF-8
(4)在JMeter中新建查看结果树、汇总报告和聚合报告等。
# 3.3 执行压力测试
启动压力测试工程后,随后启动JMeter,测试结果如下:
- 测试基于通用模型生成SQL的HTTP请求,测试QPS:1539/s。
- 测试基于通用模板生成SQL的HTTP请求,测试QPS:1326/s。
可以看到,基于本机开发环境测试高性能SQL引擎的性能还是比较高的,如果部署到服务器上,高性能SQL引擎的性能还会更高。另外, 之所以基于通用模板生成SQL的性能比基于通用模型生成SQL的性能略低,是因为基于通用模板生成SQL时,会有一个将数据从JSON字符串反序列成对象的过程。
注意:在不同的机器上运行上面的程序,得出的结果可能不同。另外,本节的JMeter压力测试脚本详见项目工程的src/test/resources/压力测试脚本.jmx文件
# 四、本节总结
本节,主要对高性能SQL引擎进行了JMeter压力测试,测试范围包括基于通用模型生成SQL和基于通用模板生成SQL。从测试结果来看,高性能SQL引擎动态生成SQL的性能是非常高的。其中,基于通用模板生成SQL会比基于通用模型生成SQL性能略低,是因为基于通用模板生成SQL时,会有一个将数据从JSON字符串反序列成对象的过程。
最后,可以在评论区写下你学完本章节的收获,祝大家都能学有所成,我们一起搞定高性能SQL引擎。
# 五、写在最后
在冰河的知识星球除了已完结的高性能网关和热更的RPC视频外,还有其他众多高并发、高性能中间件与业务场景项目,像DeepSeek大模型、手写高性能熔断组件、手写通用指标上报组件、手写高性能数据库路由组件、分布式IM即时通讯系统、Sekill分布式秒杀系统、手写RPC、简易商城系统等等,这些项目的需求、方案、架构、落地等均来自互联网真实业务场景,让你真正学到互联网大厂的业务与技术落地方案,并将其有效转化为自己的知识储备。
值得一提的是:冰河自研的Polaris高性能网关比某些开源网关项目性能更高,并且冰河也正在为企业级高性能RPC框架录制视频,全程带你分析原理和手撸代码。 你还在等啥?不少小伙伴经过星球硬核技术和项目的历练,早已成功跳槽加薪,实现薪资翻倍,而你,还在原地踏步,抱怨大环境不好。抛弃焦虑和抱怨,我们一起塌下心来沉淀硬核技术和项目,让自己的薪资更上一层楼。

目前,领券加入星球就可以跟冰河一起学习《DeepSeek大模型》、《手写高性能通用熔断组件项目》、《手写高性能通用监控指标上报组件》、《手写高性能数据库路由组件项目》、《手写简易商城脚手架项目》、《手写高性能RPC项目》和《Spring6核心技术与源码解析》、《实战高并发设计模式》、《分布式Seckill秒杀系统》、《分布式IM即时通讯系统》和《手写高性能Polaris网关》,从零开始介绍原理、设计架构、手撸代码。
花很少的钱就能学这么多硬核技术、中间件项目和大厂秒杀系统与分布式IM即时通讯系统,比其他培训机构不知便宜多少倍,硬核多少倍,如果是我,我会买他个十年!
加入要趁早,后续还会随着项目和加入的人数涨价,而且只会涨,不会降,先加入的小伙伴就是赚到。
另外,还有一个限时福利,邀请一个小伙伴加入,冰河就会给一笔 分享有奖 ,有些小伙伴都邀请了50+人,早就回本了!
# 六、其他方式加入星球
- 链接 :打开链接 http://m6z.cn/6aeFbs 加入星球。
- 回复 :在公众号 冰河技术 回复 星球 领取优惠券加入星球。
特别提醒: 苹果用户进圈或续费,请加微信 hacker_binghe 扫二维码,或者去公众号 冰河技术 回复 星球 扫二维码加入星球。
**好了,今天就到这儿吧,我是冰河,我们下期见~~