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

 更新时间:2026年03月01日 09:04:27   作者:有志  
这篇文章主要为大家详细介绍了Java项目添加慢SQL查询工具的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下

日期: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查询内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • SpringBoot多环境配置方式的新手教程

    SpringBoot多环境配置方式的新手教程

    我们平时做项目的时候,一般都会分几套环境,每一套环境的配置都是不一样的,所以这篇文章就来为大家详细介绍一下SpringBoot多环境配置方式,希望对大家有所帮助
    2023-11-11
  • Spring MVC中自带的跨域问题解决方法

    Spring MVC中自带的跨域问题解决方法

    最近做一个微信小项目遇到一个跨域问题,就是我的前端和后端是放在不同的服务器上的,然后使用opst请求的时候报错,所以通过查找相关的资料终于解决了,下面这篇文章主要给大家介绍了关于Spring MVC中自带的跨域问题解决方法的相关资料,需要的朋友可以参考下。
    2017-09-09
  • javax.xml.bind.JAXBContext操作XML的实现示例

    javax.xml.bind.JAXBContext操作XML的实现示例

    JAXB是Java标准API,用于Java对象与XML的相互转换,通过注解定义映射规则,本文就来详细的介绍一下javax.xml.bind.JAXBContext操作XML的实现,感兴趣的可以了解一下
    2025-12-12
  • C# log4net使用案例详解

    C# log4net使用案例详解

    这篇文章主要介绍了C# log4net使用案例详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-08-08
  • java使用jacob.jar将word转pdf

    java使用jacob.jar将word转pdf

    这篇文章主要为大家详细介绍了java利用jacob.jar将word转pdf,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-12-12
  • Java 入门图形用户界面设计之事件处理上

    Java 入门图形用户界面设计之事件处理上

    图形界面(简称GUI)是指采用图形方式显示的计算机操作用户界面。与早期计算机使用的命令行界面相比,图形界面对于用户来说在视觉上更易于接受,本篇精讲Java语言中关于图形用户界面的事件处理
    2022-02-02
  • SpringBoot整合EasyExcel实现复杂Excel表格的导入导出

    SpringBoot整合EasyExcel实现复杂Excel表格的导入导出

    这篇文章主要为大家详细介绍了SpringBoot如何整合EasyExcel实现复杂Excel表格的导入导出功能,文中的示例代码讲解详细,感兴趣的小伙伴可以参考下
    2023-11-11
  • 简单总结单例模式的4种写法

    简单总结单例模式的4种写法

    今天带大家学习java的相关知识,文章围绕着单例模式的4种写法展开,文中有非常详细的介绍及代码示例,需要的朋友可以参考下
    2021-06-06
  • 基于SpringBoot实现单账号登录控制功能

    基于SpringBoot实现单账号登录控制功能

    在很多业务场景中,我们需要控制单个账号的登录数量,本文介绍一种轻量级实现方案,灵活支持单账号单登录和多登录两种模式,有需要的小伙伴可以了解下
    2025-12-12
  • Spring扩展接口知识总结

    Spring扩展接口知识总结

    今天带大家学习Java Spring的相关知识,文中对Spring扩展接口作了非常详细的介绍及代码示例,对正在学习java的小伙伴们有很好地帮助,需要的朋友可以参考下
    2021-05-05

最新评论