mysql 5.6之前alter table全程copy并持表级排他锁,阻塞所有dml和select;5.7支持inplace但需短暂mdl_exclusive锁;8.0.12+新增instant算法,仅限末尾加列等极少数操作,真正零拷贝秒级完成。

MySQL 5.6 之前:全程 COPY + 全表排他锁
老版本 MySQL(5.6 及更早)执行 ALTER TABLE 时,底层只能走 COPY 算法:先建新表、逐行拷贝数据、重建索引、删旧表、重命名。整个过程需要对原表加 EXCLUSIVE 表级锁,SELECT 和所有 DML 都被阻塞。
这意味着哪怕只是加一个空字段,1000 万行的表在机械盘上也可能锁表 2 分钟以上——不是它“慢”,是设计上就要求独占。
- 不支持任何 Online DDL,
ALGORITHM参数无效 - 即使加的是
NULL列,也必须重建全表 - MDL 锁一旦申请失败,后续所有新请求都会排队等它,容易引发连接池雪崩
MySQL 5.7:INPLACE 成为主流,但仍有隐式锁升级
5.7 引入了真正可用的 INPLACE 算法,对多数元数据变更(如加普通字段、加索引)不再拷数据,只改数据字典。但关键限制在于:INPLACE 不等于“无锁”——它仍需短暂获取 MDL_EXCLUSIVE 锁来切换元数据。
这个“短暂”有多短?取决于并发负载。如果此时有长查询正在跑,ALTER 会卡在 Waiting for table metadata lock 状态,而它一旦拿到锁,又会反过来阻塞后面所有新请求。
- 加
NOT NULL列且无默认值 → 仍需COPY,锁表 - 加
NOT NULL DEFAULT x列 →INPLACE,但会触发全表更新默认值,可能长时间持有行锁 -
LOCK=NONE仅对部分操作生效,不是万能开关
MySQL 8.0+:INSTANT 支持末尾加列,但适用场景极窄
8.0.12 起支持 INSTANT 算法,典型操作是“在表末尾加一列”或“删列”。它只修改数据字典,不触碰聚簇索引,真正零拷贝、秒级完成、不加 MDL 排他锁。
但注意:INSTANT 是特例,不是通解。只要涉及列顺序调整、类型变更、非末尾加列、加约束、改默认值等,MySQL 仍会退回到 INPLACE 甚至 COPY。
- 加列必须在最后位置,且不能是
FIRST或指定AFTER xxx - 不能对已有数据做逻辑变更(比如把
VARCHAR(10)扩到VARCHAR(20)就不支持INSTANT) -
INSTANT操作后首次访问该列的行时,才惰性补默认值——这点容易被监控忽略
为什么底层流程不能统一?根本矛盾在存储格式与事务一致性
MySQL 的锁策略差异,本质是 InnoDB 存储引擎在“物理结构安全”和“逻辑一致性”之间做的权衡。比如:
-
COPY最稳妥:新旧表完全隔离,崩溃可回滚到原表 -
INPLACE要求原地修改页结构,必须保证每一步都可前滚/回滚,因此某些变更(如列类型扩展)无法原地完成 -
INSTANT绕过数据页,靠字典标记+运行时解析,牺牲了部分语义灵活性来换速度
真正容易被忽略的点是:**你写的那条 ALTER 语句最终走哪种算法,MySQL 不由你决定,而是由存储引擎根据当前表结构、字段定义、版本能力动态判定**。查 performance_schema.table_lock_waits_summary_by_table 或执行 SHOW PROCESSLIST 看状态,比盲目信文档更可靠。











