真正要查的是“开了很久却没提交”的事务,需通过INNODB_TRX筛选trx_state='RUNNING'且trx_started早于当前时间300秒的事务,并结合PROCESSLIST中HOST、USER及Sleep状态综合判断持锁源头。

真正要查的是“开了很久却没提交”的事务,不是报错的那条 SQL。 Lock wait timeout exceeded 错误本身只是结果,背后几乎总是某个未提交事务在持锁不放——它可能已空挂几分钟,而你看到的报错语句只是第一个等不及的受害者。
查 INNODB_TRX 找出长时间 RUNNING 的事务
这是最直接的入口。别只盯着报错线程,重点筛那些“启动早、没动作、还活着”的事务:
SELECT * FROM information_schema.INNODB_TRX WHERE trx_state = 'RUNNING' AND trx_started —— 查启动超 1 分钟仍为 RUNNING 的事务- 重点关注
trx_query为空的记录:说明事务开了但没执行任何语句,极大概率是应用异常中断或忘记COMMIT/ROLLBACK - 如果
trx_isolation_level = 'REPEATABLE READ',哪怕只执行过一条SELECT,事务一启就持有 MDL 锁和可能的间隙锁 - 记下
trx_mysql_thread_id,后续要和PROCESSLIST关联确认来源
联查 INNODB_LOCK_WAITS 定位谁在堵谁
单看 INNODB_TRX 只知道“有长事务”,但不知道它是否正在阻塞别人。必须通过锁等待关系确认因果链:
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;- 返回结果中,
blocking_query是真凶;如果它是空值或类似SELECT ... FROM t WHERE id = ?这种简单语句,基本可断定是事务没提交导致锁滞留 - 若查不到结果,不代表没锁——可能是锁刚释放,或已触发死锁自动回滚(此时错误日志会有
Deadlock found)
结合 PROCESSLIST 确认连接状态和来源
很多阻塞源不是正在跑 SQL 的线程,而是 COMMAND = 'Sleep' 却仍挂着事务的“幽灵连接”:
SELECT p.ID, p.USER, p.HOST, p.DB, p.COMMAND, p.TIME, p.STATE, p.INFO, t.trx_id, t.trx_started, t.trx_state FROM information_schema.PROCESSLIST p JOIN information_schema.INNODB_TRX t ON p.ID = t.trx_mysql_thread_id WHERE p.COMMAND = 'Sleep' AND t.trx_state = 'RUNNING' ORDER BY t.trx_started;- 关键判断点:
TIME > 300且INFO为空 → 大概率是应用连接池归还了连接但没清理事务 - 注意
p.HOST和p.USER,能帮你快速定位到具体应用实例或 DBA 终端,避免误杀系统线程(如User = 'system user')
真正难处理的不是“明显卡住”的事务,而是那种 Sleep 状态下静默持锁几十分钟的连接——它不占 CPU、不发日志、在 SHOW PROCESSLIST 里毫无威胁感,却能让后续所有写请求排队超时。排查时务必把 INNODB_TRX 和 PROCESSLIST 联合起来看,漏掉任一维度都可能让问题反复出现。


















