应优先查 sys.innodb_lock_waits 视图,重点关注 wait_age_secs 字段(>30秒需立即处理),它比 information_schema.innodb_lock_waits 更直观可靠;遇 mdl 锁需切 sys.schema_table_lock_waits;幽灵事务则须 join innodb_trx 与 processlist 查 sleep 但 running 的连接。

直接查 sys.innodb_lock_waits 看谁在等谁
这个视图是 MySQL 5.7 中最直观的锁等待关系入口,字段设计比原生 INFORMATION_SCHEMA.INNODB_LOCK_WAITS 更友好。重点看 wait_age_secs(当前已等待秒数),而不是 wait_started(需手动算差值)。
-
SELECT * FROM sys.innodb_lock_waits ORDER BY wait_age_secs DESC LIMIT 5—— 一眼揪出最久的 5 个等待项 -
wait_age_secs > 30基本可判定异常,需立即介入 - 结果为空 ≠ 没锁等待:可能是锁刚释放,或 performance_schema 捕获有毫秒级延迟
-
waiting_query为NULL很常见(比如事务只BEGIN未执行语句),此时必须用waiting_pid去SHOW PROCESSLIST查Info列
为什么不能只靠 INFORMATION_SCHEMA.INNODB_LOCK_WAITS
MySQL 5.7 虽保留该表,但字段精简、关联不稳定,常返回空或漏掉阻塞源头。例如它不暴露 blocking_query 或 sql_kill_blocking_query,排查时得手动 JOIN 多张表拼凑信息,效率低且易出错。
- 它没有
wait_age_secs字段,只能靠wait_started和NOW()计算,容易因时区或精度出偏差 - 缺少对 MDL 锁(
Waiting for table metadata lock)的支持——这类锁根本不会出现在该表中 - 字段如
blocking_trx_id可能为NULL,无法定位持锁方
遇到 Waiting for table metadata lock 怎么办
这是 DDL(如 ALTER TABLE)被长查询或未提交事务阻塞的典型表现,sys.innodb_lock_waits 完全沉默,必须切到 sys.schema_table_lock_waits。
-
SELECT * FROM sys.schema_table_lock_waits\G—— 直接看到blocking_pid和waiting_pid,以及生成的sql_kill_blocking_query - 若
blocking_lock_type = 'SHARED_READ',大概率是某个SELECT或隐式事务(BEGIN后断开)在 hold 表级读锁 - 该视图不显示
Sleep状态的连接,所以如果阻塞源是“挂着但没操作”的连接,还得结合INNODB_TRX+PROCESSLISTJOIN 查询
别忽略“Sleep 却 RUNNING”的悬挂事务
连接池场景下最隐蔽的锁源:应用归还连接前没 COMMIT,线程状态是 Sleep,但事务状态仍是 RUNNING,持续持有行锁或 MDL 锁。
- 必须用 JOIN:
SELECT t.trx_id, t.trx_started, t.trx_state, p.ID, p.COMMAND, p.TIME, p.INFO FROM information_schema.INNODB_TRX t JOIN information_schema.PROCESSLIST p ON t.trx_mysql_thread_id = p.ID WHERE p.COMMAND = 'Sleep' AND t.trx_state = 'RUNNING' -
TIME > 300且INFO为空 → 极大概率是应用异常中断或忘记COMMIT -
trx_isolation_level = 'REPEATABLE READ'→ 即使只执行过一次SELECT,事务一启动就持 MDL 锁
真正卡住你的,往往不是正在跑的 SQL,而是那个静静躺在 PROCESSLIST 里、状态为 Sleep、却死握着锁不放的“幽灵事务”。查 sys.innodb_lock_waits 是起点,但得随时准备切到 sys.schema_table_lock_waits 或手写 JOIN,才能把这类事务从阴影里拖出来。











