当前位置: 代码网 > it编程>编程语言>Java > Java项目添加慢SQL查询工具的实践指南

Java项目添加慢SQL查询工具的实践指南

2026年03月01日 Java 我要评论
日期:2026-02-28场景:校园项目访问 oracle 数据库,部分查询慢,偶尔出现 hikari 连接池超时,需要定位慢 sql 并统计分析。背景问题项目在高并发或大数据量查询时,经常出现:ca

日期:2026-02-28

场景:校园项目访问 oracle 数据库,部分查询慢,偶尔出现 hikari 连接池超时,需要定位慢 sql 并统计分析。

背景问题

项目在高并发或大数据量查询时,经常出现:

caused by: org.springframework.jdbc.cannotgetjdbcconnectionexception: 
failed to obtain jdbc connection; request timed out after 30000ms

初步排查发现:

  • oracle 数据库连接池配置较小,慢 sql 导致连接占用过久
  • 业务查询中存在 like、group by、分页等操作,大数据量下执行慢

目标:快速定位慢 sql,统计出现次数和耗时,便于优化

工具选型

选择 p6spy + logback

  • p6spy 拦截 jdbc 调用,透明记录 sql
  • 可配置 慢 sql 阈值(executionthreshold)
  • 性能损耗极小

spring boot 配置示例

spring:
  datasource:
    driver-class-name: com.p6spy.engine.spy.p6spydriver
    url: jdbc:p6spy:oracle:thin:@// **********  /orcl
    username: *********
    password: **********************

spy.properties

appender=com.p6spy.engine.spy.appender.slf4jlogger
executionthreshold=1000      # 超过1秒才记录
logmessageformat=com.p6spy.engine.spy.appender.singlelineformat
resultsetloggable=false

logback.xml

<appender name="slow_sql_file" class="ch.qos.logback.core.rolling.rollingfileappender">
    <file>${log_path}/slow-sql.log</file>
    <rollingpolicy class="ch.qos.logback.core.rolling.sizeandtimebasedrollingpolicy">
        <filenamepattern>${log_path}/slow-sql-%d{yyyy-mm-dd}.%i.log</filenamepattern>
        <maxfilesize>50mb</maxfilesize>
        <maxhistory>30</maxhistory>
    </rollingpolicy>
    <encoder>
        <pattern>%d{yyyy-mm-dd hh:mm:ss.sss} %msg%n</pattern>
    </encoder>
</appender>

<logger name="p6spy" level="info" additivity="false">
    <appender-ref ref="slow_sql_file"/>
</logger>

这样就可以单独输出慢 sql 到 slow-sql.log,不干扰 info.logerror.log

分析思路

慢 sql 日志样例:

2026-02-28 17:03:13.617 1772269393616|1073|statement|connection 0|url jdbc:p6spy:oracle:thin:@//...|select count(*) ...

字段说明:

字段含义
1073sql 执行耗时(ms)
statementsql 类型
connection 0jdbc 连接编号
select ...实际执行 sql

分析方法:

  • 找出耗时 > 阈值的 sql
  • 判断 sql 是否包含 likegroup by 或分页
  • 统计出现次数、最大耗时、平均耗时
  • 结合连接池监控判断是否是连接占用导致超时

java 分析工具实现

可以直接在 java 环境运行,无需 python。

import java.io.*;
import java.util.*;
import java.util.regex.*;

public class slowsqlanalyzer {

    public static void main(string[] args) throws ioexception {
        if (args.length < 1) {
            system.out.println("usage: java slowsqlanalyzer <slow-sql.log>");
            return;
        }

        string logfile = args[0];

        // 正则匹配 p6spy sql 日志行
        pattern sqlpattern = pattern.compile("\\|\\d+\\|statement\\|.*?\\|(select|insert|update|delete).*", pattern.case_insensitive);

        map<string, sqlstats> statsmap = new hashmap<>();

        try (bufferedreader reader = new bufferedreader(new filereader(logfile))) {
            string line;
            while ((line = reader.readline()) != null) {
                string[] parts = line.split("\\|", 6);
                if (parts.length < 6) continue;
                int exectime;
                try {
                    exectime = integer.parseint(parts[1]);
                } catch (numberformatexception e) {
                    continue;
                }
                string sqltext = parts[5].trim();
                matcher matcher = sqlpattern.matcher(line);
                if (matcher.find()) {
                    // 用前200字符作为 key,避免重复太多
                    string key = sqltext.length() > 200 ? sqltext.substring(0, 200) : sqltext;
                    sqlstats s = statsmap.getordefault(key, new sqlstats(sqltext));
                    s.count++;
                    s.totaltime += exectime;
                    s.maxtime = math.max(s.maxtime, exectime);
                    s.like = s.like || sqltext.touppercase().contains("like");
                    s.groupby = s.groupby || sqltext.touppercase().contains("group by");
                    statsmap.put(key, s);
                }
            }
        }

        // 输出统计结果
        system.out.printf("%5s | %12s | %12s | %6s | %8s | %s%n",
                "次数", "平均耗时(ms)", "最大耗时(ms)", "like", "group by", "sql示例前200字符");
        system.out.println("--------------------------------------------------------------------------------------------------------");

        statsmap.values().stream()
                .sorted((a,b) -> long.compare(b.totaltime, a.totaltime))
                .foreach(s -> {
                    long avg = s.totaltime / s.count;
                    system.out.printf("%5d | %12d | %12d | %6b | %8b | %s%n",
                            s.count, avg, s.maxtime, s.like, s.groupby, s.sqlsnippet);
                });
    }

    static class sqlstats {
        string sqlsnippet;
        int count = 0;
        long totaltime = 0;
        long maxtime = 0;
        boolean like = false;
        boolean groupby = false;

        sqlstats(string sql) {
            this.sqlsnippet = sql;
        }
    }
}

执行方法

javac slowsqlanalyzer.java
java slowsqlanalyzer /data/javaapp/rest-tonp-realization/logs/slow-sql.log

输出示例:

次数 | 平均耗时(ms) | 最大耗时(ms) | like | group by | sql示例前200字符
--------------------------------------------------------------------------------------------------------
3    | 1234         | 1567         | true | true     | select ... from certification ...
2    | 1050         | 1200         | false| true     | select ... from product_type_new ...

收获与优化建议

可视化慢 sql

  • 统计出现次数、最大耗时、平均耗时
  • 可快速定位热点 sql

sql 优化方向

  • 对频繁 like 查询字段加索引或改写逻辑
  • 对大数据量 group by / 分页查询优化(索引 + 分批)
  • 避免长事务占用连接,结合 hikari 连接池配置

整体收益

  • 每条慢 sql都可被记录和统计
  • 高并发环境下连接池超时问题更容易排查

总结:

通过 p6spy + logback + java 分析工具,可以快速定位慢 sql 并统计,为性能优化提供数据支持。

到此这篇关于java项目添加慢sql查询工具的实践指南的文章就介绍到这了,更多相关java添加慢sql查询内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com