应查INFORMATION_SCHEMA.INNODB_TRX中trx_state='RUNNING'且trx_query为空的事务,这通常表明Spring事务卡在非DB操作;需结合PROCESSLIST和jstack验证线程状态,并排查@Transactional失效场景。

查 INNODB_TRX 里 trx_state = 'RUNNING' 且 trx_query 为空的事务
这类事务最可疑:MySQL 明明开了事务(trx_autocommit = 0),但 trx_query 是空的,说明没在执行 SQL,而是在等别的事。Spring 声明式事务一开启,连接就自动关 autocommit,线程却可能卡在 Feign 调用、Thread.sleep()、大文件读取或 JSON 解析上。直接执行:
SELECT trx_id, trx_started, trx_state, trx_mysql_thread_id, trx_query FROM information_schema.INNODB_TRX WHERE TIME_TO_SEC(TIMEDIFF(NOW(), trx_started)) > 30 AND trx_state = 'RUNNING' AND (trx_query IS NULL OR trx_query = '');
重点关注 trx_started 时间戳和 trx_mysql_thread_id,后者要和下一步的 PROCESSLIST 对齐。
比对 PROCESSLIST 中对应线程的 COMMAND 和 STATE
如果 INNODB_TRX 里看到一个 90 秒的空事务,它的 trx_mysql_thread_id 是 12345,那就立刻查:
SELECT ID, USER, HOST, DB, COMMAND, TIME, STATE, INFO FROM information_schema.PROCESSLIST WHERE ID = 12345;
常见陷阱现象:
-
COMMAND = 'Sleep'且TIME > 60:线程早就不在事务方法里了,但连接没释放——大概率是事务方法里调了同步远程接口,对方超时或挂了,Spring 还以为它在“干活” -
COMMAND = 'Query'但INFO是空或只显示COMMIT:说明事务已执行完 SQL,正卡在提交前的某处(比如自定义TransactionSynchronization回调里) -
HOST是应用服务器 IP,但USER不是你配置的数据库账号:说明连接被复用或代理穿透,需检查 HikariCP 的allowPoolSuspension或中间件配置
用 jstack 匹配线程栈与 MySQL 线程 ID
PROCESSLIST.ID 和 JVM 线程 ID 并不相等,但可通过 java.lang.Thread.getId() 间接关联。更实用的做法是:
先用 jstack <pid> | grep -A 10 -B 2 'YourService\.updateOrder' 找到正在执行事务方法的线程,记下它的 tid(十六进制);再用 jstack 输出里的 java.lang.Thread.State: RUNNABLE 行下方堆栈,看是否包含 org.springframework.transaction 和你业务方法名;最后对比该线程的 os_prio 和 nid 是否与 PROCESSLIST.ID 接近(Linux 下常有偏差,但时间戳应强一致)。
若发现线程堆栈停在 RestTemplate.execute 或 FileInputStream.read,基本坐实是事务内干了不该干的事。
确认事务边界是否被 Spring AOP 绕过
如果 INNODB_TRX 里压根看不到对应事务,不是长,是“没生效”。典型绕过场景:
- 方法是
private或protected:Spring AOP 默认不代理非public方法,MySQL 连接始终autocommit = 1 - 类内
this.doSomething()调用另一个@Transactional方法:走的是原始对象,没经过代理,事务切面完全失效 - 用
new XxxService()实例化对象:不在 Spring 容器管理范围,代理对象根本不存在 - 事务方法被
try-catch吞掉异常且未显式TransactionAspectSupport.currentTransactionStatus().setRollbackOnly():事务不会回滚,但也不会提前结束,连接一直挂着
真正难排查的,是那种 INNODB_TRX 里有记录、PROCESSLIST 显示 Sleep、jstack 却找不到对应线程的情况——往往意味着连接被 HikariCP 强制回收后又重连,但旧事务没清理干净,得结合 hikari.leak-detection-threshold 日志确认。


















