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

查 MySQL 当前活跃长事务:INNODB_TRX
事务没提交不是“看不见”,而是卡在了 MySQL 内部。直接连上数据库执行:
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;
这条语句会捞出持续超 30 秒的事务。重点关注:trx_state = 'RUNNING' 且 trx_query 是空(说明事务已开启但没执行 SQL)——这大概率是 Spring 事务方法卡在非 DB 操作上,比如远程调用、大文件读取或 Thread.sleep()。
若 trx_query 显示的是一个简单 UPDATE 或 INSERT,但耗时异常长,要结合应用日志看是不是锁等待(trx_state = 'LOCK WAIT'),而非事务本身失效。
确认事务是否真由 Spring 控制:trx_autocommit = 0 但无对应应用线程
Spring 声明式事务开启后,MySQL 连接的 autocommit 会被设为 0。你可以查当前连接状态:
SELECT ID, USER, HOST, DB, COMMAND, TIME, STATE, INFO FROM information_schema.PROCESSLIST WHERE COMMAND != 'Sleep' AND TIME > 30;
如果看到一堆 COMMAND = 'Sleep' 但 TIME > 60 的连接,且它们的 DB 是你的业务库,就说明连接被 Spring 拿着没释放——事务还没结束,但线程可能早就在干别的事(比如等 Feign 超时)。这时再用 jstack 对比线程 ID 和 PROCESSLIST.ID,能确认是否线程卡在事务方法内部。
常见陷阱:HikariCP 连接池里连接数打满,新请求进不来,但旧连接一直挂着,INNODB_TRX 看起来像“很多长事务”,其实是同一个事务拖久了,把池子占死。
验证事务边界是否被绕过:this.xxx() 或非 public 方法
事务“失效”不等于“没走事务逻辑”,而是根本没进事务切面。最典型表现就是:你加了 @Transactional,但 INNODB_TRX 里压根看不到对应事务。
-
@Transactional方法是private/protected—— Spring AOP 不代理,MySQL 根本不会关autocommit - 类内
this.doSomething()调用另一个@Transactional方法 —— 走的是原始对象,不是代理,事务拦截器完全不触发 - 方法没被 Spring 容器管理(缺
@Service)或用了 new 实例 —— 代理对象都不存在
快速验证:在疑似事务方法第一行加 System.out.println("in tx method");,同时打开 Hikari DEBUG 日志。如果日志里没出现 HikariPool - Connection acquired,但控制台又打印了那行输出,基本确定事务注解根本没生效。
区分“长事务”和“事务失效”:看连接持有时间是否 ≈ 方法执行时间
真正的问题往往不是事务“失效”,而是事务“太长”。用 Hikari DEBUG 日志对齐时间戳:
搜索 HikariPool-1 - Connection acquired 和紧随其后的 HikariPool-1 - Connection returned to pool,算差值;再对比同一请求的全链路耗时(如 Spring Boot Actuator 的 /actuator/metrics/http.server.requests)。如果两者基本一致,说明事务确实从头持到尾——问题不在失效,而在业务逻辑不该放在事务里。
容易被忽略的一点:@Transactional 的传播行为默认是 REQUIRED,如果外层已有事务,内层方法会复用同一个连接。这意味着一个“看似短”的方法,可能因为嵌套在更长的事务里,间接导致连接长期占用。


















