waiting for table metadata lock的根本原因是长事务或未提交事务持有mdl共享锁,导致mysqldump等备份操作无法获取mdl共享锁;innodb_lock_wait_timeout对此无效,需调小lock_wait_timeout(默认31536000秒)并定位持锁会话。

备份脚本执行 mysqldump 或 mysqlpump 时卡在 Waiting for table metadata lock,不是锁参数调得小,而是默认值太大——lock_wait_timeout 默认设为 31536000 秒(一年),导致备份进程傻等,而应用层 HTTP 请求早已超时断连,连接堆积、线程耗尽。
为什么改 innodb_lock_wait_timeout 没用
这个参数只控制 InnoDB 行锁等待,对元数据锁(MDL)完全无效。备份工具在读表前会申请 MDL 共享锁,若此时有长事务未提交、或客户端假死还挂着连接,mysqldump 就会被卡在获取 MDL 这一步,根本进不到行锁阶段。
-
innodb_lock_wait_timeout默认 50 秒,影响的是UPDATE/DELETE等语句的行锁等待 -
lock_wait_timeout才是控制 MDL 等待的参数,MySQL 8.0 默认 31536000 秒 - 备份报错里出现
Lock wait timeout exceeded,但实际状态是Waiting for table metadata lock,这是典型 MDL 卡住,不是行锁问题
如何安全调整 lock_wait_timeout
线上建议设为 30–60 秒,和应用层 read_timeout 对齐,避免备份无限挂起。注意:该参数只影响「新申请锁」的线程,不中断已持有的锁。
- 临时生效(当前会话):
SET lock_wait_timeout = 45; - 全局生效(新连接):
SET GLOBAL lock_wait_timeout = 45; - 永久生效:在
my.cnf的[mysqld]段下加lock_wait_timeout = 45,然后重启 MySQL - 备份脚本中可显式设置会话级参数:
mysqldump --set-gtid-purged=OFF --single-transaction -e "SET lock_wait_timeout=45;" ...(需配合--init-command或前置连接执行)
比调参更关键的是定位阻塞源
调小 lock_wait_timeout 只会让报错更快出现,真正要解决的是谁在长期持有 MDL。备份卡住,90% 是因为未提交事务、慢查询或空闲连接没断开。
- 查长事务:
SELECT * FROM information_schema.INNODB_TRX WHERE TIME_TO_SEC(TIMEDIFF(NOW(), trx_started)) > 60; - 查疑似假死连接:
SELECT ID, USER, HOST, DB, COMMAND, TIME, STATE, INFO FROM information_schema.PROCESSLIST WHERE COMMAND != 'Sleep' AND TIME > 60; - 查 MDL 等待链:
SELECT * FROM performance_schema.metadata_locks ml JOIN performance_schema.threads t ON ml.OWNER_THREAD_ID = t.THREAD_ID WHERE ml.LOCK_STATUS = 'PENDING'; - 重点看
PROCESSLIST_STATE为Waiting for table metadata lock且PROCESSLIST_INFO为空的线程——这大概率是卡住的备份进程
真正难处理的不是参数本身,而是那些开着事务却不执行任何 SQL、也不提交的“幽灵连接”。它们不会出现在慢日志里,也不会被常规监控捕获,却能持续阻塞所有 DDL 和备份操作。上线前务必确认应用层有连接空闲超时、事务自动回滚机制,否则再小的 lock_wait_timeout 也只是让失败来得更早一点。











