如何在线执行MySQL DDL避免长时间锁表

作者:袖梨 2026-08-31

能避免,但需满足条件:MySQL 5.6+末尾加列默认INPLACE、仅持MDL锁,理论上不阻塞DML;但长事务、非末尾加列(8.0.12前)、TEXT/BLOB列或隐式降级仍会导致卡住。

能避免,但得看操作类型、MySQL版本、表大小和当前负载——不是加个 ALGORITHM=INPLACE 就万事大吉。

ADD COLUMN 为什么有时还是卡住

MySQL 5.6+ 对末尾加列默认走 INPLACE,只持 MDL 锁(元数据锁),理论上不阻塞 DML。但实际中仍可能卡住,原因常是:

  1. 有长事务正在读该表:MDL 锁会等待所有活跃事务释放对表的隐式引用,SHOW PROCESSLIST 看不到它,但 SELECT * FROM information_schema.INNODB_TRX 能查到
  2. 表上有未提交的显式锁,比如 LOCK TABLES users WRITE 没释放
  3. DDL 自身触发了隐式拷贝:比如加的是 TEXTBLOB 列,或加在非末尾位置(MySQL 8.0.12+ 才支持非末尾 INSTANT
  4. LOCK=NONE 被忽略:MySQL 判定无法满足无锁条件时,会静默降级为 LOCK=SHARED 或更重的锁,不会报错

什么时候必须用 pt-online-schema-change

当你要 MODIFY COLUMN、改字段长度、调整 ENUM 值列表、或 MySQL 版本 COPY 模式 → 全表重建 → 长时间写锁。这时 pt-online-schema-change 是更可控的选择,但它不是银弹:

  1. 要求表有主键或唯一非空索引,否则增量同步无法定位行
  2. 不能容忍触发器带来的写延迟:它靠触发器捕获原表变更,高并发写入下可能堆积
  3. 执行前必须跑 --dry-run,重点看它生成的 RENAME TABLE 语句是否跨库、是否涉及视图或外键依赖
  4. 严禁在从库单独执行再切主从:DDL 不复制,主从表结构立刻不一致

MySQL 8.0.12+ 的 INSTANT 操作真能秒加字段?

能,但仅限于特定场景:

  1. 只支持末尾添加列(AFTER last_column),且类型不能是 TEXT/BLOB/JSON
  2. 不能修改已有列、不能删列、不能改列顺序
  3. 不记录 undo log,所以 DDL 回滚不可行;一旦执行就不可逆
  4. 表必须是 InnoDB,且之前没用过 COPY 算法做过 DDL(否则内部 flag 被置位,后续 INSTANT 失效)

验证是否生效:执行后查 information_schema.COLUMNS,新列 ORDINAL_POSITION 应等于原列数 + 1,且 ALTER TABLE 返回耗时通常

卡死时怎么快速判断和止损

DDL 卡住,别先急着 KILL —— 很可能杀的是无辜的等待者,真正持锁的事务还在运行:

  1. 查元数据锁等待:SELECT * FROM performance_schema.metadata_locks WHERE OBJECT_SCHEMA = 'db' AND OBJECT_NAME = 'tbl';
  2. 找持有锁的事务:SELECT * FROM performance_schema.threads t JOIN performance_schema.events_statements_current e USING (THREAD_ID) WHERE e.SQL_TEXT LIKE '%ALTER%';
  3. 确认长事务:SELECT * FROM information_schema.INNODB_TRX WHERE TIME_TO_SEC(NOW()) - TIME_TO_SEC(TRX_STARTED) > 60;
  4. 真要 KILL,优先 kill TRX_STATE = 'RUNNING'TRX_QUERY 是普通 SELECT 的事务,而不是那个 ALTER 进程本身(它可能正等锁)

最易被忽略的一点:MDL 锁不体现在 SHOW OPEN TABLES WHERE In_use > 0 里,这个命令只反映表缓存状态,跟元数据锁无关。

相关文章

精彩推荐