必须停复制后验证三组值:gtid模式下比对retrieved_gtid_set与executed_gtid_set是否完全相等、主从@@global.gtid_executed输出是否一字不差;非gtid模式下比对read_master_log_pos与exec_master_log_pos是否相等且relay_master_log_file指向同一binlog文件。

迁移前怎么确认从库真追平了,不是“假0”?
Seconds_Behind_Master = 0 不等于数据已一致。它只反映 IO 线程拉日志和 SQL 线程执行之间的差值,不包含大事务内部重放耗时。比如主库一个事务更新 50 万行,从库 SQL 线程可能刚执行完前 49 万行,Seconds_Behind_Master 已显示 0,但最后 1 万行还没落地。
必须停掉复制后验证三组值:
-
STOP SLAVE后查Retrieved_Gtid_Set和Executed_Gtid_Set是否完全相等(注意空格、换行、UUID顺序) - 主库和从库分别执行
SELECT @@global.gtid_executed,输出字符串必须一字不差 - 若用非 GTID 模式,则比对
Read_Master_Log_Pos和Exec_Master_Log_Pos是否相等,且Relay_Master_Log_File指向同一 binlog 文件
任何一项不满足,说明还没真正追平,此时切流就是把延迟问题变成永久不一致。
切换时为什么 CHANGE MASTER TO MASTER_AUTO_POSITION = 1 报错 1236?
这个错误本质是:从库请求的 GTID 在新主库的 binlog 里已经不存在。常见原因有:
- 新主库 expire_logs_days 设得太小,或人为执行过 PURGE BINARY LOGS
- 从库之前连过其他主库(如测试环境),gtid_executed 里混入了别的 server_uuid 的事务
- 切换前没开 log_slave_updates,导致从库升主后无法提供完整 binlog 历史
不能跳过或硬塞 GTID。安全做法只有两个:
- 重建从库:用新主库当前一致性快照(
mysqldump --single-transaction --set-gtid-purged=ON或物理备份)恢复 - 强制对齐
gtid_purged:前提是确认从库没执行过任何额外写入,且已按规范清理 relay log 文件和内存状态
迁移窗口内如何避免“一边写旧库、一边读新库”的时间差污染?
pt-table-checksum 在延迟未清零时跑,结果全是误报——它校验的是“此刻主从快照是否一致”,而不是“是否已同步完成”。
实操要点:
- 切流前必须停写旧库(或切到只读),否则新主库持续产生 binlog,从库永远追不上
- 若无法停写,改用心跳表法:在旧主库定时插入
INSERT INTO heartbeat (ts) VALUES (NOW(6)),切换后查新从库该记录的NOW(6) - ts,确认真实延迟 ≤ 100ms 再放读流量 -
mysqldump备份时务必加--set-gtid-purged=ON,否则恢复后gtid_executed为空,后续增量同步断链
为什么并行复制开了还是慢?
MySQL 5.7+ 默认slave_parallel_type = DATABASE,只对跨库操作有效。如果业务集中在单库多表(如 shop_db),这个设置完全无效。
真正起效的是 LOGICAL_CLOCK 模式,但需同时满足:
- 主库开启
binlog_transaction_dependency_tracking = WRITESET - 从库设
slave_parallel_workers > 0,建议值为 CPU 核数 × 1.5(8 核设 12,别盲目设 32) - 检查
SHOW PROCESSLIST中是否有多个Slave_worker线程处于Waiting for an event from Coordinator状态——若有,说明并行没跑起来,大概率是主库没启用 writeset
大事务仍是最大瓶颈。与其调优索引,不如拆事务:每 1000 行提交一次,避免在事务里混入 HTTP 调用或大字段更新。
真实延迟藏在事务内部,不在监控数字里。迁移不是比谁切得快,而是比谁确认得准。











