instant算法生效需同时满足:仅限add column(末尾)、drop column等元数据操作;引擎为innodb且row_format=dynamic/compressed;新增列不能为not null无默认值;表不含fulltext/spatial索引、非分区表;innodb_strict_mode=on。

MySQL 8.0 新增列快,不是因为“执行更快”,而是根本不用动数据行——INSTANT 算法只改元数据,跳过重建表流程。5.7 的 ALTER TABLE ... ADD COLUMN 默认走 COPY 或 INPLACE,哪怕加一个 VARCHAR(1) 字段,也可能触发全表拷贝和索引重建。
INSTANT 算法生效的前提条件有哪些?
它不是无条件可用,必须同时满足以下几点,否则会自动退化为传统方式:
- 操作类型仅限于
ADD COLUMN(且不能是FIRST)、DROP COLUMN、RENAME COLUMN、CHANGE COLUMN(仅改默认值或注释) - 表引擎必须是
InnoDB,且格式为Dynamic或Compressed(Redundant和Compact不支持) - 新增列不能有
NOT NULL且无默认值(否则需填充旧行,无法跳过) - 表不能含全文索引(
FULLTEXT)、空间索引(SPATIAL),也不能是分区表 -
innodb_strict_mode=ON(8.0 默认开启,关掉可能绕过校验但不推荐)
为什么加个字段后 SELECT 能读到默认值?
关键在“版本号+内存拼装”:InnoDB 为每张表维护一个 table_version,老数据行仍用旧版本号;新插入行用新版本号,并带新增列真实值。当查询老行时,引擎发现其版本低于当前表结构版本,就从数据字典中取出该列的 DEFAULT 值,在内存中动态补上,再返回给客户端。这个过程对 SQL 层完全透明,也不写磁盘。
注意:SELECT * FROM t 查老行时,新增列的默认值是运行时计算的,不是物理存储的——所以虚拟列、函数默认值(如 DEFAULT (UUID()))在 INSTANT 场景下不被允许,因为无法保证一致性。
容易踩的坑:明明满足条件却没走 INSTANT?
常见原因包括:
- 执行
ALTER TABLE时用了ALGORITHM=COPY或ALGORITHM=INPLACE显式指定,覆盖了自动判断逻辑 - 表里存在
GENERATED列(即使未使用),会强制禁用 INSTANT -
innodb_file_format被设为Antelope(8.0 已废弃,但若从老版本升级未清理,仍可能残留影响) - 执行前刚做过
OPTIMIZE TABLE或大事务回滚,导致内部元数据状态异常,可尝试FLUSH TABLES后重试
验证是否真走了 INSTANT:执行完后查 INFORMATION_SCHEMA.INNODB_TABLES,看 INSTANT_COLS 列是否大于 0;或者观察 SHOW CREATE TABLE 输出里是否有 ALGORITHM=INSTANT 注释(8.0.29+ 版本会显式写出)。
最常被忽略的一点:INSTANT 只解决“加列”,不解决“改列类型”或“加索引”。比如 ADD COLUMN x INT DEFAULT 0 是毫秒级,但紧跟着 ADD INDEX (x) 就又变成秒级甚至分钟级——这两步不能合并优化,得分开评估。











