在高并发业务场景中,企业级 Spring Boot 应用常遇到以下连锁故障:
GetConnectionTimeoutException: wait millis 6000, active 100
(等待 6 秒未获取连接,当前 100 个连接被占满)。SHOW PROCESSLIST
发现 167 个 MySQL 进程,其中 100 + 个进程Time
字段显示 “3600”(已运行 1 小时),状态为Sending data
或Locked
。GetConnectionTimeoutException
该异常的直接原因是连接池无法在指定时间内提供可用连接。结合 MySQL 长进程现象,根因可归纳为以下四类(附权威验证):
故障类型 | 现象特征 | 权威依据 |
---|---|---|
连接池配置不合理 | 连接池maxPoolSize 过小(如设为 50),但业务并发量达 100TPS,导致连接池被占满 |
HikariCP 官方文档:建议maxPoolSize 为 CPU 核心数 ×2+1(参考HikariCP 配置指南) |
连接泄露 | 代码未关闭Connection /Statement ,连接池idleConnections 持续下降至 0 |
JDBC 规范:Connection 必须显式关闭(JDK7 + 推荐try-with-resources 自动回收) |
慢查询 / 长事务 | MySQL 进程Time 字段超长(如 3600 秒),Info 显示无索引的大表查询 |
MySQL 官方文档:SHOW PROCESSLIST 可定位长查询(参考MySQL 8.0 文档) |
连接有效性失效 | MySQLwait_timeout 设为 3600 秒(1 小时),但连接池未验证连接存活,导致持有无效连接 |
HikariCP 文档:需配置connection-test-query 验证连接(参考HikariCP FAQ) |
当用户已无法访问,需快速释放数据库资源:
通过以下 SQL 定位 “执行中且耗时超 600 秒” 的进程(避免终止Sleep
状态的空闲连接):
sql
SELECT ID, USER, HOST, DB, TIME, STATE, INFO
FROM information_schema.processlist
WHERE TIME > 600 AND STATE != 'Sleep';
执行KILL [进程ID]
终止长进程(注意:可能导致未提交事务回滚,需确认业务容忍度):
sql
KILL 1234; -- 终止ID为1234的长查询进程
结论:通常无需重启 Tomcat,但需满足连接池配置正确;若配置不当,可能需要重启。
若连接池(如 HikariCP)配置了连接有效性验证和超时回收机制,Tomcat 可自动回收无效连接并创建新连接:
连接有效性验证(connection-test-query
):
连接池在获取连接时,会执行轻量级 SQL(如SELECT 1
)验证连接是否存活。若 MySQL 进程已被终止,连接失效,验证失败后连接池会丢弃该连接并创建新连接。
超时回收机制(idle-timeout
/max-lifetime
):
空闲连接超过idle-timeout
会被主动关闭;连接存活超过max-lifetime
会被强制回收,避免持有老化连接。
若未配置连接有效性验证,或idle-timeout
大于 MySQL 的wait_timeout
,可能出现以下问题:
Connection
对象未被销毁,但底层 TCP 连接已断开)。SQLNonTransientConnectionException: No operations allowed after connection closed
。此时需重启 Tomcat,强制销毁所有连接池中的无效连接,并重新初始化连接池。
终止 MySQL 进程后,通过以下步骤判断是否需要重启:
验证项 | 操作方法 | 无需重启的特征 | 需要重启的特征 |
---|---|---|---|
应用日志 | 查看 Tomcat 日志(如catalina.out ) |
出现HikariPool-1 - Closing connection com.mysql.cj.jdbc.ConnectionImpl@xxx (无效连接被回收) |
出现SQLNonTransientConnectionException: No operations allowed after connection closed (无效连接被重复使用) |
连接池监控 | 访问/actuator/metrics (需启用 Spring Actuator) |
hikaricp.connections.idle (空闲连接数)逐渐上升,hikaricp.connections.active (活跃连接数)下降 |
hikaricp.connections.idle 持续为 0,hikaricp.connections.pending (等待连接数)大于 0 |
GetConnectionTimeoutException
)通过以下配置平衡连接利用率与稳定性(参考 HikariCP 官方推荐):
properties
# application.properties
spring.datasource.hikari.maximum-pool-size=20 # CPU核心数×2+1(如4核设为9,高并发可适当调大)
spring.datasource.hikari.minimum-idle=5 # 最小空闲连接数(建议≤maxPoolSize)
spring.datasource.hikari.connection-timeout=30000 # 连接获取超时时间(30秒,避免短时间重试)
spring.datasource.hikari.idle-timeout=1800000 # 空闲连接超时(30分钟,小于MySQL的wait_timeout)
spring.datasource.hikari.max-lifetime=3600000 # 连接最大存活时间(1小时,避免连接老化)
spring.datasource.hikari.leak-detection-threshold=30000 # 连接泄露检测(30秒未关闭则日志报警)
spring.datasource.hikari.connection-test-query=SELECT 1 # 验证连接存活(避免持有MySQL已关闭的连接)
通过以下参数从数据库层拦截长耗时操作(参考 MySQL 8.0 官方文档):
ini
# my.cnf(MySQL配置文件)
[mysqld]
max_execution_time=5000 # 单个查询最大执行时间(5秒,超时自动终止)
wait_timeout=3600 # 空闲连接超时(1小时,需大于连接池的idle-timeout)
interactive_timeout=3600 # 交互式连接超时(与wait_timeout一致)
slow_query_log=1 # 开启慢查询日志(记录执行超2秒的查询)
slow_query_log_file=/var/log/mysql/slow.log
long_query_time=2 # 慢查询阈值(2秒)
错误代码示例(未关闭资源):
java
// 反例:未使用try-with-resources,连接可能未关闭
Connection conn = dataSource.getConnection();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM big_table");
// 业务逻辑...
// 未显式关闭conn/stmt/rs!
正确代码示例(自动关闭资源):
java
// 正例:JDK7+使用try-with-resources自动关闭
try (Connection conn = dataSource.getConnection();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM big_table")) {
// 业务逻辑...
} // 自动关闭conn/stmt/rs(无需finally)
通过 Spring 的@Transactional
注解设置事务超时时间(单位:秒),超时自动回滚并释放连接(参考Spring 事务文档):
java
@Service
public class OrderService {
// 事务超时30秒(包含查询、锁等待等所有操作)
@Transactional(timeout = 30)
public void updateOrder() {
// 执行可能耗时的SQL(如更新大表)
orderMapper.updateLargeTable();
}
}
手动终止 MySQL 长进程后,通过以下方式确认连接池自动重连:
HikariPool-1 - Closing connection
(无效连接被回收)或HikariPool-1 - Added connection
(新连接创建)日志。/actuator/metrics/hikaricp.connections.active
(活跃连接数)是否下降,hikaricp.connections.idle
(空闲连接数)是否上升。active
、pending
、idle
)。pt-query-digest
分析慢查询日志,定位高频慢 SQL(示例命令:pt-query-digest /var/log/mysql/slow.log > slow_report.txt
)。通过 “临时终止→配置优化→代码修复→监控预防” 四步,可彻底解决长耗时进程引发的系统故障: