ALTER TABLE卡在Waiting for table metadata lock是因需获取MDL_EXCLUSIVE锁,而被未提交事务或Sleep连接持有的MDL_SHARED_READ锁阻塞;须查performance_schema.metadata_locks与INNODB_TRX定位持锁线程。

ALTER TABLE 会抢元数据锁(MDL),而前端写入要等它释放
不是“改长度本身慢”,而是 ALTER TABLE 在执行时必须获取表级元数据锁(MDL),而所有 INSERT、UPDATE、DELETE、甚至普通 SELECT 都需要先拿到 MDL 才能继续。一旦 ALTER TABLE 卡住,后续所有对该表的请求就会排队等待——前端表现为接口超时、500 错误、连接池打满。
常见卡住原因包括:
- 表上存在长事务(哪怕只执行了一条
SELECT后没提交),它一直持有 MDL 不放 - 有慢查询正在扫描全表,阻塞了
ALTER获取锁 - DDL 正在等某个已存在的 DML 操作完成,而那个 DML 又在等别的资源(形成锁链)
为什么“只是改 VARCHAR(255) → VARCHAR(2048)”也锁表?
字段长度扩到 VARCHAR(2048) 在 utf8mb4 下最大占 8192 字节,但 MySQL 的 COMPACT/DYNAMIC 行格式中,变长字段长度仅用 1–2 字节记录。只要原字段最大实际字节数 ≤ 255(即头部用 1 字节表示),扩到 2048 后仍只需 2 字节,就不会重写每行数据——这种变更本可 ALGORITHM=INPLACE 秒级完成。
但即便如此,它仍需 MDL 锁,且锁持有时间取决于:
立即学习“前端免费学习笔记(深入)”;
- 是否有活跃事务正在访问该表(查
information_schema.INNODB_TRX) - 是否触发了隐式校验:比如开了严格模式,MySQL 会逐行检查现有数据是否符合新长度定义
- 字段带前缀索引(如
INDEX(col(50))),MODIFY 后索引定义未同步更新,也可能导致 DDL 卡在元数据一致性检查阶段
前端写入阻塞 ≠ DDL 失败,但现象几乎一样
应用层看到的是连接超时、com.mysql.cj.jdbc.exceptions.MySQLTimeoutException 或 Connection refused,日志里却找不到报错 SQL——因为问题不在语句本身,而在连接根本拿不到 MDL 去执行它。
快速定位方法:
- 执行
SHOW FULL PROCESSLIST;,看有没有状态为Waiting for table metadata lock的线程 - 查阻塞源头:
SELECT * FROM performance_schema.metadata_locks WHERE OBJECT_SCHEMA = 'db' AND OBJECT_NAME = 'table'; - 确认长事务:
SELECT TRX_ID, TRX_STARTED, TRX_STATE, TRX_QUERY FROM information_schema.INNODB_TRX WHERE TIME_TO_SEC(NOW()) - TIME_TO_SEC(TRX_STARTED) > 60;
真正影响业务连续性的不是“能不能改”,而是“谁在挡路”
很多团队花时间纠结要不要用 pt-online-schema-change,其实更该先盯住那几个挂着 20 分钟不提交的 Sleep 连接。一个没 COMMIT 的 BEGIN; SELECT ...; 就能让整个订单表写入停摆。
线上改字段前,务必做三件事:
- 查
INNODB_TRX确认无长事务 - 用
SELECT MAX(LENGTH(col)) FROM table;验证现有数据不超新长度(尤其注意LENGTH()是字节数,不是字符数) - 检查该字段是否建了前缀索引,如有,
DROP INDEX+CREATE INDEX必须同步做
DDL 本身不难,难的是它把所有并发控制逻辑都摊在明面上——你绕不开谁在持锁、谁在等锁、谁在悄悄拖慢整个链路。


















