pt-table-sync 的三个硬默认:目标是从库直接退出、--print 不检查子表、--replicate 要求 STATEMENT
pt-table-sync 在 MySQL 表之间同步数据,只同步数据,不同步表结构、索引或其它 schema 对象。它直接改数据,用之前先备份。--execute 之前至少先跑一遍 --dry-run 和 --print。
常用命令形态:
pt-table-sync --execute h=host1,D=db,t=tbl h=host2
pt-table-sync --execute host1 host2 host3
pt-table-sync --execute --sync-to-source replica1
pt-table-sync --execute --replicate percona.checksum source1
pt-table-sync --execute --replicate percona.checksum --sync-to-source replica1
pt-table-sync --execute --sync-to-source h=source2,D=db,t=tbl
最后一种别写成这样:
# Don't do this in a source-source setup!
pt-table-sync --execute h=source1,D=db,t=tbl source2
三个必须先记住的默认值与边界
1. 目标 DSN 是从库,工具直接退出
--[no]check-replica 默认是 yes。工具会检查目标服务器是否为从库;如果是,改动直接写到目标上通常不安全,于是报错退出。要绕过只能显式 --no-check-replica,风险自负。
2. --print 不检查子表,REPLACE 的 DELETE 会级联删子表
只在指定 --replace、--replicate 或 --sync-to-source 时,工具才会检查表的子表是否带 ON DELETE CASCADE、ON UPDATE CASCADE、ON UPDATE SET NULL。命中就打印错误并跳过该表,原因是 REPLACE 实际是 DELETE 再 INSERT,那个 DELETE 会级联删掉子表行,最坏情况把子表清空。
--print 不走这个检查。所以“打印出来看着没问题”不代表能安全执行,涉及外键级联的表要单独确认。
3. --sync-to-source / --replicate 要求 statement-based 复制
这两个选项依赖二进制日志把变更投递到从库,工具会在会话内把 binlog_format 设为 STATEMENT,因此执行用户必须有 SUPER 权限。没有这个前提,后面讲的“在主库做 no-op 变更”就不成立。
三种运行形态
不指定 --replicate 时,工具自己用一种算法找出差异,这是默认形态,也是最容易失控的形态——它不知道 pt-table-checksum 已经确认过哪里不一致。
DSN 的处理逻辑:
- DSN 里带
t部分:只同步这一张表。 - 一个 DSN 加
--sync-to-source:该 DSN 是从库,连到它的主库再同步。 - 多个 DSN:第一个是源,依次把后面每个 DSN 同步过去。
- 带
--replicate:若同时带--sync-to-source,DSN 是从库;否则 DSN 是主库,工具自行发现所有从库并逐个连接。 - 只有一个 DSN 加
--sync-to-source且落在自动发现表的分支:找出所有表,逐表同步到主库。
第一个 DSN 给后面的 DSN 提供默认值,不确定解析结果时用 --explain-hosts 看一遍。
第二种形态 --replicate percona.checksum 直接消费 pt-table-checksum 的结果表,只修被标记出来有差异的地方;它会自动把 --wait 设为 60,并且把变更做在主库而不是从库上。指定 --replicate 或 --sync-to-source 时,从库不会被加锁。
第三种形态 --sync-to-source 针对单个从库。注意它只同步这一个从库,但主库上如果还有别的从库,那些变更是发到主库的,其它从库会顺着复制一起收到。要一次处理主库下的多个从库,只能 --replicate 不加 --sync-to-source。
修复方向:优先主库 no-op
安全做法是在主库上写入“把值设成它当前值”的 no-op 变更,让变更走 binlog 到从库,实际只改从库。前提是能在主库上 REPLACE 进这张表——REPLACE 只在有唯一索引时才是替换语义,没有唯一索引它等同普通 INSERT。所以表上有唯一键时,用 --sync-to-source 和/或 --replicate。
没有唯一键就没得选,只能改从库;工具会检测到这个情况并退出,除非给 --no-check-replica。
源-源拓扑下要同步的表没有主键或唯一键,变更只能写在目标端,此时必须加 --no-bin-log,否则这些改动会再复制回源端。
还有一类改不动的差异:a 上有主键、b 上有唯一键、两列的值交叉,任何 INSERT/UPDATE/DELETE 组合都会撞唯一键。工具在第一次索引冲突后会自动把语句改写成 DELETE 加 REPLACE。
算法与各自的失败面
- Chunk:按数值首列索引把范围切成
--chunk-size行一块,先校验块再校验行。分块列是字符串且所有值首字符相同时失效,工具会退出并建议换算法。 - Nibble:基于 LIMIT 的逐段扫描。
- GroupBy:整表按所有列加
COUNT(*)分组,不依赖主键或唯一索引。 - Stream:整表一个流,效率最低。
--verbose 的列与退出码 2
带 --verbose 时每张表输出一行,列依次是:
DELETE REPLACE INSERT UPDATE ALGORITHM START END EXIT DATABASE.TABLE
例如:
# 0 0 3 0 Chunk 13:00:00 13:00:17 2 test.test1
含义是这张表需要 3 条 INSERT,用了 Chunk 算法,EXIT STATUS 为 2——2 表示发现了差异并做了处理,不是执行失败。看清这一列再判断这轮到底改了什么。
修完重跑 checksum
同步结束不等于数据一致。回到 pt-table-checksum 把同一批表再校验一遍,确认差异归零;如果还有残留,说明这一轮被跳过(比如命中了子表级联检查)或者修复方向选错了,需要从 DSN 形态和唯一键这两处重新确认。
双向同步仍是实验特性:只在互相独立的服务器之间工作,不适用于复制拓扑,必须用 Chunk,不支持 N 向,也不处理 DELETE 变更,冲突靠 --conflict-column 一类选项解决。