MySQL 8.0的INSTANT DDL能秒级完成,是因为仅修改mysql.columns、mysql.innodb_sys_columns等系统表元数据,不触碰用户数据页;读旧数据时动态拼接默认值,新行才真实写入,全程无数据扫描、索引重建或临时表拷贝。

MySQL 8.0 的 ALTER TABLE 能秒级完成,不是因为“快”,而是因为“没动数据”——它只改了元数据,不碰一行磁盘数据。
INSTANT DDL 实际上只修改哪些系统表
它不重建表、不扫描行、不写 redo log(对数据页),只更新 InnoDB 内置的系统表:
-
mysql.columns:插入新列定义(名称、类型、默认值、位置偏移) -
mysql.innodb_table_stats和mysql.innodb_index_stats:更新表/索引统计版本号(TOTAL_ROW_VERSIONS) -
mysql.innodb_sys_columns:新增列元信息(含hidden标志、是否 instant 列)
这些操作在事务内完成,原子提交。你执行完 ALTER TABLE ... ALGORITHM=INSTANT,SHOW CREATE TABLE 立刻可见新列,但老数据行的物理存储(.ibd 文件里)仍和原来一模一样。
为什么老数据查出来却有新列的值
这不是“补全”,是查询时动态合成:
- SELECT 读到旧行(无该列字段) → MySQL 检查该行对应元数据版本号 → 发现当前表结构多了
new_col→ 自动填入其DEFAULT值 - INSERT 新行 → InnoDB 按最新列定义写入完整字段(含新列真实值)
- UPDATE 旧行 → 仅更新原字段;除非显式 SET 新列,否则该列仍保持“逻辑默认值”,物理上依然不存在
所以 SELECT * FROM t1 对老行返回默认值,SELECT new_col FROM t1 同样生效 —— 全靠解析层实时拼接,不是存储层真有数据。
ALGORITHM=INSTANT 失败最常见的 4 个硬性拦截点
报错 ERROR 1845 (0A000): ALGORITHM=INSTANT is not supported,基本就这四类:
- 引擎不是
InnoDB(比如MyISAM、Memory表直接不支持) - 表启用了压缩:
ROW_FORMAT=COMPRESSED或KEY_BLOCK_SIZE > 0(哪怕只是建表时写了ROW_FORMAT=DYNAMIC,也要确认实际生效值) - 表上有
FULLTEXT索引(哪怕已废弃不用,也得先DROP INDEX) - 单条语句混了多个操作:
ADD COLUMN a INT, ADD INDEX idx_a(a)—— 即使语法合法,INSTANT 也不认,必须拆成两条独立ALTER
验证方式别只信 SHOW CREATE TABLE:用 SELECT ROW_FORMAT FROM INFORMATION_SCHEMA.INNODB_TABLES WHERE NAME = 'db_name/tbl_name'(注意库名小写+斜杠)看真实行格式;用 SHOW INDEX FROM tbl_name 扫全文本索引。
Instant 加列后,新列默认值存在哪里
不在用户数据页,也不在系统表的“值字段”里,而是一个轻量描述符:
- 存于表的
instant columns元信息区(每张表最多支持 64 次 instant 加/删列) - 包含:列序号、默认值类型(常量 / 表达式)、字节长度、是否为
NULL - 例如
DEFAULT 'hello'存的是字符串字面量二进制;DEFAULT CURRENT_TIMESTAMP存的是类型标记 + 生成逻辑标识
这意味着:默认值不能是函数调用结果(如 NOW() 在 8.0.29 前不支持),也不能是子查询;且一旦设了 DEFAULT,后续想删该列或改默认值,都可能触发非 instant 回退。


















