mysql执行select语句时,先经连接器校验身份,再查查询缓存(命中则直返结果),未命中则由分析器做词法与语法解析,优化器生成执行计划,执行器调用存储引擎获取数据;server层统一处理连接、解析、优化、执行等逻辑,存储引擎层(如innodb)负责实际数据存取、索引、事务等。

INSTANT加列到底改了什么
它不碰任何一行数据,只改三样东西:数据字典里的列定义、表元信息区的默认值描述符、行记录头部的“列存在位图”。老数据物理上还是旧格式,SELECT时MySQL在server层动态补默认值——不是写进磁盘,是运行时合成。
这意味着:DESCRIBE t1立刻看到新列,SELECT * FROM t1对旧行返回默认值,但SELECT c1 FROM t1 WHERE id = 123查到的仍是原始行结构里没有c1字段的那条记录。
为什么DEFAULT NULL能走INSTANT,DEFAULT 'x'就不行
语义冲突是核心问题。如果旧数据没这个字段,又指定了非NULL默认值,MySQL无法区分“该字段尚未存在”和“该字段值就是'x'”。所以强制要求:带非NULL默认值的列必须把值真实写入每一行,这就绕不开全表扫描。
-
ADD COLUMN c1 INT NULL→ 可INSTANT(显式声明DEFAULT NULL或省略) -
ADD COLUMN c1 INT NOT NULL DEFAULT 42→ 退化为INPLACE(必须写入每行) -
ADD COLUMN c1 VARCHAR(10) DEFAULT NOW()→ 直接报错(函数默认值不支持INSTANT)
哪些条件不满足就会让INSTANT自动降级或报错
INSTANT不是版本开关,而是硬性校验。只要有一项不满足,MySQL要么fallback到INPLACE(耗时),要么直接报ERROR 1845 (0A000)。
- 表引擎不是
InnoDB(MyISAM/MEMORY一律不支持) - 行格式是
COMPRESSED或KEY_BLOCK_SIZE > 0 - 表上有
FULLTEXT索引 - 语句里混了其他DDL,比如
ADD COLUMN x INT, ADD INDEX idx_x(x) - MySQL 8.0.12–8.0.27中,表存在隐式主键(建表时没写
PRIMARY KEY且无NOT NULL UNIQUE列)
验证方式:用SHOW CREATE TABLE t1看引擎和索引;用SELECT ROW_FORMAT FROM INFORMATION_SCHEMA.INNODB_TABLES WHERE NAME = 'db/t1'确认真实行格式(库名小写+斜杠)。
怎么确认你的ALTER真的走了INSTANT
别信执行时间短——小表本来就快。真正判断依据是“是否与表大小脱钩”和“是否绕过数据页重写”。
- 对1TB表执行
ALTER TABLE t ADD COLUMN x INT NULL,若耗时仍SHOW PROCESSLIST显示状态卡在altering table,说明没走INSTANT - 更可靠的方式:开启
performance_schema后查events_stages_history_long,过滤instant关键字:SELECT EVENT_NAME, WORK_COMPLETED, WORK_ESTIMATED FROM performance_schema.events_stages_history_long WHERE EVENT_NAME LIKE '%alter%instant%' ORDER BY TIMER_START DESC LIMIT 1 - 强制指定
ALGORITHM=INSTANT,失败会明确报错,比默认隐式降级更容易定位问题
INSTANT真正的复杂点在于:它看起来像“加了个字段”,实际是引入了一套运行时字段解析机制。一旦后续要对该列建索引、做分区裁剪、或参与JSON生成,行为就不再透明——这些地方容易被忽略,但恰恰是线上出问题的高发区。











