高并发场景下 JSQLParser 性能瓶颈及替代方案实践

一、问题背景:高并发下的性能雪崩

在业务系统中新增 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

选型理由

  1. 项目已集成 Druid 连接池,无需额外引入依赖,无版本兼容风险;
  2. 对 MySQL/Oracle 等国内主流数据库方言支持更友好;
  3. 轻量高效,适配高并发场景的性能要求。

三.性能对比

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 曲线平稳,响应耗时接近无解析场景。

五、核心结论与最佳实践

  1. 性能层面:高并发场景下,Druid SQL Parser 解析速度比 JSQLParser 快数百倍,CPU 占用率仅上升 5% 左右,远低于 JSQLParser 的 40%+ 涨幅;
  2. 选型建议:
    • 低并发 / 简单 SQL 场景:可继续使用 JSQLParser(入门成本低);
    • 高并发 / 复杂 SQL 场景:优先选择 Druid SQL Parser(已用 Druid 连接池)或 Apache Calcite(大数据 / 复杂优化场景);
  3. 集成原则:避免在高并发链路中引入性能损耗大的解析工具,优先选择与现有技术栈兼容的组件(如 Druid 生态),减少集成成本与风险。

六、扩展建议

  1. 对解析后的 SQL AST 操作,可参考 Druid 官方文档实现 SQL 改写、关键字校验等业务逻辑;
  2. 高并发场景下可增加 SQL 解析结果缓存(如 Caffeine),避免重复解析相同 SQL;
  3. 建议在压测阶段重点验证 SQL 解析组件的性能,提前暴露瓶颈。
相关推荐
不想写bug呀1 小时前
Redis持久化:RDB与AOF
java·数据库·redis
CV_J8 小时前
安装kibana
java·elasticsearch·spring cloud·docker·容器
码农水水10 小时前
国家电网Java面试被问:TCP的BBR拥塞控制算法原理
java·开发语言·网络·分布式·面试·wpf
木风小助理11 小时前
PostgreSQL基础知识——DDL深度解析
数据库·postgresql
hanqunfeng11 小时前
(四十四)Redis8 新增的数据类型 -- Vector Set
数据库·redis·缓存
qq_3363139311 小时前
java基础-网络编程-TCP
java·网络·tcp/ip
咕噜咕噜啦啦11 小时前
Java期末习题速通
java·开发语言
盐真卿11 小时前
python2
java·前端·javascript
梦梦代码精12 小时前
BuildingAI vs Dify vs 扣子:三大开源智能体平台架构风格对比
开发语言·前端·数据库·后端·架构·开源·推荐算法