应查information_schema.innodb_trx中trx_started早、trx_state='running'且trx_query为空的长事务,因其持有mdl读锁阻塞ddl;勿依赖show processlist中sleep或time字段,而需以trx_started时间戳超120秒为关键判断依据。

如何快速识别阻塞DDL的长事务
DDL卡在 Waiting for table metadata lock 状态时,大概率是某个事务没提交,还拿着表的 MDL 读锁。别只看 SHOW PROCESSLIST ——很多长事务状态是 Sleep 或 Query 但 TIME 很大,INFO 字段可能只显示 SELECT ... 或空,容易被忽略。
真正可靠的判断依据是 information_schema.INNODB_TRX 中的 trx_started 时间戳:
- 执行
SELECT trx_id, trx_started, trx_state, trx_mysql_thread_id, SUBSTRING_INDEX(trx_query,' ', 4) AS short_query FROM information_schema.INNODB_TRX WHERE trx_started - 重点不是
trx_state = 'RUNNING',而是trx_started是否远早于当前时间(比如超过 30 秒或 5 分钟,按业务容忍度调整) -
short_query能帮你快速判断是不是一个简单SELECT后忘了COMMIT,而不是正在跑复杂逻辑
为什么 KILL QUERY 不解决问题
用 KILL QUERY <thread_id></thread_id> 只终止当前语句,事务本身仍处于活跃状态,MDL 锁不会释放——DDL 还是卡着。必须用 KILL <thread_id></thread_id> 才能触发事务回滚、释放锁。
注意几个关键点:
- 不要对正在执行
ALTER TABLE的线程执行KILL:它只是在等锁,杀掉后重试照样卡住 - 优先联系对应服务 owner 确认该线程是否可杀,尤其在业务高峰期
- 如果
INNODB_TRX查不到明显长事务,但PROCESSLIST里有大量Sleep连接且TIME很大,可能是应用异常退出后连接未回收,需从应用侧排查连接池配置
如何避免下次再踩坑
长事务阻塞 DDL 不是偶发故障,而是可预防的运维信号:
- 开发侧:所有显式事务(
BEGIN)必须配对COMMIT或ROLLBACK,尤其在工具类脚本或 DBA 客户端中,避免只查不提交 - DBA 侧:建立定时巡检,例如每 5 分钟跑一次
SELECT ... FROM information_schema.INNODB_TRX WHERE TIMESTAMPDIFF(SECOND, trx_started, NOW()) > 120,告警超 2 分钟未提交事务 - 上线变更前:强制要求检查
INNODB_TRX和PROCESSLIST,确认无长事务再执行 DDL
最常被忽略的是:事务即使只执行了一条 SELECT,只要没 COMMIT,就持续持有 MDL 读锁——DDL 需要写锁,冲突立即发生。这不是性能问题,是锁协议本身的刚性约束。











