Waiting for table metadata lock 根本原因不是MySQL慢,而是ALTER TABLE需MDL_EXCLUSIVE锁,却在等待未提交事务持有的MDL_SHARED_READ锁;常见阻塞源包括长事务、Sleep连接、ORM隐式事务未提交及带SLEEP或大结果集的SELECT。

卡在 Waiting for table metadata lock 不是 MySQL 慢,是它在等一个早已“下班”却没关工位的会话——那个会话还拿着 MDL_SHARED_READ 锁没释放。
为什么一条 SELECT 就能让 ALTER TABLE 卡住几十分钟
只要执行过 SELECT(哪怕只查了一行)、INSERT 或 UPDATE,且事务没提交,MySQL 就会为这张表持有一个 MDL_SHARED_READ 锁。这个锁生命周期和事务绑定:不 COMMIT 或 ROLLBACK,它就不释放。
ALTER TABLE 需要的是 MDL_EXCLUSIVE 锁,而它和任何 MDL_SHARED_READ 互斥。所以不是 DDL 在干活,是在排队等别人交钥匙。
- ORM 框架隐式开启事务后忘记
COMMIT是高频原因 -
Sleep状态、Time > 300的连接,大概率是应用端连接池没正确 close 游标 - 带
SLEEP(300)的调试 SQL 或大结果集查询未取完,也会一直 hold 住锁
ALGORITHM=INPLACE 和 LOCK=NONE 为什么还是卡
这两个参数只控制“数据操作阶段”是否拷表或阻塞 DML,但无法跳过“元数据变更阶段”——这个阶段必须获取一次短暂的 MDL_EXCLUSIVE 锁来更新数据字典、写 binlog。
也就是说:ALGORITHM=INPLACE ≠ 不需要 MDL 锁;LOCK=NONE ≠ 不等待 MDL 锁。
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
- 即使支持 inplace,只要已有会话持有
MDL_SHARED_READ,ALTER仍会卡在Waiting for table metadata lock -
LOCK=NONE失败时不会静默降级,而是直接报错ERROR 1845,别指望它兜底 - MySQL 8.0.12+ 的
INSTANT算法(如末尾加列)才真正绕开这把锁,但仅限极少数操作
怎么快速定位谁在 hold 锁
别只查 INNODB_TRX,它只管事务,不管 MDL。重点看 performance_schema.metadata_locks(MySQL 5.7+):
SELECT OBJECT_SCHEMA, OBJECT_NAME, LOCK_TYPE, LOCK_DURATION, LOCK_STATUS, OWNER_THREAD_ID FROM performance_schema.metadata_locks WHERE OBJECT_NAME = 'your_table' AND LOCK_STATUS = 'GRANTED';
再关联线程信息找源头:
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_DURATION = 'TRANSACTION';
- 重点关注
LOCK_DURATION = 'TRANSACTION'的记录,这类锁最危险 -
PROCESSLIST_INFO为空不等于没在干活,可能是空闲连接但事务未关闭 -
SHOW PROCESSLIST中Command = Sleep且Time > 300的连接,优先 Kill
线上执行前必须做的三件事
不是“能不能加字段”,而是“现在能不能加”。执行前必须确认环境干净:
- 查长事务:
SELECT * FROM information_schema.INNODB_TRX WHERE TIME_TO_SEC(NOW() - trx_started) > 60; - 查阻塞链:
SELECT * FROM performance_schema.metadata_locks WHERE LOCK_STATUS = 'PENDING' AND OBJECT_SCHEMA = 'db_name'; - 设好超时:
SET SESSION lock_wait_timeout = 30;—— 这不是防慢,是让问题 30 秒内暴露出来
真正容易被忽略的是:LOCK=NONE 对 NOT NULL 字段默认值不友好,哪怕加的是 DEFAULT NULL,也得显式写出来,否则 MySQL 可能悄悄退化到 COPY 模式。

















