instant加字段快在仅修改三处元数据且不触碰用户数据:更新mysql.columns、innodb_sys_columns及表头“版本位”,读旧行时动态拼接默认值,新行才写入,故耗时恒定、与表大小无关,全程无数据页扫描、索引重建或临时表拷贝。

它真能秒加字段、不锁表、不重建数据——但前提是五个硬性条件全满足,漏一个就 fallback 到几小时的 COPY。
INSTANT 加字段到底快在哪?
它不碰一行用户数据,只改三处元数据:mysql.columns 插新列定义、innodb_sys_columns 记偏移位置、表头加“版本位”标识。后续 SELECT 读旧行时,InnoDB 动态拼接默认值;INSERT 新行才真正写入该列值。所以耗时恒定,和表大小无关。
- 物理存储里老数据行依然没这个字段,
DESCRIBE t1看得见,SELECT *返回的是运行时合成的默认值,不是补全 - 没有数据页扫描、没有索引重建、没有临时表拷贝,自然不占额外磁盘空间、不拖慢主从复制
- MDL 锁只在元数据更新前后各持一次,通常
为什么 ALGORITHM=INSTANT 会突然报错 ERROR 1845?
这不是版本问题,而是五条硬门槛中至少一条没过。常见失效点:
- 引擎不是
InnoDB:MyISAM、Memory、CSV表直接不支持 - 行格式是
COMPRESSED或KEY_BLOCK_SIZE > 0:查SELECT ROW_FORMAT, KEY_BLOCK_SIZE FROM INFORMATION_SCHEMA.INNODB_TABLES WHERE NAME = 'db/t1' - 表上有
FULLTEXT索引:哪怕只有一条,INSTANT 就禁用 - 单条
ALTER TABLE混了其他操作:比如ADD COLUMN c INT和ADD INDEX idx_c (c)写在同一句里,逗号分隔也不行 - MySQL 8.0.12–8.0.27 中,表无显式主键且无
NOT NULL UNIQUE列(即依赖隐式主键)
如何验证一张表是否真正支持 INSTANT?
别只看 MySQL 版本或 SHOW CREATE TABLE,必须查运行时状态:
- 确认引擎:
SHOW CREATE TABLE t1中必须含ENGINE=InnoDB - 确认无全文索引:
SHOW CREATE TABLE t1输出里不能出现FULLTEXT KEY - 查真实行格式:
SELECT ROW_FORMAT FROM INFORMATION_SCHEMA.INNODB_TABLES WHERE NAME = 'db/t1'(库名小写 + 斜杠) - 查是否压缩:
SELECT KEY_BLOCK_SIZE FROM INFORMATION_SCHEMA.INNODB_TABLES WHERE NAME = 'db/t1',结果非 0 即不支持 - 查 instant 操作次数:
SELECT instant_cols FROM INFORMATION_SCHEMA.INNODB_TABLES WHERE NAME = 'db/t1',超过 64 次后需重建表才能继续用 INSTANT
ALGORITHM=INSTANT 的实操雷区
显式指定比依赖自动 fallback 更安全:
- 加字段时强制写
ALGORITHM=INSTANT:失败立刻报错,避免静默降级成慢操作 - 不要和
LOCK=NONE同时用:ALGORITHM=INSTANT已隐含零锁,加LOCK参数会直接报错 - 8.0.29+ 支持
FIRST/AFTER定位加列,但整条语句仍只能做列变更,混任何其他 DDL 都会失效 -
MODIFY COLUMN、ALTER COLUMN SET DEFAULT、改NOT NULL约束等,哪怕只动一个属性,也会退出 INSTANT,退化为 INPLACE
INSTANT 的边界很清晰:只要不改变已有数据行的物理布局,它就能瞬时完成;一旦涉及重排、填充、重建,锁就回来了。最易被忽略的是那 64 次限制和隐式主键陷阱——它们不会在建表时暴露,只会在第 65 次加列或升级到 8.0.27 后才突然卡住。











