java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Java添加慢SQL查询

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

作者:有志

这篇文章主要为大家详细介绍了Java项目添加慢SQL查询工具的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下

日期:2026-02-28

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

背景问题

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

Caused by: org.springframework.jdbc.CannotGetJdbcConnectionException: 
Failed to obtain JDBC Connection; request timed out after 30000ms

初步排查发现:

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

工具选型

选择 P6Spy + Logback

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

分析方法:

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 优化方向

整体收益

总结:

通过 P6Spy + Logback + Java 分析工具,可以快速定位慢 SQL 并统计,为性能优化提供数据支持。

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

您可能感兴趣的文章:
阅读全文