大表迁移的挑战与核心原则
当单表数据量超过500GB或行数过亿时,传统mysqldump导出再导入的方式会面临锁表时间长、网络传输慢、目标端写入压力大等致命问题。在线不停机迁移的核心原则是最小化对业务的影响,即迁移过程中主库读写延迟必须控制在可接受范围(通常RT增幅小于20%),且任何时刻都要具备快速回滚能力。
经验法则:迁移方案的选择取决于表结构是否变更、目标端是否同构、以及允许的最大停机窗口。若允许10分钟停机,物理文件拷贝(如Percona XtraBackup)是最优解;若要求零停机,则必须采用逻辑复制或在线DDL工具。
方案一:基于主从复制的滚动迁移
适用于源库与目标库版本一致、表结构不变的同构迁移。步骤如下:
- 在目标库建立与源库相同的表结构(使用
SHOW CREATE TABLE获取DDL)。 - 在源库配置binlog格式为ROW,并记录当前binlog文件名与位置(
SHOW MASTER STATUS)。 - 使用
mydumper并行导出数据(建议--chunk-filesize=256M),通过myloader并行导入目标库。 - 启动复制线程:
CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000123', MASTER_LOG_POS=456789;,持续追平增量数据。 - 当主从延迟小于5秒时,短暂开启只读(
SET GLOBAL read_only=ON),等待延迟归零,切换应用连接。
关键优化:导出时使用--single-transaction保证一致性快照,导入时关闭目标库外键检查(SET FOREIGN_KEY_CHECKS=0)和唯一键检查(UNIQUE_CHECKS=0),可提升40%以上导入速度。
方案二:pt-online-schema-change(PT-OSC)
当迁移同时需要变更表结构(如增加索引、修改列类型)时,PT-OSC是最成熟的选择。其原理是:
- 创建与源表结构一致的临时表
_table_new,应用新结构。 - 在源表上创建三个触发器(INSERT/UPDATE/DELETE),将增量变更实时同步到临时表。
- 分批拷贝源表数据(默认每次1000行),通过
--chunk-size控制批次大小。 - 拷贝完成后,使用
RENAME TABLE原子切换表名。
实战命令示例:
pt-online-schema-change --alter "ADD INDEX idx_user_id (user_id)" D=db_name,t=big_table --host=127.0.0.1 --user=root --ask-pass --max-load Threads_running=50 --critical-load Threads_running=100 --chunk-size=500 --pause-file=/tmp/pt-osc.pause注意:PT-OSC会占用额外磁盘空间(约为原表1.5倍),务必提前确认存储余量。同时建议在业务低峰期执行,并监控主从延迟。
方案三:gh-ost(无触发器迁移)
gh-ost由GitHub开源,采用binlog监听替代触发器,避免了对源表的额外写入开销。其工作流程:
- 连接源库作为从库,读取binlog事件流。
- 在目标库创建影子表,持续应用binlog中的变更。
- 从源表分批读取行数据写入影子表,通过
--throttle-control-replicas控制节流。 - 最后通过原子RENAME完成切换。
gh-ost的最大优势是不占用源库的触发器资源,且支持暂停/恢复(--throttle-http),适合高并发写入场景。但要求源库开启binlog_format=ROW且binlog_row_image=FULL。
性能调优与回滚预案
无论哪种方案,以下优化策略通用:
- 迁移期间将源库
innodb_buffer_pool_size临时调大10%,加速数据读取。 - 使用
--compress选项压缩网络传输,减少带宽占用。 - 分批提交事务,每批1000-5000行,避免长事务。
回滚预案:若迁移过程中出现严重性能劣化,立即执行STOP SLAVE或--pause命令,并恢复应用连接指向源库。对于PT-OSC和gh-ost,切换前保留旧表(--no-swap-tables),以便快速回切。
总结与选型建议
根据实际场景选择:
- 表结构不变且可接受短暂只读 → 主从复制滚动迁移。
- 需要改表结构且允许触发器 → PT-OSC。
- 高并发环境且禁止触发器 → gh-ost。
- 允许10分钟停机 → XtraBackup物理备份恢复。
最后,务必在测试环境用全量数据演练一遍,记录各阶段耗时,再应用到生产。