查事务锁等待应优先使用information_schema.innodb_trx,重点关注trx_started、trx_state、trx_mysql_thread_id和trx_query字段;若trx_started久远且trx_state为running,大概率是长事务持锁不放。

查哪些事务卡住了锁
直接查 INFORMATION_SCHEMA.INNODB_TRX 是最准的入口,它能告诉你谁在跑、跑了多久、有没有在等锁、锁了什么表。重点看这几个字段:trx_started(启动时间)、trx_state(状态,RUNNING 或 LOCK WAIT)、trx_mysql_thread_id(线程 ID)、trx_query(当前 SQL,可能为 NULL)。如果发现某个事务 trx_started 是几小时前的,且 trx_state 是 RUNNING,基本就是它占着锁不放。
为什么 processlist 看不到等待链
SHOW FULL PROCESSLIST 只显示连接线程的当前命令,但锁等待本身不体现在「正在执行」状态里——比如两个事务都在 COMMIT 阶段卡住,processlist 里都显示 NULL 或空查询;又或者事务执行完 UPDATE 但没 COMMIT,processlist 里只显示「Sleep」,根本看不出它还持着行锁。这时候光靠 processlist 会漏掉真凶,必须结合 INNODB_TRX 和 INNODB_LOCK_WAITS 才能串起等待关系。
kill 前先确认是否安全
拿到 trx_mysql_thread_id 后别急着 KILL,先做两件事:
- 用
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS WHERE lock_trx_id = 'xxx'查它锁了哪些索引和记录(lock_table、lock_index、lock_rec) - 用
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS WHERE requesting_trx_id = 'xxx'看有没有其他事务正等它释放锁
如果它是只读事务(trx_query 为空,trx_rows_modified = 0),且没人在等它,kill 影响最小;但如果它刚改了几万行还没提交,kill 会导致回滚,IO 压力可能瞬间拉高。
避免下次再被卡住
临时 kill 只是止痛,根子在应用层没管好事务生命周期:
- 检查代码里是否有
begin后没配对commit/rollback,尤其注意异常分支、超时逻辑、连接池回收前的状态清理 - Spring 项目重点看
@Transactional的传播行为和 rollbackFor 配置,避免事务意外延长 - 把
autocommit=1设为默认,除非明确需要多语句事务 - 监控
innodb_trx表中trx_started超过 30 秒的事务,自动告警
真正难处理的不是锁超时本身,而是那些 trx_query 为空、trx_operation_state 为 NULL、却死死攥着锁几十分钟的“幽灵事务”——它们往往藏在连接泄漏或框架隐式事务里,得从连接池日志和应用 trace 入手才抓得住。











