编程 MySQL 加字段为什么有时秒级、有时锁表:INSTANT / INPLACE / COPY 怎么选

2026-10-14 23:35:48

MySQL 加字段为什么有时秒级、有时锁表:INSTANT / INPLACE / COPY 与 pt-osc、gh-ost 的取舍

先放几个会用到的地址:

  • MySQL 8.4 官方 InnoDB 在线 DDL 文档:https://dev.mysqlserver.cn/doc/refman/8.4/en/innodb-online-ddl.html
  • pt-online-schema-change(Percona Toolkit):https://www.percona.com/software/database-tools/percona-toolkit
  • gh-ost:https://github.com/github/gh-ost

三种 ALTER 算法

同样是 ALTER TABLE,耗时差几个数量级,根源是走了哪种算法。

  • INSTANT:只改数据字典元数据,不动表数据,秒级完成,不受表大小影响。表尾加列(add column)、修改字段名、修改表名等可以走这条路。
  • INPLACE(ALGORITHM=INPLACE):存储引擎层就地重建表,不阻塞 DML,但大表变更期间可能长时间主从不一致。
  • COPY(ALGORITHM=COPY):像「修改列类型」「修改表字符集」这类只能 table copy 的操作,单线程重建全表并阻塞 DML,不属于 Online DDL。

用 ALTER 语法控制行为

ALTER TABLE tbl_name ADD PRIMARY KEY (column), ALGORITHM=INPLACE;
ALTER TABLE area algorithm=inplace, lock=none, add index idx_fa (father);
  • LOCK 子句:LOCK=NONE 允许读写,LOCK=SHARED 只允许读。请求的并发级别不可用时,语句立即停止而不是降级执行。
  • 显式指定 ALGORITHM=INSTANT / INPLACE / COPY,可以用于性能对比,也可以强制回退到 copy。
  • 默认 MySQL 会尽量选择 inplace + lock=none,通常不需要手写这两个选项;如果担心线上负载,显式指定更安全,因为不支持时会立刻报错,而不是悄悄退化成锁表操作。
  • innodb_online_alter_log_max_size:DDL 期间并发 DML 的日志上限。临时日志超限时 DDL 失败,未提交的 DML 会被回滚。调大能容纳更多并发 DML,代价是末尾应用日志阶段锁表时间更长。

改列类型为什么还是得 COPY

MySQL 生态里修改字段类型需要 COPY 全表重建,期间只能查询、不能并发写入,而且单线程效率低;到 MySQL 8.0 仍然只能 COPY。所以「加字段秒级完成」的经验,换到「改字段类型」就完全失效。

报错原文长这样:

ERROR 1846 (0A000): ALGORITHM=INSTANT is not supported. Reason: Cannot change column type INPLACE. Try ALGORITHM=COPY/INPLACE.

云厂商对在线改类型的补丁

PolarDB MySQL「秒级修改字段 Instant Modify Column」:只改表定义、不改已有数据,任意大小的表都能快速改类型。

  • 开关 loose_innodb_polar_instant_modify_column_enable,默认 OFF。
  • loose_innodb_polar_instant_modify_column_max_times 单表最大次数默认 16,范围 1~64。
  • 限制:仅支持长度扩展变更,不支持改长度以外的属性;仅支持行格式 COMPACT / DYNAMIC;不支持分区表;不支持同一条 SQL 里混合其他 DDL。
  • 灰度发布需要配额 polarm_82_instant_modify_column。可以用 EXPLAIN DDL 预判。INFORMATION_SCHEMA.INNODB_INSTANT_COLUMNS 记录被秒级修改过的字段。
ALTER TABLE t modify COLUMN a bigint DEFAULT NULL,algorithm=INSTANT;

AliSQL「在线修改列类型 Online Modify Column」:把改列类型下沉到 InnoDB 引擎层,复用 INPLACE 框架,过程中不阻塞读写。

  • 版本要求:MySQL 8.4 / 8.0 内核小版本 ≥20250731 / 5.7 内核小版本 ≥20251031。
  • 开关 loose_rds_upgrade_datatype_online_enable,默认 OFF。
  • 限制:仅 InnoDB;整数类型只支持从小变大(TINYINT/SMALLINT/MEDIUMINT/INT/BIGINT,不支持反向);CHAR/VARCHAR 只支持变长(CHAR(M)→CHAR(N), N>M);不支持修改分区键包含的列。
ALTER TABLE `t1` MODIFY `c1` bigint DEFAULT NULL, ALGORITHM=INPLACE;

腾讯云 TDSQL Boundless:用 schema version + 多版本 schema 解析,按托马斯写入法则处理回填与并发 DML,参考 Google F1 引入过渡态和 Write Fence 版本校验;Fast Online DDL 走 bulk load external sst + ingest 到 TDStore Lmax 层,省去时间戳对比。

大表该用 pt-osc 还是 gh-ost

  • pt-osc(pt-online-schema-change):用触发器同步增量,支持流控,对从库延迟敏感的场景更可控;代价是引入触发器带来锁竞争。
  • gh-ost:不依赖触发器,模拟从库拉取 row binlog,把增量异步应用到 ghost 表。流程是:直连主库 → 建 ghost 表 → 在新表 alter → 迁移原表数据 → 解析 binlog 应用到新表 → cut-over 用新表替换原表。原则是 binlog 优先于 row copy,冲突时用 insert ignore / replace into。

gh-ost 的 cut-over 利用了 MySQL 的原子性,以及 rename 请求在被 block 的请求中优先级最高的特性:一个连接对原表加锁,另一个连接尝试 rename 被 block,释放锁后 rename 先执行。整个过程可监控、可动态暂停。

选择上,如果实例里触发器本身就多、或者业务对触发器敏感,优先 gh-ost;如果已有 Percona Toolkit 的运维体系、需要现成的流控参数,pt-osc 上手更快。

openGauss 的在线 DDL

ALTER TABLE、VACUUM FULL、CLUSTER 新增 CONCURRENTLY 关键字触发在线 DDL,分四阶段:DDL 准备、基线数据拷贝、增量数据合并、DDL 提交。首末阶段持短暂八级锁,中间以四级锁为主,允许并发 DML。实现上引入 ddlDeltaLog 记录并发 INSERT/UPDATE/DELETE 及 ctid,ctidMap 记录新旧表 ctid 映射。不适用于存在对被操作表并发修改的长事务的场景。

参考链接

  • MySQL 8.4 InnoDB 在线 DDL:https://dev.mysqlserver.cn/doc/refman/8.4/en/innodb-online-ddl.html
  • pt-osc:https://www.percona.com/software/database-tools/percona-toolkit
  • gh-ost:https://github.com/github/gh-ost

标签:MySQL、Online DDL、字段变更、pt-osc、gh-ost

推荐文章

程序员茄子在线接单