根本原因是未关闭的事务而非连接池过小;需查INFORMATION_SCHEMA.INNODB_TRX中TRX_STATE='RUNNING'且超时的事务,结合PROCESSLIST定位应用来源,并启用连接泄漏检测与autocommit校验。

直接结论:不是连接池太小,而是有事务开着没关——查 INFORMATION_SCHEMA.INNODB_TRX 才能定位真凶。
查 RUNNING 状态的长事务,别只看 PROCESSLIST
很多人一上来就 SHOW PROCESSLIST,看到一堆 Sleep 就以为没事。错。事务未提交时,线程可能已退到 Sleep 状态,但连接仍被绑定在事务上,INFORMATION_SCHEMA.INNODB_TRX 才是唯一权威来源。
-
TRX_STATE = 'RUNNING'且TRX_STARTED超过 60 秒,基本可判定为泄漏源头 -
TRX_QUERY为空不等于安全——可能是 DML 执行完但没COMMIT或ROLLBACK -
TRX_STATE = 'LOCK WAIT'时,说明它在等锁,得顺着TRX_WAITING_TRX_ID去找真正 hold 锁的那个RUNNING事务
用 THREAD_ID 关联 PROCESSLIST 定位应用来源
光知道事务 ID 没用,得知道是谁发起的。用 TRX_MYSQL_THREAD_ID 去关联 INFORMATION_SCHEMA.PROCESSLIST,才能拿到真实线索:
-
HOST字段暴露客户端 IP 和端口,比如app-order-02:49821,可快速锁定服务实例 -
COMMAND != 'Sleep'且TIME很大,大概率是应用开了事务后挂了或逻辑卡住 -
USER是否匹配你应用配置的连接池账号(如统一用app_rw),能排除运维误操作
检查 autocommit 设置,别让代码“以为自己在自动提交”
很多“未提交”根本不是程序员忘了写 commit,而是整个连接默认关闭了自动提交:
- 执行
SELECT @@autocommit:返回0表示当前会话处于手动事务模式 - 执行
SELECT @@global.autocommit:若为0,所有新连接都默认进手动事务,风险极高 - Java 中
HikariCP的dataSource.setConnectionInitSql("SET autocommit = 1")可强制重置
开连接泄露检测,别等连接池崩了才报警
连接池本身不感知事务是否结束,靠日志主动暴露问题才是关键:
-
HikariCP必须设leak-detection-threshold=60000(单位毫秒),超 60 秒未归还就打 WARN 日志+堆栈 -
Druid需配removeAbandonedOnBorrow=true+removeAbandonedTimeoutMillis=60000 - 日志里一旦出现
Connection leak detection triggered,立刻查对应堆栈——90% 是try里漏了finally或异常分支没rollback
最常被忽略的一点:wait_timeout 和 interactive_timeout 对未提交事务完全无效。事务开着,连接就永远“活跃”,超时参数压根不触发。别调低它们来治标,得从代码和监控双路堵住泄漏源头。


















