
MySQL 5.7 的 Online DDL 不等于无锁,它必然在准备和提交阶段申请 MDL_EXCLUSIVE 锁,只要此时表上有未提交的长事务(哪怕只是个慢 SELECT),就会卡住并阻塞后续所有 DML。
为什么 ALGORITHM=INPLACE + LOCK=NONE 还会卡住?
很多人以为加了这两个参数就万事大吉,其实它们只影响执行阶段;DDL 仍需在开头和结尾各抢一次排他元数据锁(MDL_EXCLUSIVE)。这两个“瞬间”一旦遇到阻塞,整个操作就挂起:
- 一个未提交的
SELECT * FROM orders WHERE status = 'pending'就足以让后续ALTER TABLE orders ADD COLUMN remark TEXT卡在Waiting for table metadata lock - 该等待状态不会报错,也不会自动超时,只会一直排队,进而拖垮连接池
-
LOCK=NONE在 5.7 中支持极窄:仅对末尾加列、加索引、改默认值有效;MODIFY COLUMN或收缩VARCHAR长度会直接退化为LOCK=SHARED或更严
哪些 ALTER 操作在 5.7 中大概率触发 COPY 算法?
一旦退化为 COPY,就是全表重建 + 全程 MDL_EXCLUSIVE 锁,DML 彻底阻塞。这些操作在 5.7.32 中基本无法避免:
-
CHANGE COLUMN或MODIFY COLUMN(哪怕只是把VARCHAR(100)改成VARCHAR(150)) -
VARCHAR长度收缩(如VARCHAR(200) → VARCHAR(100)),不支持 INPLACE -
VARCHAR跨字节边界扩容(如VARCHAR(255) → VARCHAR(256),长度字节数从 1 字节升为 2 字节) - 添加
FULLTEXT INDEX,5.7 不支持 INPLACE 创建
如何快速判断当前 DDL 是否安全?
别依赖直觉,用这几条命令现场验证:
- 查活跃事务:
SELECT * FROM information_schema.INNODB_TRX WHERE TIME_TO_SEC(NOW() - trx_started) > 60;—— 有运行超 1 分钟的事务,就别急着跑 DDL - 看锁等待:
SHOW PROCESSLIST;找出状态为Waiting for table metadata lock的线程,顺藤摸瓜定位源头 SQL - 确认算法是否真生效:执行前加
EXPLAIN FORMAT=JSON(需 5.7.8+),或事后查performance_schema.table_lock_waits_summary_by_table中的锁等待统计 - 测试环境先跑
ALTER ... ALGORITHM=INPLACE, LOCK=NONE,再立刻KILL一个模拟长事务,观察是否卡住
真正危险的不是 DDL 本身耗时多长,而是它在元数据层制造的串行化瓶颈——这个锁看不见、摸不着,却能让整个表的读写请求堆成队列。线上执行前,务必确认没有慢查询或未提交事务在持有该表的共享 MDL。


















