MySQL 8.0 Clone Plugin 实战:不跑 mysqldump,把 500G 数据跨洋搬进轻云互联美国服务器

1. 痛点与选型思路

从国内 IDC 迁移到美国服务器,最大瓶颈不是网速,而是 跨洋链路的 RTT 延迟和长肥管道丢包。用 mysqldump 导逻辑备份,到了 500G 这个量级,光导出就要 2~3 小时,导入时目标端还要做二级索引重建,总耗时动辄 8 小时以上。用 Percona XtraBackup 又得装额外依赖,在目标端还得处理增量备份的应用顺序。

MySQL 8.0 的 Clone Plugin 是一个一直被人忽略的官方原生方案:它直接在源库和目标库之间跑物理克隆,走 MySQL 协议,无需中间文件,也不用额外工具。配合 GTID 复制和最后几分钟的只读切换,可以把整体停机窗口压到 1 分钟以内

下面是我在轻云互联美国服务器(CentOS 7 + MySQL 8.0.20)上做的完整迁移实战。目标端是轻云互联的 E5 高频版,内存 64G,NVMe SSD,带宽 100M 独享,实测跨洋 RTT 约 180ms。

2. 前置条件

  • 源库和目标库都必须是 MySQL 8.0.17+,官方文档虽然写的是 8.0.17,但 8.0.20 以下有大量 Bug,强烈建议都升到 8.0.28+。
  • 源库必须加载 clone 插件(默认自带),目标库必须允许 remote cloning。
  • 目标库可以是空实例,也可以是临时环境,因为 CLONE INSTANCE 会销毁目标库原有数据目录
  • 磁盘空间:目标库的可用空间必须大于源库的数据大小。不要只算 data 目录,还要留出 clone 内部的临时文件和 binlog 空间。
  • 双方都开启 GTID 模式(推荐):gtid_mode=ON + enforce_gtid_consistency=ON。如果不开 GTID,就得手动记录 binlog 坐标,麻烦且容易错。

3. 操作步骤

3.1 源库准备克隆账号

-- 源库执行
CREATE USER 'cloner'@'%' IDENTIFIED BY 'YourStr0ngP@ss';
GRANT BACKUP_ADMIN ON *.* TO 'cloner'@'%';
GRANT CLONE_ADMIN ON *.* TO 'cloner'@'%';
FLUSH PRIVILEGES;

注意 CLONE_ADMINBACKUP_ADMIN 缺一不可。目标库执行克隆时,用的就是源库这个账号连回源库拉数据。

3.2 目标库设置 donor 白名单

-- 目标库执行
SET GLOBAL clone_valid_donor_list = '源库IP:3306';

这个变量是 SESSION 和 GLOBAL 都有。如果没有设成 *:3306,则必须写具体的源库 IP。跨洋环境下,建议用内网或直连 IP,别走 DNS,避免解析超时。

3.3 执行远程克隆

-- 目标库执行
CLONE INSTANCE FROM 'cloner'@'源库IP' IDENTIFIED BY 'YourStr0ngP@ss';

执行后目标库会马上重启,然后开始从源库拉取所有数据文件。这个过程是流式的,源库不需要停写,因为 Clone Plugin 会基于 InnoDB 的 Redo Log 做一致性快照,克隆期间的新写入不会丢。这比 mysqldump --single-transaction 更稳,因为 clone 复制的是物理页,不会出现逻辑备份那种大事务导致的 UNDO 膨胀。

3.4 监控克隆进度

目标库重启后,重新登录,执行:

SELECT STAGE, STATE, BEGIN_TIME, END_TIME
FROM performance_schema.clone_status\G
SELECT STAGE, STATE, FILENAME, TRANSFER_TIME, DATA_SIZE
FROM performance_schema.clone_progress;

这个特别重要。跨洋链路上,你会看到 STAGE 依次是:DROP TABLE DATAFILE COPYPAGE COPYREDO COPYFILE SYNC。大部分时间卡在 FILE COPY 上。我这里 500G 数据,100M 带宽,跑了大约 7 小时。别慌,这是正常的,因为 clone 会默认用单线程传文件。
你可以通过调大 clone_donor_timeout 防止长链路断连:

-- 源库和目的库都执行
SET GLOBAL clone_donor_timeout = 3600*8;  -- 8小时超时

3.5 克隆完成后的自动状态

克隆结束后,目标库已经拥有源库的完整数据文件,并且 gtid_executed 和 gtid_purged 都同步过来了。此时目标库是独立可读写的实例。为了做增量同步,把它配置成源库的 replica:

-- 目标库执行
STOP REPLICA;
RESET REPLICA ALL;
CHANGE MASTER TO
  MASTER_HOST = '源库IP',
  MASTER_PORT = 3306,
  MASTER_USER = 'repl',
  MASTER_PASSWORD = 'repl_pass',
  MASTER_AUTO_POSITION = 1;
START REPLICA;

这个 repl 账号需要源库上有 REPLICATION SLAVE 权限,提前建好。GTID 自动定位会从源库当前 GTID 位置开始补数据,不会有重复也不会有空洞。

