一、问题背景:高并发下的性能雪崩
在业务系统中新增 SQL 拦截器并引入 JSQLParser 作为 SQL 解析工具后,功能测试阶段未发现异常,但压测时出现核心性能指标断崖式下跌:
- CPU 使用率:从日常 10% 飙升至 50%+;
- 接口响应耗时:平均 400ms 增至 5s+,超时次数短时间内显著升高;
通过 Arthas 线程分析定位到核心问题:net.sf.jsqlparser.parser.CCJSqlParser 相关方法占用极高 CPU(单线程占比 30.7%),且调用栈集中在 SQL 解析的语法分析阶段。本地模拟测试进一步验证:复杂 SQL 解析耗时常超过 500ms,成为高并发场景下的性能瓶颈。
shell
[arthas@1]$ thread -n 1
"thread-global-34" Id=283 cpuUsage=30.7% deltaTime=62ms time=53396ms RUNNABLE
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3R_RelObjectNameWithoutValue_1411_5_505(CCJSqlParser.java:18961)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3R_RelObjectName_1446_6_339(CCJSqlParser.java:18754)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3R_RelObjectName_1446_5_156(CCJSqlParser.java:18762)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3R_RelObjectNameExt_1476_7_350(CCJSqlParser.java:18460)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3R_RelObjectNameExt_1476_5_169(CCJSqlParser.java:18468)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3R_RelObjectNameList_1383_5_262(CCJSqlParser.java:19335)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3R_InternalFunction_4028_5_526(CCJSqlParser.java:21320)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3R_Function_4004_11_391(CCJSqlParser.java:21423)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3R_Function_4002_5_192(CCJSqlParser.java:21475)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3R_PrimaryExpression_3448_11_305(CCJSqlParser.java:24200)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3R_PrimaryExpression_3421_5_126(CCJSqlParser.java:24437)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3R_BitwiseXor_3375_5_205(CCJSqlParser.java:24704)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3R_MultiplicativeExpression_3345_5_200(CCJSqlParser.java:24830)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3R_AdditiveExpression_3321_5_397(CCJSqlParser.java:24910)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3R_BitwiseAndOr_3289_5_197(CCJSqlParser.java:25064)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3R_ConcatExpression_3266_5_295(CCJSqlParser.java:25153)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3R_SimpleExpression_3242_5_122(CCJSqlParser.java:25240)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3_117(CCJSqlParser.java:25520)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3R_ComparisonItem_3196_3_181(CCJSqlParser.java:25568)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3_97(CCJSqlParser.java:19594)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3R_RegularCondition_2829_5_179(CCJSqlParser.java:19934)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3R_Condition_2811_9_351(CCJSqlParser.java:20007)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3R_Condition_2809_5_176(CCJSqlParser.java:20047)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3R_AndExpression_2772_9_643(CCJSqlParser.java:20207)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3R_AndExpression_2771_5_595(CCJSqlParser.java:20222)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3R_OrExpression_2749_5_538(CCJSqlParser.java:20298)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3R_XorExpression_2730_5_416(CCJSqlParser.java:20351)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3R_Expression_2720_5_226(CCJSqlParser.java:20403)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3R_ComplexExpressionList_3093_5_223(CCJSqlParser.java:26343)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3_125(CCJSqlParser.java:23934)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3_139(CCJSqlParser.java:23962)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3R_PrimaryExpression_3421_5_126(CCJSqlParser.java:24469)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3R_BitwiseXor_3375_5_205(CCJSqlParser.java:24704)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3R_MultiplicativeExpression_3345_5_200(CCJSqlParser.java:24830)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3R_AdditiveExpression_3321_5_397(CCJSqlParser.java:24910)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3R_BitwiseAndOr_3289_5_197(CCJSqlParser.java:25064)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3R_ConcatExpression_3266_5_295(CCJSqlParser.java:25153)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3R_SimpleExpression_3242_5_122(CCJSqlParser.java:25240)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3_117(CCJSqlParser.java:25520)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3R_ComparisonItem_3196_3_181(CCJSqlParser.java:25568)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3R_RegularCondition_2829_5_179(CCJSqlParser.java:19863)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3R_Condition_2811_9_351(CCJSqlParser.java:20007)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3R_Condition_2809_5_176(CCJSqlParser.java:20047)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3R_AndExpression_2772_9_643(CCJSqlParser.java:20207)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3R_AndExpression_2771_5_595(CCJSqlParser.java:20222)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3R_OrExpression_2751_9_641(CCJSqlParser.java:20285)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3R_OrExpression_2749_5_538(CCJSqlParser.java:20302)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3R_XorExpression_2730_5_416(CCJSqlParser.java:20351)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3R_Expression_2720_5_226(CCJSqlParser.java:20403)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3R_ComplexExpressionList_3093_5_223(CCJSqlParser.java:26343)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3_125(CCJSqlParser.java:23934)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3_139(CCJSqlParser.java:23962)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3R_PrimaryExpression_3421_5_126(CCJSqlParser.java:24469)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3R_BitwiseXor_3375_5_205(CCJSqlParser.java:24704)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3R_MultiplicativeExpression_3345_5_200(CCJSqlParser.java:24830)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3R_AdditiveExpression_3321_5_397(CCJSqlParser.java:24910)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3R_BitwiseAndOr_3289_5_197(CCJSqlParser.java:25064)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3R_ConcatExpression_3266_5_295(CCJSqlParser.java:25153)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3R_SimpleExpression_3242_5_122(CCJSqlParser.java:25240)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3_105(CCJSqlParser.java:19460)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3R_SQLCondition_2889_5_180(CCJSqlParser.java:19511)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3_93(CCJSqlParser.java:19988)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3R_Condition_2809_5_176(CCJSqlParser.java:20049)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_3_90(CCJSqlParser.java:20117)
at net.sf.jsqlparser.parser.CCJSqlParser.jj_2_90(CCJSqlParser.java:17254)
at net.sf.jsqlparser.parser.CCJSqlParser.AndExpression(CCJSqlParser.java:8676)
at net.sf.jsqlparser.parser.CCJSqlParser.OrExpression(CCJSqlParser.java:8579)
at net.sf.jsqlparser.parser.CCJSqlParser.XorExpression(CCJSqlParser.java:8557)
at net.sf.jsqlparser.parser.CCJSqlParser.Expression(CCJSqlParser.java:8528)
at net.sf.jsqlparser.parser.CCJSqlParser.WhereClause(CCJSqlParser.java:7816)
at net.sf.jsqlparser.parser.CCJSqlParser.PlainSelect(CCJSqlParser.java:4771)
at net.sf.jsqlparser.parser.CCJSqlParser.SetOperationList(CCJSqlParser.java:4960)
at net.sf.jsqlparser.parser.CCJSqlParser.SelectBody(CCJSqlParser.java:4631)
at net.sf.jsqlparser.parser.CCJSqlParser.Select(CCJSqlParser.java:4626)
at net.sf.jsqlparser.parser.CCJSqlParser.SingleStatement(CCJSqlParser.java:155)
at net.sf.jsqlparser.parser.CCJSqlParser.Statement(CCJSqlParser.java:83)
at net.sf.jsqlparser.parser.CCJSqlParserUtil.parseStatement(CCJSqlParserUtil.java:188)
at net.sf.jsqlparser.parser.CCJSqlParserUtil.parse(CCJSqlParserUtil.java:63)
at net.sf.jsqlparser.parser.CCJSqlParserUtil.parse(CCJSqlParserUtil.java:38)
at com.baomidou.mybatisplus.extension.parser.JsqlParserSupport.parserSingle(JsqlParserSupport.java:49)
at com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor.beforeQuery(TenantLineInnerInterceptor.java:65)
at com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor.intercept(MybatisPlusInterceptor.java:78)
at org.apache.ibatis.plugin.Plugin.invoke(Plugin.java:62)
at com.sun.proxy.$Proxy374.query(Unknown Source)
at com.yoc.ptc.orm.data.encryption.interceptor.ParameterInterceptor.query4(ParameterInterceptor.java:89)
at com.yoc.ptc.orm.data.encryption.interceptor.ParameterInterceptor.intercept(ParameterInterceptor.java:65)
at org.apache.ibatis.plugin.Plugin.invoke(Plugin.java:62)
at com.sun.proxy.$Proxy374.query(Unknown Source)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:151)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:145)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:140)
at sun.reflect.GeneratedMethodAccessor330.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:427)
at com.sun.proxy.$Proxy174.selectList(Unknown Source)
at org.mybatis.spring.SqlSessionTemplate.selectList(SqlSessionTemplate.java:224)
at com.baomidou.mybatisplus.core.override.MybatisMapperMethod.executeForMany(MybatisMapperMethod.java:166)
at com.baomidou.mybatisplus.core.override.MybatisMapperMethod.execute(MybatisMapperMethod.java:77)
at com.baomidou.mybatisplus.core.override.MybatisMapperProxy$PlainMethodInvoker.invoke(MybatisMapperProxy.java:148)
at com.baomidou.mybatisplus.core.override.MybatisMapperProxy.invoke(MybatisMapperProxy.java:89)
at com.sun.proxy.$Proxy300.listByPlatformCodes(Unknown Source)
at sun.reflect.GeneratedMethodAccessor921.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:139)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212)
at com.sun.proxy.$Proxy301.listByPlatformCodes(Unknown Source)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:750)
二.主流高效 JSQLParser 替代方案
1. Apache Calcite(推荐高并发 / 高性能场景)
Apache Calcite 是一款功能强大的开源 SQL 解析 / 优化引擎(很多知名大数据框架如 Flink、Hive、Drill 都基于它构建),在高并发场景下表现远优于 JSQLParser,核心优势和使用要点如下:
-
核心优势:
- 解析性能优异,内置缓存机制和高效的语法分析器实现,并发吞吐量高;
- 支持完整的 SQL 标准(DDL、DML、复杂查询、窗口函数等),兼容性远超 JSQLParser;
- 不仅支持解析,还提供 SQL 验证、优化、生成等全链路能力,可扩展性极强;
- 线程安全,适合高并发场景下的复用,无额外线程安全开销。
-
使用方式:
引入 Maven 依赖(核心包):
xml<dependency> <groupId>org.apache.calcite</groupId> <artifactId>calcite-core</artifactId> <version>1.36.0</version> <!-- 推荐使用稳定新版本 --> </dependency>简单解析示例:
import org.apache.calcite.sql.SqlNode; import org.apache.calcite.sql.parser.SqlParseException; import org.apache.calcite.sql.parser.SqlParser; public class CalciteSqlParser { public static SqlNode parseSql(String sql) throws SqlParseException { // 构建解析器配置,支持自定义SQL方言 SqlParser.Config config = SqlParser.configBuilder() .setCaseSensitive(false) .build(); SqlParser parser = SqlParser.create(sql, config); // 执行解析,返回SQL抽象语法树(AST) return parser.parseStmt(); } }
2. Druid SQL Parser(阿里开源,适合 Java 生态 / 数据库兼容场景)
Druid 是阿里开源的数据库连接池 & SQL 解析工具,其 SQL 解析模块轻量高效,专门针对国内主流数据库(MySQL、Oracle、SQL Server 等)做了优化,非常适合高并发的业务系统。
-
核心优势:
- 解析速度快,内存占用低,针对高并发场景做了大量性能优化;
- 对国内主流数据库的方言支持极佳(如 MySQL 的自定义函数、Oracle 的存储过程等);
- 内置 SQL 语法校验、注入检测、格式化等实用功能,开箱即用;
- 集成成本低,若项目已使用 Druid 连接池,可直接复用,无额外依赖冗余。
-
使用方式:
引入 Maven 依赖:
xml<dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.18</version> <!-- 稳定版 --> </dependency>简单解析示例:
import com.alibaba.druid.sql.SQLUtils; import com.alibaba.druid.sql.ast.SQLStatement; import com.alibaba.druid.util.JdbcConstants; import java.util.List; public class DruidSqlParser { public static List<SQLStatement> parseSql(String sql) { // 指定数据库方言(此处为MySQL,可替换为JdbcConstants.ORACLE等) return SQLUtils.parseStatements(sql, JdbcConstants.MYSQL); } }
3. Antlr4(自定义 SQL 解析,适合特殊场景需求)
Antlr4 是一款强大的语法分析器生成工具,并非专门的 SQL 解析器,但可以通过自定义 SQL 语法规则生成高效的解析器,适合有特殊 SQL 方言、个性化解析需求的场景。
- 核心优势:
- 灵活性极高,可自定义 SQL 语法规则,支持非标准 SQL 解析;
- 生成的解析器性能优异,支持并发调用;
- 社区成熟,提供丰富的语法模板(含标准 SQL 模板),减少开发成本。
- 注意点:相比前两者,Antlr4 有一定学习成本,需要定义语法文件(.g4)并生成解析器代码,适合有定制化需求的高并发场景。
针对高并发场景的性能需求,对比三款主流 SQL 解析工具的核心特性,最终选择 Druid SQL Parser 作为替代方案:
| 解析工具 | 核心优势 | 适用场景 | 集成成本 |
|---|---|---|---|
| JSQLParser | 轻量、入门成本低 | 低并发 / 简单 SQL 场景 | 低 |
| Apache Calcite | 性能优异、支持完整 SQL 标准、可扩展性强 | 高并发 / 大数据 / 复杂 SQL 优化场景 | 中 |
| Druid SQL Parser | 解析速度快、内存占用低、适配国内主流数据库方言 | 高并发 Java 业务系统(尤其已用 Drui | 无 |
经过对比,我们选择使用Druid SQL Parser
选型理由:
- 项目已集成 Druid 连接池,无需额外引入依赖,无版本兼容风险;
- 对 MySQL/Oracle 等国内主流数据库方言支持更友好;
- 轻量高效,适配高并发场景的性能要求。
三.性能对比
1.单 SQL 解析耗时对比
java
try {
StopWatchUtil stopWatchUtil = StopWatchUtil.create("sql解析时长分析");
stopWatchUtil.start("CCJSqlParserUtil解析");
Statement statement = CCJSqlParserUtil.parse(sql);
stopWatchUtil.stop();
stopWatchUtil.start("Druid Sql Parser 解析");
SQLStatement sqlStatement = SQLUtils.parseSingleStatement(sql, JdbcConstants.MYSQL);
stopWatchUtil.stop();
logger.warn(stopWatchUtil.prettyPrint());
} catch (Exception e) {
throw ExceptionUtils.mpe("Failed to process, Error SQL: %s", e.getCause(), sql);
}
java
---------------------------------------------
ns % Task name
---------------------------------------------
000208200 098% CCJSqlParserUtil解析
000003400 002% SQLUtils解析
[,] 2026-01-12 11:32:24.465 WARN [my-demo,,,] 15356 --- [ buyCredit-64] y.y.a.u.s.p.MySqlInterceptor 53 : StopWatch 'sql解析时长分析': running time = 104100 ns
---------------------------------------------
ns % Task name
---------------------------------------------
000099700 096% CCJSqlParserUtil解析
000004400 004% SQLUtils解析
[,] 2026-01-12 11:32:24.465 WARN [my-demo,,,] 15356 --- [ buyCredit-275] y.y.a.u.s.p.MySqlInterceptor 53 : StopWatch 'sql解析时长分析': running time = 116600 ns
---------------------------------------------
ns % Task name
---------------------------------------------
000109500 094% CCJSqlParserUtil解析
000007100 006% SQLUtils解析
[,] 2026-01-12 11:32:24.464 WARN [my-demo,,,] 15356 --- [ buyCredit-58] y.y.a.u.s.p.MySqlInterceptor 53 : StopWatch 'sql解析时长分析': running time = 197100 ns
---------------------------------------------
ns % Task name
---------------------------------------------
000188200 095% CCJSqlParserUtil解析
000008900 005% SQLUtils解析
[,] 2026-01-12 11:32:24.465 WARN [my-demo,,,] 15356 --- [ buyCredit-70] y.y.a.u.s.p.MySqlInterceptor 53 : StopWatch 'sql解析时长分析': running time = 105300 ns
---------------------------------------------
ns % Task name
---------------------------------------------
000101000 096% CCJSqlParserUtil解析
000004300 004% SQLUtils解析
[,] 2026-01-12 11:32:24.465 WARN [my-demo,,,] 15356 --- [ buyCredit-458] y.y.a.u.s.p.MySqlInterceptor 53 : StopWatch 'sql解析时长分析': running time = 105300 ns
---------------------------------------------
ns % Task name
---------------------------------------------
000099500 094% CCJSqlParserUtil解析
000005800 006% SQLUtils解析
[,] 2026-01-12 11:32:24.466 WARN [my-demo,,,] 15356 --- [ buyCredit-69] y.y.a.u.s.p.MySqlInterceptor 53 : StopWatch 'sql解析时长分析': running time = 258400 ns
---------------------------------------------
ns % Task name
---------------------------------------------
000252700 098% CCJSqlParserUtil解析
000005700 002% SQLUtils解析
[,] 2026-01-12 11:32:24.467 WARN [my-demo,,,] 15356 --- [ buyCredit-412] y.y.a.u.s.p.MySqlInterceptor 53 : StopWatch 'sql解析时长分析': running time = 124700 ns
---------------------------------------------
ns % Task name
---------------------------------------------
000118100 095% CCJSqlParserUtil解析
000006600 005% SQLUtils解析
[,] 2026-01-12 11:32:24.467 WARN [my-demo,,,] 15356 --- [ buyCredit-250] y.y.a.u.s.p.MySqlInterceptor 53 : StopWatch 'sql解析时长分析': running time = 238800 ns
---------------------------------------------
ns % Task name
---------------------------------------------
000233000 098% CCJSqlParserUtil解析
000005800 002% SQLUtils解析
通过 StopWatch 对相同复杂 SQL 进行解析耗时对比,结果如下(单位:纳秒):
| 解析工具 | 平均耗时 | 占比 | 性能提升倍数 |
|---|---|---|---|
| CCJSqlParserUtil | 150,000+ ns(0.15ms+) | 95%+ | - |
| Druid SQLUtils | 5,000 ns 左右(0.005ms) | 5%- | 约 30 倍 + |
注:实际复杂 SQL 场景下,JSQLParser 耗时可达 500ms+,而 Druid 仍稳定在 1ms 内,性能差距达数百倍。
2. 高并发压测对比(模拟 1000QPS 场景)
现在编写以下代码,模拟1000qps的高并发下,大批量sql并发执行的场景
@Test
public void testParser() {
System.out.println("开始执行");
//模拟1000次访问
for (int i = 0; i < 1000; i++) {
int finalI = i;
MyThreadPoolTask.execute(()->{
List<Callable<User>> list = new ArrayList<>();
//模拟一个用户会触发1000次sql查询
for (int j = 0; j < 1000; j++) {
int finalJ = j;
userService.selectById((long) finalI * 100);
}
List<Future<User>> futures = null;
try {
//并发执行sql
futures = myThreadPoolTask.getThreadPoolExecutor().invokeAll(list, 20, TimeUnit.SECONDS);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
//执行
for (Future<User> future : futures) {
if (!future.isCancelled()) {
try {
future.get();
} catch (Exception e) {
log.error("任务执行异常", e);
}
}
}
System.out.println("单次执行完成");
});
}
System.out.println("遍历完毕");
ThreadUtil.sleep(100000);
}
CCJSqlParserUtil



druid的SQL parser
只执行SQLUtils.parseStatements(originSql, DbType.mysql)

完整版



不解析sql只使用拦截器时


压测场景:模拟单用户触发 1000 次 SQL 查询,并发执行 1000 次请求,对比三种场景的性能表现:
| 测试场景 | CPU 使用率 | 接口响应稳定性 | 核心特征 |
|---|---|---|---|
| 使用 JSQLParser 解析 | 飙升 40%+ | 响应耗时波动极大,大量超时 | 解析阶段成为核心瓶颈 |
| 使用 Druid SQL Parser 解析 | 仅上升 5% 左右 | 响应耗时稳定,无明显超时 | 解析耗时可忽略,性能接近无解析场景 |
| 仅拦截器(无 SQL 解析) | 基本无上升 | 响应耗时与原系统一致 | 基准性能参考 |
压测核心结论:
- JSQLParser 解析导致 CPU 资源被大量消耗,引发性能雪崩;
- Druid 解析器对系统性能影响极小,CPU 曲线平稳,响应耗时接近无解析场景。
五、核心结论与最佳实践
- 性能层面:高并发场景下,Druid SQL Parser 解析速度比 JSQLParser 快数百倍,CPU 占用率仅上升 5% 左右,远低于 JSQLParser 的 40%+ 涨幅;
- 选型建议:
- 低并发 / 简单 SQL 场景:可继续使用 JSQLParser(入门成本低);
- 高并发 / 复杂 SQL 场景:优先选择 Druid SQL Parser(已用 Druid 连接池)或 Apache Calcite(大数据 / 复杂优化场景);
- 集成原则:避免在高并发链路中引入性能损耗大的解析工具,优先选择与现有技术栈兼容的组件(如 Druid 生态),减少集成成本与风险。
六、扩展建议
- 对解析后的 SQL AST 操作,可参考 Druid 官方文档实现 SQL 改写、关键字校验等业务逻辑;
- 高并发场景下可增加 SQL 解析结果缓存(如 Caffeine),避免重复解析相同 SQL;
- 建议在压测阶段重点验证 SQL 解析组件的性能,提前暴露瓶颈。