执行 SHOW FULL PROCESSLIST; 查 State 为 Waiting for table metadata lock 且 Time 超 30 秒的线程,结合 sys.schema_table_lock_waits 或 information_schema.INNODB_TRX 定位持锁长事务,并调大 lock_wait_timeout 至 60 秒防堆积。

怎么看当前有没有MDL锁等待?
直接执行 SHOW FULL PROCESSLIST;,重点扫 State 列:只要看到大量线程停在 Waiting for table metadata lock,且 Time 超过 30 秒,基本就是 MDL 等待已堆积。
别只盯着慢 SQL 本身——它只是“受害者”,不是“加害者”。真正持锁的线程往往状态是 Query 或 Sleep,Info 列里可能只有 BEGIN、SELECT ... 这类看似无害的操作,但事务没提交,SHARED_READ 锁就一直挂着。
- 用
SELECT * FROM sys.schema_table_lock_waits\G(MySQL 8.0+)直接拿到blocking_pid和waiting_pid,比人工扫PROCESSLIST快得多 - 如果
sys.schema_table_lock_waits返回空,不代表没锁——可能是锁刚释放,或 performance_schema 采集未启用,得立刻查performance_schema.metadata_locks -
SHOW PROCESSLIST里Info为空但State是Waiting for table metadata lock的线程,大概率是应用层连接假死,得结合客户端日志交叉验证
怎么查谁在持MDL锁?
持锁者通常不是 DDL 操作本身,而是某个早已开始、却迟迟不结束的普通事务。关键不是找“正在 ALTER TABLE”的人,而是找“早就 SELECT 了但没 COMMIT”的人。
执行:SELECT * FROM information_schema.INNODB_TRX WHERE TIME_TO_SEC(TIMEDIFF(NOW(), trx_started)) > 60;,重点关注 trx_state = 'RUNNING' 且运行时间超 1 分钟的事务。
- 若
trx_query为空,说明事务只BEGIN没执行语句,去SHOW PROCESSLIST查对应trx_mysql_thread_id的Info列,看最后执行的是什么 - 持锁类型看
sys.schema_table_lock_waits.blocking_lock_type:如果是SHARED_READ,基本就是长事务;如果是EXCLUSIVE,大概率是卡住的 DDL 自己在等别的锁 - 别漏掉隐式事务:有些 ORM 或驱动默认开启 autocommit=0,一次查询就开事务,不显式
COMMIT就永远持锁
如何让MDL等待不拖垮整个库?
lock_wait_timeout 是唯一能控制 MDL 等待时长的参数,默认值 31536000 秒(一年),线上必须改。设成 30–60 秒,和应用层 read_timeout 对齐,避免连接堆积、线程耗尽。
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
改法:SET GLOBAL lock_wait_timeout = 60;(立即生效,新连接);永久生效需在 my.cnf 的 [mysqld] 段加 lock_wait_timeout = 60 并重启。
- 注意:
lock_wait_timeout只影响“新申请锁”的线程,不会中断已持有的锁——改了它,卡住的 DDL 还是得等持锁事务结束 - 别和
innodb_lock_wait_timeout混:后者管行锁,对 MDL 完全无效;改错参数等于白调 - 改完要监控:
SELECT @@lock_wait_timeout;确认生效,再观察performance_schema.threads中PROCESSLIST_STATE = 'Waiting for table metadata lock'的线程是否在超时后干净退出
为什么日志里看不出MDL等待?
慢日志里的 Query_time 包含了 MDL 等待时间,但 Lock_time 不记录 MDL 锁——它只统计 InnoDB 行锁等待。所以你会看到 Query_time: 120.345、Lock_time: 0.000123、Rows_examined: 1,这种组合就是 MDL 阻塞的铁证。
- 别信
Rows_examined:它只算真正扫描的行,等 MDL 锁的时间完全不计入 -
Rows_sent = 0时更危险——说明连结果集都没生成,卡在锁等待入口,可能连事务都没进到执行器 - 用
grep 'Query_time.*Lock_time.*Rows_examined' /var/log/mysql/slow.log | awk '{if($2>$4*100 && $8 快速筛出疑似 MDL 卡顿的记录
真正麻烦的不是怎么查,而是持锁源头往往藏在应用逻辑深处:一个没 COMMIT 的事务、一个被异常中断却没 rollback 的连接、一个开启了事务却忘了关的 HTTP 请求——这些都得靠业务代码配合排查,数据库只能告诉你“谁卡住了”,没法告诉你“为什么卡住”。










