DDL卡住主因是元数据锁(MDL)等待,而非innodb_lock_wait_timeout控制的行锁;真正决定DDL等待超时的是lock_wait_timeout,默认31536000秒,需设为30–60秒并与应用层超时对齐,同时优先排查未提交事务、慢查询或假死连接等阻塞源。

直接改 lock_wait_timeout,别碰 innodb_lock_wait_timeout —— 后者对 DDL 卡住完全无效。
为什么 ALTER TABLE 一直卡着,改了 innodb_lock_wait_timeout 没用?
因为 DDL(如 ALTER TABLE、CREATE INDEX)阻塞的根源是元数据锁(MDL),不是 InnoDB 行锁。innodb_lock_wait_timeout 只控制 UPDATE/DELETE 等语句等待行锁的时间;而 lock_wait_timeout 才真正决定线程申请 MDL 时最多等多久。MySQL 8.0 默认值是 31536000 秒(一年),等于“死等”,应用层 HTTP 请求早超时断开了,MySQL 还在挂起。
- 错误现象:
Lock wait timeout exceeded; try restarting transaction出现在 DDL 场景中,基本可判定是 MDL 等待 - 常见误操作:看到报错里有 “lock wait timeout”,就去 SET
innodb_lock_wait_timeout = 10,结果 DDL 依旧卡住不动 - 本质区别:
innodb_lock_wait_timeout影响事务内 DML 的行锁等待;lock_wait_timeout影响 DDL 开始前获取表级 MDL 的等待
怎么查和设 lock_wait_timeout 才真正生效?
它支持会话级、全局级和持久化设置,但注意:所有设置都只对“新发起的锁申请”生效,不中断已卡住的 DDL。
- 查当前值:
SELECT @@lock_wait_timeout; - 临时改当前会话(仅本次连接):
SET SESSION lock_wait_timeout = 60; - 全局改(影响后续所有新连接,需 SUPER 权限):
SET GLOBAL lock_wait_timeout = 60; - 推荐持久化(重启不失效):
SET PERSIST lock_wait_timeout = 60;—— 不要用SET PERSIST_ONLY,它不改内存值,DDL 不会立刻响应 - 永久写配置文件:在
my.cnf的[mysqld]段下加lock_wait_timeout = 60,然后重启 MySQL
设成多少合适?别只调参数,先看谁在占着锁
线上建议设为 30–60 秒,与应用层的 read_timeout 或 HTTP 超时对齐。但光调这个参数治标不治本——如果阻塞源(比如一个没提交的 SELECT)一直不释放 MDL,哪怕你设成 1 秒,它照样报错退出,问题还是没解决。
- 必须配合诊断:查
performance_schema.metadata_locks中LOCK_STATUS = 'PENDING'的线程,再 JOINthreads看是谁在等 - 重点排查源头:
SELECT * FROM information_schema.INNODB_TRX WHERE trx_state = 'RUNNING' AND trx_query IS NULL,这类“空跑事务”最常持有 SHARED_READ 锁 - 别忽略 Sleep 连接:
SHOW PROCESSLIST里Command = 'Sleep'且Time > 60的,很可能是客户端假死但事务未提交 -
wait_timeout和interactive_timeout虽不控制 MDL,但设太大会让这类假死连接长期滞留,间接加剧 MDL 阻塞
DDL 卡住时,最常被忽略的真相
90% 的 DDL 卡住,根本原因不是锁超时参数设得太大,而是存在未结束的事务、慢查询或客户端连接假死。InnoDB 在执行原地 DDL 前,必须确保没有活跃事务访问该表(哪怕是只读),否则就卡在 MDL 获取阶段。这意味着:即使你把 lock_wait_timeout 设成 1 秒,只要那个长事务不提交,DDL 就永远只能失败退出,而不是“更快地成功”。真正要做的,是定位并终止持锁源,而不是反复调整等待时间。


















