mysqldump和物理拷贝均不可行:前者因单线程导入超大SQL文件(>1TB)需15+小时,远超停机窗口;后者跨机房无法保证/var/lib/mysql原子性,易因版本/配置/文件系统差异导致启动失败、页损坏或复制中断。

单表20亿条数据跨机房迁移,不能用 mysqldump,也不能停机拷贝,必须走“全量 + 增量”双轨同步,且增量链路要能容忍网络抖动和重放。
为什么 mysqldump 和物理拷贝都不可行
导出20亿行的 mysqldump 文件会生成超大SQL(通常 >1TB),导入时单线程逐条 INSERT,实测在万兆内网下也需 15+ 小时——这远超业务可接受的停机窗口;而物理拷贝要求源库停写或强一致性快照,跨机房无法保证 /var/lib/mysql 目录级原子性,且目标机房 MySQL 版本/配置/文件系统差异极易导致启动失败。
常见错误现象包括:Table 'xxx' doesn't exist(frm 文件缺失)、InnoDB: Database page corruption(page checksum 不匹配)、主从复制中断后无法追平(因 binlog position 跨越了拷贝时间点)。
- 即使使用
--single-transaction,对 20 亿行表执行一致性快照本身就会触发长事务,拖慢源库 DML - rsync 同步运行中的
ibd文件,99% 概率导致目标实例崩溃 - 未校验
innodb_file_per_table、innodb_page_size等底层参数,恢复后表能查但 count(*) 卡死
推荐方案:Canal + Otter(或自研消费者)做增量同步,Mydumper + Myloader 做可控全量
这是目前在生产环境验证过、兼顾可靠性与可控性的组合。核心逻辑是:先用 mydumper 并行导出全量(带 --no-schemas 避免结构冲突),同时 Canal 实时拉取源库 binlog,Otter 或自研 Kafka 消费者将变更落地到目标库;最后比对 checksum 并切换流量。
关键控制点:
-
mydumper必须加--chunk-filesize=64(单位 MB),避免单文件过大阻塞网络传输 - Canal 的
destination配置需指定slaveId,防止与线上从库 binlog 位点冲突 - 目标库必须关闭
foreign_key_checks和unique_checks,否则批量导入时索引维护开销爆炸 - 增量同步阶段,Otter 的
pipeline需开启enableDml+enableDDL,但 DDL 同步要人工确认(如ADD COLUMN不能自动执行)
网络不稳定时怎么保数据不丢
跨机房专线并非永远可靠,尤其金融/政务类客户常遇到间歇性丢包或延迟突增。此时单纯依赖 Canal → Otter 直连链路会丢事件。
真实有效的做法是中间加一层 Kafka 缓存:
- Canal 输出到 Kafka Topic,分区键用
table_name + pk_hash,保证同一行变更顺序性 - Kafka 设置
replication.factor=3、min.insync.replicas=2,防节点宕机丢数据 - 消费者(Otter 或自研)启用
enable.auto.commit=false,每处理完一批消息才提交 offset,失败可重试 - 若目标库写入失败(如唯一键冲突),消息进入
dlq主题,人工干预后重新投递,不卡主流程
注意:Kafka 消息体必须包含完整 binlog event(含 server_id、binlog_position、timestamp),否则无法回溯源头。
校验与切换阶段最容易被忽略的三个细节
很多团队全量导完、增量追平就切流量,结果上线后发现数据错位。真正关键的是校验方式和切换节奏。
- 不要只比
COUNT(*)—— 它在大表上不准(InnoDB 估算值),要用pt-table-checksum按主键分段校验,且目标库必须开启binlog_format=ROW - 切换前 10 分钟,手动在源库
STOP SLAVE并记录SHOW SLAVE STATUS\G中的Exec_Master_Log_Pos,确保增量链路已消费到该位置 - 应用层 DNS 切换必须配合连接池热刷新(如 HikariCP 的
refreshPeriod),不能等旧连接自然超时,否则部分请求仍打到老库
最复杂的不是迁移过程本身,而是如何让业务方相信“数据真的没少、没乱、没重复”。这需要把 checksum 日志、binlog position 对齐截图、Kafka lag 监控图全部准备好,而不是只说“已经同步完成了”。


















