mysql连接池枯竭表现为应用无法获取连接、超时陡增,根本原因是应用层未及时归还连接;需分层配置hikaricp的connection-timeout、jdbc的sockettimeout、statement的querytimeout及mysql服务端max_execution_time等超时机制。

MySQL连接池枯竭的典型表现和根本原因
连接池枯竭不是MySQL服务端的问题,而是应用层连接没及时归还——长SQL执行时间过长,Connection被占着不放,新请求排队等不到空闲连接。常见现象包括:应用日志里反复出现 Cannot get JDBC connection 或 HikariPool-1 - Connection is not available,数据库监控里 Threads_running 没爆,但应用侧超时陡增。
关键点在于:MySQL本身不管理“连接池”,它只管客户端连进来后执行语句;真正要设超时的,是应用使用的连接池(如HikariCP、Druid)和JDBC驱动层。
HikariCP + MySQL Connector/J 的超时组合配置
单靠 socketTimeout 或单靠 connectionTimeout 都不够。必须分层设防:
-
connection-timeout(HikariCP):获取连接的等待上限,建议设30000(30秒),避免线程卡死在“拿连接”这步 -
socket-timeout(JDBC URL参数):网络读写超时,影响executeQuery()等调用,设socketTimeout=10000(10秒)可中断挂住的查询 -
query-timeout(JDBCStatement.setQueryTimeout()):由驱动发KILL QUERY终止正在执行的SQL,需配合MySQL服务端wait_timeout和权限(用户需有PROCESS权限) - 别漏掉
max-lifetime和idle-timeout:防止连接因MySQL主动断开(如wait_timeout=28800)后还在池里残留,引发Connection reset
示例Hikari配置(Spring Boot application.yml):
spring:
datasource:
hikari:
connection-timeout: 30000
max-lifetime: 1800000
idle-timeout: 600000
jdbc-url: jdbc:mysql://localhost:3306/test?socketTimeout=10000&connectTimeout=5000
MySQL服务端配合项:不能只靠客户端设超时
客户端超时再严,也挡不住恶意或低效SQL反复重试。服务端要兜底:
-
max_execution_time(MySQL 5.7.8+):对单条SELECT强制加执行时间限制,单位毫秒,可全局设或会话级动态设:SET SESSION max_execution_time = 5000 -
wait_timeout和interactive_timeout:控制空闲连接存活时间,建议设为600(10分钟),避免连接长期空转占坑 - 慎用
long_query_time:它只记录慢日志,不终止SQL,不能防枯竭 - 检查
SHOW PROCESSLIST中大量State: Sending data或Copying to tmp table的线程——这些才是真正在耗连接的长SQL源头
最容易被忽略的三个细节
很多团队配了超时还是出问题,往往栽在这几个地方:
- MyBatis默认不启用
queryTimeout:除非显式在<select timeout="10"></select>里写,或全局配置defaultStatementTimeout,否则setQueryTimeout()根本不调用 - JDBC URL里的
socketTimeout对executeUpdate()也生效,但某些旧版Connector/J(如5.1.x)对批量操作支持不完整,升级到8.0.33+更稳 - Spring声明式事务中,
@Transactional(timeout = 5)控制的是事务生命周期,不是SQL执行时间——它不会触发KILL QUERY,只会在事务管理器层面抛异常回滚
真正的防线是分层的:连接获取超时 → 网络传输超时 → SQL执行超时 → 服务端强制熔断。少一层,就可能让一条慢SQL拖垮整个池。











