查未提交事务不能只看INNODB_TRX,必须关联PROCESSLIST验证连接状态:通过trx_mysql_thread_id与p.ID连接,过滤COMMAND≠'Sleep'且trx_state='RUNNING'的活跃线程,结合USER、HOST、conn_time定位连接池泄漏。

查未提交事务不能只看 INNODB_TRX,得关联连接池线程状态
单纯执行 SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX 只能看到事务本身,但无法判断它是否来自连接池中“被复用却没清理干净”的连接。真实场景中,很多未提交事务是应用从连接池取到一个旧连接(上个请求没正确结束事务),直接执行新 SQL 导致的。必须把事务线程 ID 和连接池当前活跃连接对上。
推荐组合查询:
SELECT t.trx_id, t.trx_mysql_thread_id, t.trx_started, t.trx_state, t.trx_query, p.ID AS processlist_id, p.USER, p.HOST, p.DB, p.COMMAND, p.TIME AS conn_time, p.STATE FROM INFORMATION_SCHEMA.INNODB_TRX t JOIN INFORMATION_SCHEMA.PROCESSLIST p ON t.trx_mysql_thread_id = p.ID WHERE t.trx_state = 'RUNNING' AND p.COMMAND != 'Sleep' ORDER BY t.trx_started;
-
trx_state = 'RUNNING'排除已提交或回滚但尚未释放的伪活跃事务 - 过滤掉
COMMAND = 'Sleep'的线程,它们大概率是空闲连接,不持有有效事务 - 重点看
USER和HOST是否匹配你应用的连接池配置(比如统一用app_pool用户) -
conn_time显著大于业务正常耗时(如 > 30 秒),基本可判定为泄漏
连接池配置不当会掩盖事务泄漏,leak-detection-threshold 必须开
HikariCP、Druid 等主流连接池默认不检测连接泄漏,导致事务卡住数小时都无人发现。MySQL 层面看到的是“正常运行中”的事务,而应用层以为连接已归还。
- HikariCP 必须显式设置:
leak-detection-threshold=60000(单位毫秒),超 60 秒未归还就打印 WARN 日志并带上堆栈 - Druid 对应参数是:
removeAbandonedOnBorrow=true+removeAbandonedTimeoutMillis=60000 - 别依赖
maxLifetime或idleTimeout自动回收——它们只管连接对象生命周期,不管事务是否提交 - 开启后日志里出现类似
Connection leak detection triggered就要立刻查对应代码段:是不是 try 块里没进 finally、或者异常分支漏了connection.rollback()
修复时不能直接 KILL 线程,先确认是否还在被应用使用
盲目执行 KILL 12345 可能导致应用端报 Communications link failure,甚至引发二次异常(比如重试逻辑叠加)。尤其在微服务调用链中,一个 KILL 可能让上游误判为超时重试,放大问题。
- 先用
SHOW PROCESSLIST看该线程的STATE:如果是Locked或Sending data,说明还在干活,等它自然结束更安全 - 如果
STATE是Query且INFO字段为空(即没在执行 SQL),但TIME很大,基本可确定卡在事务里,此时再KILL - 生产环境建议加条件:只
KILL持续时间 > 300 秒、且USER匹配应用账号的线程,避免误杀监控或备份连接 - 执行
KILL后,立刻检查应用日志——如果大量出现Transaction is not active或Connection reset,说明有代码依赖该连接做后续操作,得回溯修复
根本解法不是调参,而是让每个连接在归还前强制清理
靠 DBA 定期巡检 INNODB_TRX 是被动防御。真正可靠的方案,是在连接归还连接池前,由连接池主动执行清理语句。
- HikariCP 支持
connection-init-sql=SET autocommit = 1; COMMIT;(注意:MySQL 8.0.3+ 才支持多语句) - 更通用的做法是配置
connection-test-query=SELECT 1+test-while-idle=true,并在测试 SQL 前插入清理逻辑(需自定义DataSource包装器) - 最稳妥的仍是代码层规范:所有手动事务必须用
try-finally包裹,finally中明确调用connection.rollback()或commit(),而不是依赖连接关闭自动回滚 - 容易被忽略的一点:
AUTOCOMMIT=1的连接执行START TRANSACTION后,仍需显式COMMIT;连接池不会帮你补这个动作
事务泄漏的根因几乎总在应用代码里,而不是 MySQL 配置。连接池只是放大器——它让一个本该 1 秒内结束的事务,变成持续占用资源数小时的隐患。查 INNODB_TRX 是起点,但定位到具体哪行 Java/Python 代码忘了 commit,才是终点。


















