alter table本质是物理重建表:创建新表、逐行拷贝数据、重建索引、重命名替换,全程需全表扫描与写入;而update仅修改匹配行所在数据页,不重建索引,开销量级不同。

因为 ALTER TABLE 在绝大多数情况下要重建整张表,而 UPDATE 只改匹配行的数据页 —— 这是本质差异,不是“慢一点”,而是量级不同。
ALTER TABLE 实际干了什么?
InnoDB 默认执行 ALTER TABLE(比如 ADD COLUMN、MODIFY COLUMN)时,并不是直接改元数据,而是:
- 创建一张结构更新后的新空表
- 逐行读取原表数据,写入新表(含所有索引重建)
- 重命名新表为原表名,删旧表
- 整个过程需完整扫描 + 写入 + 排序 + 索引构建,I/O 和 CPU 压力集中
哪怕只加一个 VARCHAR(10) 字段,5000 万行就要做 5000 万次 INSERT + 所有二级索引的 B+ 树分裂和重组。这不是“改个定义”,是物理重写。
UPDATE 为什么相对快?
UPDATE 的开销取决于 WHERE 条件是否命中索引、修改字段是否涉及索引列、以及是否触发行迁移(如 VARCHAR 变长字段扩大)。但它:
- 只定位并修改目标行所在的数据页(通常几 KB~几十 KB)
- 不触碰未匹配的行,更不重建索引树(除非更新的是索引列本身)
- 事务日志(redo log)记录变更即可,无需全量复制
例如:对 5000 万行的 users 表执行 UPDATE ... WHERE id = 123456,实际只操作 1 行所在的 1 个数据页;而 ALTER TABLE users ADD COLUMN phone VARCHAR(20) 要处理全部 5000 万行 + 每个二级索引的全部条目。
哪些 ALTER 操作能跳过重建?
MySQL 8.0+ 支持 ALGORITHM=INSTANT,但限制极严:
- 仅支持添加/删除列(不含
DEFAULT值)、重命名列、修改列注释 - 不能改类型、不能加
NOT NULL、不能改ENUM/SET值列表(已用值除外) - 一旦涉及默认值存储或类型转换(如
INT → BIGINT),立刻 fallback 到COPY模式
例如:ALTER TABLE t ALTER COLUMN c SET DEFAULT 'x' 是 instant;但 ALTER TABLE t MODIFY c VARCHAR(100) NOT NULL 必须 copy —— 即使该列原本就是 NOT NULL,只要语义上“确认”了约束,就触发重建。
真正卡住的往往不是 SQL,是锁和复制延迟
大表 ALTER TABLE 期间:
- DDL 会持有
S(共享)MDL 锁,阻塞后续UPDATE/INSERT/DELETE,直到 copy 完成才释放 - 主从架构下,copy 阶段产生的大量 binlog 会堆积,导致从库严重延迟
- 如果用了
pt-online-schema-change,它靠触发器捕获增量,但高并发写入可能拖慢 copy 进度,甚至触发TOO MANY CONNECTIONS或死锁
所以你看到的“42 分钟”,可能只有 15 分钟在真正 copy 数据,其余时间耗在锁等待、I/O 竞争、从库追赶或触发器开销上。
别指望靠调大 innodb_buffer_pool_size 或 sort_buffer_size 来“加速”普通 ADD COLUMN —— 它们只缓解瓶颈,不改变必须全表扫描的事实。真要动大表结构,得提前规划影子表、业务灰度、binlog 过滤,或者接受 DDL 就是数据库里最重的操作之一。











