调低lock_wait_timeout不能解决ddl超时,仅缩短等待报错时间;应通过sys.schema_table_lock_waits定位阻塞者并kill其线程id。

直接调低 lock_wait_timeout 不但不能解决 DDL 超时,反而会让问题更难定位、更易误杀。 它只控制「等待元数据锁(MDL)」的时长,不是 DDL 执行本身超时,也不是行锁或事务超时。盲目设为 3 秒,可能让合法 DDL 在竞争稍强时直接失败,而真正阻塞它的长事务却毫发无损。
为什么改 lock_wait_timeout 是个误导性操作
这个参数控制的是:当一个会话试图获取 MDL(比如执行 ALTER TABLE)时,如果锁被别人占着,它最多等多久就报错 Lock wait timeout exceeded。它不决定 DDL 多久能完成,也不影响谁在持有锁。
- 默认值是 31536000 秒(1 年),确实太长,容易让 DDL 卡住不报错,业务感知为“假死”
- 但设成 3 秒后,你看到的只是“更快失败”,而不是“更快成功”——阻塞源依然存在,只是你更早放弃了
- 很多线上环境误配该值后,DDL 频繁失败,运维只能反复重试,反而放大了锁竞争
真正要查的是谁在 hold 着 MDL 锁
关键不是“等多久”,而是“等谁”。MySQL 5.7+ 提供了精准定位手段,依赖 performance_schema.metadata_locks,但需先启用:
- 检查是否开启:
SELECT * FROM performance_schema.setup_instruments WHERE NAME = 'wait/lock/metadata/sql/mdl'; - 若
ENABLED是NO,需动态开启:UPDATE performance_schema.setup_instruments SET ENABLED = 'YES' WHERE NAME = 'wait/lock/metadata/sql/mdl'; - 然后查阻塞源头:
SELECT * FROM sys.schema_table_lock_waits WHERE blocking_lock_type 'SHARED_UPGRADABLE';
注意:SHARED_READ_ONLY 类型的持有者才是真凶(比如一个没提交的 SELECT ... FOR UPDATE 或长时间运行的只读查询),而 SHARED_UPGRADABLE 是可升级锁,允许并发读写,一般不阻塞 DDL。
kill 哪个会话?看 sql_kill_blocking_connection 字段
sys.schema_table_lock_waits 视图里有个现成字段,直接给出 kill 命令:
SELECT sql_kill_blocking_connection FROM sys.schema_table_lock_waits WHERE blocking_lock_type 'SHARED_UPGRADABLE' AND waiting_query LIKE 'alter table%';
结果类似:KILL 28452 —— 这才是该 kill 的会话 ID。不要凭 SHOW PROCESSLIST 盲猜,尤其别 kill 正在执行 ALTER 自身的线程(那是受害者,不是加害者)。
- 执行
KILL 28452后,原 DDL 通常几秒内就能继续 - 如果该会话是应用层重试导致的“幽灵连接”,需同步排查应用事务管理逻辑
- 频繁出现说明业务侧存在未关闭的长事务或慢查询,不能只靠 kill 治标
最常被忽略的一点:MDL 锁的持有者可能根本不在 PROCESSLIST 当前活跃列表里——它可能是一个已断开但事务未回滚的连接(State: Sleep 但 Command: Sleep 且 Time 很大),这类连接不会自动释放 MDL,必须手动 KILL。排查时别只盯着 Command: Query 的行。