3.6 校验一致性

Seconds_Behind_Master 变成 0 后,在源库和目标库分别执行:

-- 源库
SET session lock_wait_timeout=1;
SELECT TABLE_SCHEMA, TABLE_NAME, COUNT(*)
FROM information_schema.tables
WHERE engine='InnoDB'
GROUP BY TABLE_SCHEMA, TABLE_NAME;

更严谨的做法是在两边都跑 CHECKSUM TABLE,但几百张表逐条跑太慢。我这里用了一个聪明的办法:只对最大的 10 张表校验,因为小表的数据很少,复制错概率极低。
如果你不放心,可以用 pt-table-checksum,但注意它在跨洋高延迟下也会很慢,不如直接看 GTID 是否一致:

SELECT @@gtid_executed;
SELECT @@gtid_purged;

只要 gtid_executed 全集和源库相等(除了源库新产生的执行记录),数据就是一致的。

4. 切换与回滚

4.1 源库秒级只读

-- 源库执行
SET GLOBAL read_only=ON;
SET GLOBAL super_read_only=ON;

4.2 等目标库追平

-- 目标库执行
SELECT SERVICE_STATE FROM performance_schema.replication_connection_status;
SHOW REPLICA STATUS\G
-- 观察 Seconds_Behind_Master = 0

4.3 应用连接切割

在 DNS 或负载均衡器上把业务流量切到轻云互联美国服务器的 IP 上。当时我们用的轻云互联提供的 SLB 快速切换,全程无感知。如果你不想用 SLB,可以直接改应用连接串,然后重启业务进程。
这里提醒:不要急着把源库恢复可写,先让目标库持续追着源库的 binlog,观察 30 分钟没有业务报错,再让源库下线。

4.4 回滚方案

如果新美国服务器出问题,源库的 super_read_only 一关,把连接池再切回来即可。因为源库一直保持着目标库回传的复制?不对——我们只做了目标库拉源库的单向复制,回滚时源库不会自动获得目标库产生的新写入。所以如果目标库短暂可写后产生新数据,需要额外处理。因此实际操作中建议:

  • 切换前先确认业务可接受短时间只读(5 分钟以内)。
  • 如果一定要双向同步,那就得用 MGR 或 Orchestrator 管理主从切换,别直接用裸复制。
  • 最稳妥的切换姿势:业务全部停写 30 秒,目标库从源库追平后,立刻在目标库执行 RESET REPLICA ALL 断掉复制,转为独立主库。这样不会产生从目标库回流到源库的双主冲突。

5. 排错与踩坑

5.1 克隆时提示 Access denied

检查源库上 cloner 账号是否真的有 BACKUP_ADMINCLONE_ADMIN 权限。用 SHOW GRANTS FOR 'cloner'@'%'; 查看。

5.2 克隆中途卡死或断连

跨洋网络抖动很常见。目的库的 clone_status 会显示 ERROR_MESSAGE。可以重试,Clone 支持断点续传?官方文档说可以,但我实测重试时往往从头开始。所以更靠谱的方式是把 clone_donor_timeout 给大,并保证网络稳定。

5.3 克隆后目标库起不来

大概率是目标库原本的配置项和源库不一样,例如 innodb_page_sizelower_case_table_names。Clone 只复制数据文件,不复制 my.cnf,所以必须提前让两边的这些关键参数保持一致。
最保险的做法:在目标库上用源库的 my.cnf 作为基底,只修改 server_id 和 datadir 路径。

5.4 复制起点报错 1236

如果遇到 Could not find first log file name in binary log index,说明源库的 binlog 已经被 purge 了。要先确认源库的 binlog_expire_logs_seconds 够长,或者在克隆前先手动锁库记录 GTID,再在目标端设置 CHANGE MASTER TO MASTER_AUTO_POSITION=0 并指定 MASTER_LOG_FILEMASTER_LOG_POS

6. 有什么理由不选 Clone?

如果源库是 MySQL 5.7 或者 MariaDB,那没辙。另外如果你的数据量超过了 1T,且源端磁盘 IO 本来就很差,Clone 在复制阶段会抢占源库的 IO,可能影响线上业务。这时优先用 mysqldump --innodb-consistent-snapshot 加上 --set-gtid-purged=ON 做逻辑迁移,或者用 mysqlshellutil.dumpInstance 更省事。

但对于 1T 以下的 MySQL 8.0 实例,Clone Plugin 是我目前见过最省心的跨洋迁移方式——一个 SQL 命令,不用装 XtraBackup,不用盯着 scp,不用处理 backup 文件的格式兼容性。配合轻云互联美国服务器的 NVMe 盘,复制完的数据文件直接挂载即用,启动速度比源库还快。

最后提醒一句:任何数据迁移,备份永远是第一优先级。Clone 不是备份,迁移前一定要在源库用 mysqldumpBACKUP DATABASE 再打一份安全网。别把主库性命全押在一个命令上。