DDL卡住报Lock wait timeout实为元数据锁(MDL)阻塞,与innodb_lock_wait_timeout无关;真正控制DDL等待MDL超时的是lock_wait_timeout参数,默认一年,需设为合理值(如30秒)以快速暴露阻塞源,并通过performance_schema定位持有锁的线程后KILL解决。

DDL卡住报Lock wait timeout,不是innodb_lock_wait_timeout的问题
这个错误90%以上跟innodb_lock_wait_timeout完全无关。DDL(如ALTER TABLE、TRUNCATE TABLE、CREATE INDEX)卡住,真正拦路的是元数据锁(MDL),它走的是独立锁路径,不受InnoDB行锁参数控制。你调大innodb_lock_wait_timeout,DDL照样卡着不动。
查阻塞源必须看performance_schema.metadata_locks
SHOW PROCESSLIST里看到Waiting for table metadata lock,说明DDL在等MDL EXCLUSIVE锁,而有人正拿着SHARED_READ或SHARED_WRITE锁不放。这时候要查:
-
SELECT * FROM performance_schema.metadata_locks WHERE OBJECT_SCHEMA = 'your_db' AND OBJECT_NAME = 'your_table' AND LOCK_STATUS = 'PENDING'—— 找出卡在哪条DDL上 -
SELECT THREAD_ID, PROCESSLIST_INFO FROM performance_schema.threads WHERE THREAD_ID IN (SELECT OWNER_THREAD_ID FROM performance_schema.metadata_locks WHERE LOCK_STATUS = 'GRANTED')—— 找出持有锁的线程正在执行什么SQL - 重点关注
PROCESSLIST_INFO里有没有未提交的SELECT、长事务里的SELECT FOR UPDATE、或者假死连接还在挂着
lock_wait_timeout才是DDL真正的超时开关
控制DDL等MDL多久就放弃的,是lock_wait_timeout,默认值是31536000秒(一年)。线上必须改:
- 临时生效:
SET lock_wait_timeout = 30(只对当前会话有效,DDL启动后才起作用) - 全局生效:
SET GLOBAL lock_wait_timeout = 30(影响后续新连接,但不会中断已卡住的DDL) - 配置文件写法:
lock_wait_timeout = 30放在[mysqld]段,重启生效 - 注意:设成30秒不是为了让DDL“等够30秒”,而是让它快速失败,好立刻暴露阻塞源——否则你永远不知道是谁在拖后腿
KILL比调参更直接,但得KILL对人
DDL卡住时,最有效的动作不是改参数,而是定位并干掉持有MDL的源头:
- 先用上面查到的
OWNER_THREAD_ID,转成PROCESSLIST.ID - 确认该线程状态不是
Sleep就是Query且长时间没结束,再执行KILL [ID] - 特别小心:KILL一个正在执行
SELECT的只读事务,一般无副作用;但KILL一个正在写入的事务,可能造成部分数据不一致(需业务侧兜底) - 如果KILL后立刻重试又卡住,说明应用层有自动重试逻辑,得同步停掉对应服务或切到低峰期操作
真正难处理的从来不是超时时间设多少,而是那个没提交的SELECT、那个卡在RPC调用里的事务、那个忘了关连接池的Java应用——它们不会出现在任何超时参数里,但会持续堵死所有DDL。排查时别被“timeout”字眼带偏节奏。


















