DDL卡在“Waiting for table metadata lock”时,真正源头是持有SHARED_READ/SHARED_WRITE锁的未提交事务或隐式会话,需通过performance_schema.metadata_locks定位LOCK_STATUS='GRANTED'的持锁线程,并结合sys.schema_table_lock_waits或INNODB_TRX与PROCESSLIST交叉验证,重点排查COMMAND='Sleep'但trx_state='RUNNING'、TIME>300且INFO为空的悬挂事务。

DDL卡在Waiting for table metadata lock时怎么找源头
这不是DDL慢,是它被别的事务按住了。最常见的是长事务、未提交的SELECT、或慢查询占着连接不放。SHOW PROCESSLIST里看到状态是Waiting for table metadata lock,说明该DDL已进入等待队列,它本身不是罪魁祸首,而是受害者。
关键要查谁拿了S锁没释放——注意:自动提交的SELECT也会短暂持锁,ORM框架隐式开启的事务、连接池未关闭游标,都可能留下MDL S锁,且生命周期为TRANSACTION。
- 先查
performance_schema.metadata_locks,过滤目标表名:SELECT OBJECT_SCHEMA, OBJECT_NAME, LOCK_TYPE, LOCK_DURATION, LOCK_STATUS, OWNER_THREAD_ID FROM performance_schema.metadata_locks WHERE OBJECT_NAME = 'your_table';
- 再关联
performance_schema.threads找线程归属:SELECT t.PROCESSLIST_USER, t.PROCESSLIST_HOST, t.PROCESSLIST_INFO FROM performance_schema.threads t JOIN performance_schema.metadata_locks m ON t.THREAD_ID = m.OWNER_THREAD_ID WHERE m.OBJECT_NAME = 'your_table' AND m.LOCK_STATUS = 'PENDING';
- 重点盯
LOCK_DURATION = 'TRANSACTION'的记录——这种锁只要事务不结束就一直挂着,风险最高
为什么INFORMATION_SCHEMA.INNODB_TRX查不到持锁会话
INNODB_TRX只反映InnoDB层的活跃事务,但MDL锁和事务不完全绑定。比如一个SELECT语句执行完、自动提交了,但连接没断开,游标没close,performance_schema.threads里状态仍是Sleep,却仍可能持有MDL S锁。
所以不能只依赖INNODB_TRX,必须交叉验证:
- 查
performance_schema.threads中状态为Sleep但PROCESSLIST_INFO IS NULL的连接:SELECT * FROM performance_schema.threads WHERE PROCESSLIST_STATE = 'Sleep' AND PROCESSLIST_INFO IS NULL;
- 再用
OWNER_THREAD_ID反查metadata_locks,确认是否正持有目标表的S锁 - 如果发现某连接
PROCESSLIST_TIME很大,且PROCESSLIST_INFO为空,大概率就是它——别光看Time值,得结合State和Info综合判断
用sys.schema_table_lock_waits快速定位等待关系
MySQL 5.7+ 自带sys库提供聚合视图,比手动JOIN更直观。只要权限允许,优先跑这句:
SELECT * FROM sys.schema_table_lock_waits;
它直接告诉你:
- 谁在等(
waiting_pid、waiting_query) - 谁在堵(
blocking_pid、blocking_query) - 等了多久(
wait_age) - 堵的是哪张表(
object_schema、object_name)
如果没有启用sys库或权限受限,退而用performance_schema.metadata_locks加LOCK_STATUS = 'PENDING'筛选,再关联threads回溯。
KILL前为什么必须交叉验证
误KILL一个正在做重要读取的连接,可能导致应用重试风暴或数据不一致。尤其要注意:
-
SHOW PROCESSLIST里状态为Sleep的连接,可能正拿着MDL S锁——它不显示在INNODB_TRX里,但能阻塞DDL - 某些ORM(如Hibernate)会在事务开始前预热执行
SELECT @@version之类语句,隐式开启事务却不提交 - KILL后,如果该连接背后是连接池,可能立刻重建新连接,问题复现
真正安全的做法是:先用sys.schema_table_lock_waits或metadata_locks + threads双重确认blocking_pid,再检查该PID对应的PROCESSLIST_INFO是否为空、PROCESSLIST_TIME是否异常长,最后才执行KILL blocking_pid。复杂点在于,锁可能来自你完全没意识到的地方——比如一个后台监控脚本执行完BEGIN就挂了,没人收尾。


















