bigywater.tech
MySQL / InnoDB · 115

在线 DDL 与元数据锁

开一个只SELECT一次就挂着不提交的事务,验证标榜'不阻塞'的在线DDL在这种情况下是不是真的不受影响。

真实被卡住
空闲事务持有MDL时在线DDL依然要排队
瞬间完成
事务结束后同样的ALTER真实立即成功

做的事:一个事务只SELECT一次就挂着不动,另一边真实跑在线DDL

代码(会话A)START TRANSACTION; SELECT COUNT(*) FROM orders; SELECT SLEEP(5); -- 真实事务还没提交,就这么挂着 代码(会话B,几乎同时)ALTER TABLE orders ADD INDEX idx_test_mdl (customer_id, amount), ALGORITHM=INPLACE, LOCK=NONE;

验证:真实标榜"不阻塞"的在线DDL,真实卡住了

实测 · 本机执行输出
ERROR 1205: Lock wait timeout exceeded; try restarting transaction

LOCK=NONE承诺的"在线",真实指的是ALTER执行过程中(拷贝/重建索引数据的阶段)不阻塞其他会话的读写;但它开始执行的那一刻,真实需要先申请一个短暂的排他性元数据锁(MDL)来记录"这张表的结构正在变化"——只要有任何事务(哪怕只是做过一次早就执行完的SELECT,只要这个事务本身还没提交)在真实持有这张表的元数据锁,ALTER的这个初始请求就会真实排队等待,直到超时。

验证:等挂着的事务结束,同样的ALTER真实瞬间完成

实测 · 本机执行输出
(会话A的事务真实结束之后)
ALTER TABLE orders ADD INDEX idx_test_mdl (customer_id, amount),
  ALGORITHM=INPLACE, LOCK=NONE;
→ 真实立即成功,无需等待

(顺带验证:真的在DDL执行期间发起一次并发UPDATE,真实不受影响)
UPDATE orders SET amount=amount WHERE id=1;
→ 真实立即成功,DDL和UPDATE互不阻塞
这组对比真实揭示了"在线DDL"这个说法容易被误解的地方:它解决的是"执行过程漫长"这个问题(拷贝几十GB数据不需要锁住整张表),但完全解决不了"有人正占着这张表的元数据锁不撒手"这个问题——一个真实忘记提交的长事务、或者一条被应用代码忘记关闭的连接,哪怕它早就不再对这张表做任何实际操作,也会真实卡住后面所有对这张表的DDL,包括标榜"在线"的那些。真实排查"ALTER TABLE怎么一直卡着"这类问题时,第一步应该是查information_schema.INNODB_TRX或者performance_schema里有没有一个真实空闲很久、却还没提交的事务占着这张表。

分步演示:一个不提交的事务,真实卡住了标榜在线的DDL

每一步都是本文上面用真实并发会话跑出来的原始结果。

在线DDL的'在线',管不住一个真实挂起的空闲事务
这一步新验证的场景

    本文用到的工具

    • 本机 MySQL 26.7.0(InnoDB),真实并发会话验证MDL对在线DDL的影响
    • ALGORITHM=INPLACE, LOCK=NONE + 元数据锁(MDL)
    京ICP备2026067502号-1京公网安备11010502063487号