这是事务等待锁超时被mysql终止,需定位长期未提交的持锁事务;通过select * from information_schema.innodb_trx where trx_state = 'running' and trx_started查出“挂尸”事务。

这不是死锁,也不是数据库卡死,而是你的事务在等锁——等满 innodb_lock_wait_timeout(默认 50 秒)后被 MySQL 主动终止;真正该处理的,是那个“开了很久却没提交”的事务,不是报错的那个。
怎么看谁在持锁不放?用 INNODB_TRX 找“挂尸”事务
别查慢 SQL 日志,Lock wait timeout exceeded 的语句本身可能 0.1 秒就执行完,只是前面卡在等锁。关键看谁开着事务不动弹:
- 运行
SELECT * FROM information_schema.INNODB_TRX WHERE trx_state = 'RUNNING' AND trx_started ,重点盯 <code>trx_started超过 1 分钟的记录 - 检查
trx_query是否为空(空挂事务),或是否在执行SLEEP()、HTTP 调用、大循环等非数据库操作 - 拿
trx_mysql_thread_id去比对SHOW PROCESSLIST,如果对应线程状态是Sleep且Time很大(比如几百秒),八成就是它 - Spring 应用特别注意:
@Transactional方法里调了 RPC 却没设超时,或catch异常后忘了rollback,极易制造这种“幽灵事务”
怎么确认谁在等谁?联查 INNODB_LOCK_WAITS
单看 INNODB_TRX 只知道有长事务,但不知道它阻塞了谁。必须关联锁等待关系:
- MySQL 5.7/8.0 兼容写法:
SELECT r.trx_id waiting_trx_id, r.trx_query waiting_query, b.trx_id blocking_trx_id, b.trx_query blocking_query FROM INFORMATION_SCHEMA.INNODB_TRX r INNER JOIN INFORMATION_SCHEMA.INNODB_LOCK_WAITS w ON r.trx_id = w.requesting_trx_id INNER JOIN INFORMATION_SCHEMA.INNODB_TRX b ON b.trx_id = w.blocking_trx_id - MySQL 8.0+ 推荐用
sys.innodb_lock_waits视图,字段更直白:直接有waiting_pid和blocking_pid - 如果查不到结果,说明锁已释放,或是刚发生了死锁(此时日志里会有
Deadlock found,且 InnoDB 已自动回滚一个事务)
KILL 谁?为什么不能 KILL 报错线程?
报错线程只是“等锁失败”,KILL 它只让当前请求失败,阻塞源还在,下个请求照常超时。真正要动的是持锁方:
- 从上一步查询结果中拿到
blocking_trx_id对应的trx_mysql_thread_id(或blocking_pid) - 执行
KILL [thread_id]—— 但先确认这个线程不是User = 'system user'(主从同步线程)或关键定时任务 - 如果
blocking_query是调试残留、死循环或网络卡住的 RPC,KILL 合理;如果是正常业务长事务,得先查清逻辑再决定是否干预 - 盲目全局调大
innodb_lock_wait_timeout(比如设成 300)只是掩盖问题:它不解决“谁在占着不放”,只会让故障延迟暴露、堆积更多等待者
最易被忽略的一点:错误发生后,持有锁的事务仍处于活跃状态,且不会主动报错或退出。它可能安静地挂着几小时,持续阻塞所有后续写请求——所以排查必须聚焦在“活着但不动”的事务上,而不是反复重试或调参。











