不能把“在线改结构”和“在线迁移”混用:前者是单库内轻量元数据变更,后者是跨实例数据流重定向,目标、风险与回滚路径均不同;ALTER TABLE 和 pt-online-schema-change 均不解决跨库迁移问题。

直接上结论:不能把“在线改结构”和“在线迁移”当成同一类操作来混用。前者是单库内元数据/数据页层面的轻量变更,后者是跨实例、跨拓扑的数据流重定向,二者目标不同、风险维度不同、失败回滚路径也完全不同。
ALTER TABLE 和 pt-online-schema-change 都不解决跨库迁移问题
很多人误以为在主库跑 pt-online-schema-change 或 ALTER TABLE ... ALGORITHM=INPLACE, LOCK=NONE 就能“顺便完成迁移”,这是危险误解。这些工具只作用于单个 MySQL 实例内的某张表,不会动 binlog 位置、不会改复制关系、更不会帮你把数据从 A 实例同步到 B 实例。
常见错误现象包括:
- 在旧主上用
pt-osc改了表结构,但从库没同步该变更(因为pt-osc默认不连从库),结果主从表结构不一致,后续 INSERT 报错ERROR 1690 (22003): DOUBLE value is out of range - 用
ALTER TABLE加了字段,但新字段带DEFAULT值,导致从库重放时 fallback 到COPY模式,Seconds_Behind_Master突然飙升到几小时 - 迁移过程中误删旧库表,而新库还没完成全量同步,应用查不到数据直接报 500
真正结合的唯一安全路径:先保结构一致,再控数据流向
所谓“结合”,本质是分阶段控制两个独立动作的时间窗口和依赖关系。核心原则是:结构变更必须在迁移启动前完成,且主从结构完全对齐;数据迁移过程禁止任何 DDL。
实操要点:
- 迁移前一周,在主库执行所有必要 DDL(如加列、调索引),并用
SELECT @@global.gtid_executed记录变更后 GTID 集合;在从库验证该集合是否完全一致 - 启动迁移工具(如
mysqldump --single-transaction --master-data=2或xtrabackup)时,必须基于结构已定型的快照;不能边 dump 边改表 - 双写阶段(如用 canal + MQ 同步)中,所有写入语句必须兼容新旧两张表结构——比如新加的
status字段在旧表里要允许 NULL,否则 INSERT 会失败 - 校验阶段必须比对行级数据,不能只看
COUNT(*):用pt-table-checksum扫描分块校验,避免因 longtext 字段截断或时区差异导致漏检
GTID 模式下最容易被忽略的陷阱
GTID 不是万能锁,它只保证事务不丢、不错乱,但无法自动修复结构不一致引发的复制中断。一个典型静默故障是:ALTER TABLE t ADD COLUMN c INT DEFAULT 0 在主库成功,但从库 binlog event 缺失该字段(binlog_row_image=MINIMAL 下易发),导致从库报 Last_SQL_Errno: 1785 并停止同步,而 SHOW SLAVE STATUS 里 Seconds_Behind_Master 仍显示为 0。
所以迁移期间必须盯住三处:
- 主库
SHOW MASTER STATUS的Executed_Gtid_Set - 从库
SHOW SLAVE STATUS\G的Retrieved_Gtid_Set和Executed_Gtid_Set - 每张表的
information_schema.COLUMNS中COLUMN_DEFAULT和IS_NULLABLE是否主从一致
结构微小差异,可能让整个迁移流程卡在最后 1% 无法收口。


















