instant add column真正“瞬时”仅当新增列允许null(即显式default null或不指定default),否则带非null默认值(如default 0、default 'x'、default current_timestamp)会退化为inplace或copy;需同时满足innodb引擎、非compressed行格式、无fulltext索引等全部条件。

INSTANT ADD COLUMN 什么情况下真正“瞬时”?
只有添加列且不指定 DEFAULT 值,或指定 DEFAULT NULL(注意不是 DEFAULT '' 或 DEFAULT 0),才能走 INSTANT 算法。MySQL 8.0.12+ 默认启用该特性,但一旦加列带非 NULL 默认值,就会退化为 INPLACE(需重建二级索引)甚至 COPY(全表拷贝)。
常见误判场景:
- 执行
ALTER TABLE t ADD COLUMN c INT DEFAULT 0→ 实际触发 INPLACE,耗时与表大小正相关 -
ALTER TABLE t ADD COLUMN c VARCHAR(10) DEFAULT 'x'→ 即使是短字符串,也强制 COPY - 对已存在大量数据的表,哪怕只加一列
DEFAULT CURRENT_TIMESTAMP,也会退化
如何验证 ALTER 是否真的走了 INSTANT?
执行完 ALTER 后立刻查 INFORMATION_SCHEMA.INNODB_TABLES 或使用 SHOW PROFILE,但最直接的方式是看 performance_schema.table_io_waits_summary_by_table 中该表的 WRITE_ROWS 计数 —— INSTANT 操作该值应为 0。
更稳妥的做法是在变更前开启慢日志并设 long_query_time = 0,然后执行 ALTER,观察是否生成慢查询记录。若没记录、且执行耗时稳定在毫秒级(比如 0.012s),基本可确认是 INSTANT。
注意:即使语句返回快,也要检查 SHOW ENGINE INNODB STATUS 的 LATEST DDL LOG 区域,里面会明确写 type: INSTANT 或 type: INPLACE。
大表迁移中必须绕开的三个默认陷阱
INSTANT 只解决“加列”,不解决“改类型”“删列”“加索引”“改默认值”。实际迁移中,这些操作常被连带发起,导致整条语句失效:
-
ADD COLUMN c INT DEFAULT 0, ADD INDEX idx_c(c)→ 整个语句退化为 INPLACE,索引构建阶段锁表时间飙升 -
MODIFY COLUMN c VARCHAR(50)→ 无论原字段多小,都必须重建聚簇索引,INSTANT 完全不生效 - 后续用
UPDATE t SET c = ...补默认值 → 这步才是真正的性能杀手,会产生大量 undo log 和磁盘 I/O,且阻塞并发写入
正确节奏应该是:先 ADD COLUMN c INT(INSTANT),再分批 UPDATE(带 LIMIT + WHERE id BETWEEN ? AND ?),最后用 ALTER TABLE ... ALTER COLUMN c SET DEFAULT 0(这个 SET DEFAULT 是 INSTANT 的,不触发行数据修改)。
为什么线上不敢直接用 INSTANT,而要搭配 pt-online-schema-change?
INSTANT 本身不锁表、不复制数据,但它不保证 DML 兼容性。MySQL 在 INSTANT 列刚加入后,旧版本客户端或未刷新元数据的连接可能读到 NULL 或报错 Unknown column 'c' in field list,尤其在应用使用连接池、未及时重连时。
更隐蔽的问题是 binlog:INSTANT 列变更不写行事件,主从延迟感知不到结构变化,如果从库还没执行完 ALTER,主库就开始写新列,就会导致从库 SQL 线程报错 Column c of table t cannot be null。
所以生产环境稳妥做法是:用 pt-online-schema-change 发起变更,它内部检测到支持 INSTANT 后会自动选用,同时帮你管控连接切换、校验主从一致性、控制更新批次——INSTANT 是引擎能力,但落地需要工具兜底。
真正容易被忽略的是:哪怕用了 INSTANT,information_schema.COLUMNS 视图的更新仍可能有毫秒级延迟,某些 ORM 初始化时缓存列名,会导致短暂报错,得预留好降级逻辑。











