Skip to content

pt-table-sync

高效同步 MySQL 表间数据,修复主从复制与多库之间的数据差异。

语法

bash
pt-table-sync [OPTIONS] DSN [DSN]

本工具会修改数据

pt-table-sync 会写入、删除表里的数据!使用前务必备份,先在测试环境用 --dry-run--print 看清它打算做什么,确认无误后再用 --execute 真正执行。源文本明确建议:始终先用 --dry-run--print 测试同步。

以下是 SYNOPSIS 中各命令示例,逐个说明其用途。

把 host1 上的 db.tbl 同步到 host2:

bash
pt-table-sync --execute h=host1,D=db,t=tbl h=host2

把 host1 上所有表同步到 host2 与 host3:

bash
pt-table-sync --execute host1 host2 host3

让 replica1 的数据与其复制源(source)保持一致:

bash
pt-table-sync --execute --sync-to-source replica1

修复 pt-table-checksum 在 source1 所有副本上发现的差异:

bash
pt-table-sync --execute --replicate percona.checksum source1

同上,但只修复 replica1:

bash
pt-table-sync --execute --replicate percona.checksum \
  --sync-to-source replica1

在源-源(source-source)复制拓扑中同步 source2(其 db.tbl 已知或疑似不正确):

bash
pt-table-sync --execute --sync-to-source h=source2,D=db,t=tbl

源-源拓扑下不要这样写

下面这条会在 source2 上直接改数据,改动经复制流向 source1 反而污染源数据,达不到目的:

bash
# Don't do this in a source-source setup!
pt-table-sync --execute h=source1,D=db,t=tbl source2

用法示例

以下命令假定已通过选项文件或本机 socket 配置好 MySQL 连接;远程主机用 DSN(如 h=主机,P=端口,u=用户,不写明文密码)指定。本工具会修改数据,所有场景都先用 --print(或 --dry-run)看清将要执行的 SQL、确认无误后,再把 --print 换成 --execute 真正执行。

场景:先干跑确认计划

第一次接手陌生表,先用 --dry-run 让工具分析并选定同步算法、打印计划后退出,确认它打算怎么分块、会不会报错,不真正比较数据:

bash
pt-table-sync --dry-run h=host1,D=db,t=tbl h=host2

场景:预览单表同步 SQL

把 host1 上的 db.tbl 同步到 host2,先打印将要执行的同步语句,人工核对后再决定执行:

bash
pt-table-sync --print h=host1,D=db,t=tbl h=host2

场景:确认无误后真正执行

已用 --print 核对过 SQL、确认改动符合预期,再用 --execute 真正落库(改动是静默的,务必先走 --print):

bash
pt-table-sync --execute h=host1,D=db,t=tbl h=host2

场景:把某台副本同步到其源

让 replica1 与它的复制源一致:工具把 replica1 当副本,自动连它的源并在源上改数据,经复制流回副本消除差异。先 --print 确认:

bash
pt-table-sync --print --sync-to-source h=replica1

场景:修复校验发现的全部副本差异

复用 pt-table-checksum 写入的结果表 percona.checksums,自动发现 source1 的所有副本并逐一修复差异(--replicate 会把改动做在源上)。先预览:

bash
pt-table-sync --print --replicate percona.checksums source1

场景:只修复其中一台副本

只修 replica1 一台,不再动其它副本:

bash
pt-table-sync --print --replicate percona.checksums --sync-to-source h=replica1

场景:只同步某个业务库

只同步 appdb 一个库,跳过其它库,缩小同步范围:

bash
pt-table-sync --print --databases appdb h=host1 h=host2

场景:只同步表里最近的数据

表很大、最近才出现不一致,用 --where 只同步最近一天的新增/变更行,省去全表比对(注意 --where--replicate 互斥,不能同时使用):

bash
pt-table-sync --print --where "ts > CURRENT_DATE - INTERVAL 1 DAY" h=host1,D=db,t=tbl h=host2

功能说明

单向与双向同步

pt-table-sync 既能单向同步、也能双向同步表数据(双向同步为实验特性,见后文)。工具不负责同步表结构、索引或其他 schema 对象。下面只讲单向同步;双向同步单独说明。

工具的运行方式由三个紧密相关的概念决定:--replicate 的用途、如何发现差异、以及如何指定主机。

是否使用 --replicate

pt-table-sync 有两种运行方式:

  • 默认不带 --replicate:工具用若干算法(见"算法")自动、高效地发现差异。
  • 指定 --replicate:复用此前运行 pt-table-checksum(用其自身的 --replicate 选项)已经找到并记录在结果表中的差异。

严格说不用 --replicate 也能工作(工具自己能找差异),但很多人习惯用 pt-table-checksum 定期校验、再用 pt-table-sync 按需修复。

指定主机:--sync-to-source 与否

无论是否用 --replicate,都要指定要同步哪些主机,有两种方式:

  • 指定 --sync-to-source:命令行上只放一个副本 DSN。工具自动发现该副本的源,通过在源上做改动、经复制流向副本来消除差异。注意:同一源上的其他副本也会收到这些改动。
  • 不指定 --sync-to-source:命令行第一个 DSN 是主机(永远只有一个源),还必须给出至少一个目标(destination)DSN(一个或多个)。源与目标必须相互独立,不能处于同一复制拓扑;若目标是副本,工具会报错退出(因为改动是直接写到目标的,直接写副本不安全)。若指定 --replicate(但不带 --sync-to-source),命令行只需一个源 DSN,工具自动发现该源的所有副本并一并同步——这是一次性同步多个(全部)副本的唯一方式(--sync-to-source 一次只能指定一个副本)。

每个命令行上的主机都以 DSN 形式给出;第一个 DSN(或 --sync-to-source 时的唯一 DSN)为其它 DSN 提供默认值,无论其它 DSN 是在命令行显式给出还是工具自动发现的。例如:

bash
pt-table-sync --execute h=host1,u=msandbox,p=msandbox h=host2

host2 会继承 host1 的 up 部分。用 --explain-hosts 可以看清工具将如何解析命令行上的 DSN。

复制安全

安全地同步源与副本是个不简单的问题。通常安全的做法是在源上改数据,让改动像其它变更一样经复制流到副本。但这要求能在源上对表做 REPLACEREPLACE 只有在表上有唯一索引时才有效(否则退化为普通 INSERT)。

  • 表有唯一键时,应当用 --sync-to-source 和/或 --replicate 把副本同步到其源,通常能正确处理。
  • 表没有唯一键时,别无选择只能在副本上改数据;工具会检测到并报错退出,除非指定 --no-check-replica
  • 在源-源(source-source)拓扑中同步无主键/唯一键的表,必须在目标服务器上改数据,因此需要指定 --no-bin-log 以免改动经复制流回源、改坏源上的数据。

源-源拓扑的通用安全做法

在源-源对上,最稳妥是用 --sync-to-source,避免改动目标服务器数据;同时需要指定 --no-check-replica,否则工具会因"你在副本上改数据"而报错。反之,若不得不在目标上改,务必 --no-bin-log

算法(ALGORITHMS)

pt-table-sync 用统一的数据同步框架,依据索引、列类型及 --algorithms 指定的偏好,为每个表自动选用最合适的算法。默认偏好顺序如下:

  • Chunk:找首列为数值(含日期时间)的索引,把该列取值范围切成约 --chunk-size 行的块;逐块对整块做校验和,若源与目标不一致再逐行比对找出差异行。网络与内存开销小;但若用字符列分块且所有值以相同字符开头,工具会退出并建议你换算法。
  • Nibble:类似 Chunk,但用固定大小的 nibble 沿索引前进(借 LIMIT 定义上下界),逐步校验每个 nibble,不一致时同样逐行比对。
  • GroupBy:按所有列分组并加 COUNT(*),比较各列;列相同再比 COUNT(*) 决定插入或删除多少行。适用于无主键/唯一索引的表。
  • Stream:一次性选出整张表逐列比较,效率最低,但在没有可用索引时仍可用。

分块比对与生成同步语句

无论哪种算法,工具都按块(chunk/nibble)比对:先对整块做校验和,块内不一致时再逐行比较,只传输主键列与校验和,命中差异行才取回整行。当发现源与目标某行不同时,会为其生成并执行 UPDATEDELETEINSERTREPLACE 语句,使目标行与源一致。

存在一类无法用 INSERT/UPDATE/DELETE 直接解决的情况:例如列 a 有主键、列 b 有唯一键,两张表的相应行交换了 ab 取值,任何 UPDATE 都会违反唯一键。此时工具在首次遇到索引冲突后会自动把语句改写为 DELETE + REPLACE,无需人工干预。

双向同步(实验性)

双向同步为实验特性

双向同步有严格限制:仅适用于把一个服务器同步到其它独立服务器;完全不兼容任何复制;要求表能用 Chunk 算法分块;只支持两台服务器之间的双向(非 N 向);并且不处理 DELETE 变更。务必先用 --print 测试再 --execute

三个易混淆的列需分清:分块列--chunk-column,仅用于切块,如 WHERE id >= 5 AND id < 10)、比较列--columns,用于逐行比对)、冲突列--conflict-column,决定谁"正确")。单向同步里冲突无悬念(直接用源行覆盖目标行);双向同步里则按冲突列与 --conflict-* 选项选出"胜者"行,用其更新另一行。

涉及三台服务器(c1 为中心伪源,r1、r2 为远端)时,需要跑两遍才能让三台完全一致:第一遍同步 c1⇄r1、再同步 c1⇄r2(带上来自 r1 的变更);第二遍以相同顺序再同步一次,让 r1 拿到 r2 的变更。工具不做 N 向,只逐对地在第一个 DSN 与后续各 DSN 间双向同步:

bash
pt-table-sync --bidirectional h=c1 h=r1 h=r2

启用 --bidirectional 后必须配合以下冲突处理选项:--conflict-column--conflict-comparison--conflict-value--conflict-threshold,可选 --conflict-error--print 打印的 SQL 会带注释标明该语句若 --execute 将在哪台主机执行。

输出

指定 --verbose 会看到表间的差异信息,每个表一行,每个服务器单独打印,例如:

# Syncing h=host1,D=test,t=test1
# DELETE REPLACE INSERT UPDATE ALGORITHM START    END      EXIT DATABASE.TABLE
#      0       0      3      0 Chunk     13:00:00 13:00:17 2    test.test1

上例表示 test.test1 在 host1 上需要 3 条 INSERT 才能同步,使用 Chunk 算法;同步于 13:00:00 开始、17 秒后结束(取自源端 NOW()),因存在差异其 EXIT STATUS 为 2。

指定 --print 会看到若同时指定 --execute 时工具实际用来同步的 SQL 语句。要看到工具用于选取块、nibble、行的 SQL,可指定一次 --print 加两次 --verbose(注意可能打印大量 SQL)。

限制与注意事项

  • 基于语句的复制:与 --sync-to-source--replicate 配合时要求 statement-based replication;必要时工具会把会话的 binlog_format 设为 STATEMENT,这要求用户有 SUPER 权限。
  • 外键级联:当表带有 ON DELETE/ON UPDATE 外键约束时,REPLACE/级联可能连带改动子表,需注意 --[no]check-child-tables
  • 主键/唯一索引:最佳场景是表有主键或唯一索引;没有时虽也能同步,但建议改用其它方式。

选项

指定 --print--execute--dry-run 中至少一个。--where--replicate 互斥。

选项说明
--algorithms类型:string;默认:Chunk,Nibble,GroupBy,Stream。比较表时使用的算法及优先级;对每个表按顺序尝试,第一个能用的算法被采用(见"算法"节)
--ask-pass连接 MySQL 时交互式询问密码
--bidirectional启用首个 DSN 与后续各 DSN 之间的双向同步(实验性);启用后会执行一系列检查,并需配合 --conflict-* 选项决定冲突如何解决
--[no]bin-log默认:yes。写入二进制日志(SET SQL_LOG_BIN=1);--no-bin-log 设为 0。在源-源(source-source)拓扑中改副本数据时必须用 --no-bin-log,否则改动会复制回源
--buffer-in-mysql让 MySQL 在内存中缓冲查询(对比查询加 SQL_BUFFER_RESULT),结果先放入临时表再返回,降低 Perl 端内存占用并尽早释放表锁;对 GroupBy/Stream 算法有用
--[no]buffer-to-client默认:yes。逐行从 MySQL 取数(mysql_use_result),省内存但可能延长服务器端行锁时间;--no-buffer-to-clientmysql_store_result 一次取回(大表可能耗尽内存)。使用 --bidirectional 时此选项被禁用
--[no]buffer-stdout默认开启 STDOUT 缓冲;使用 tee、kubectl logs 等后处理工具想看到实时进度时可关闭
--channel类型:string。连接使用复制通道(replication channels)的服务器时指定通道名;当 SHOW REPLICA STATUS 返回多行时用于确定正确的源
-A, --charset类型:string。默认字符集(utf8 时设置 binmode/mysql_enable_utf8 并执行 SET NAMES UTF8
--[no]check-child-tables默认:yes。当指定 --execute 且用了 --replace/--replicate/--sync-to-source 时,检查被同步表的子表是否含 ON DELETE/ON UPDATE CASCADEON UPDATE SET NULL 外键;若有则报错并跳过该表(REPLACE 会先 DELETE 再 INSERT,级联删除可能清空子表)
--[no]check-master默认:yes。已废弃,将在未来版本移除,改用 --[no]check-source
--[no]check-slave默认:yes。已废弃,将在未来版本移除,改用 --[no]check-replica
--[no]check-source默认:yes。配合 --sync-to-source,尝试验证检测到的源确为真正的源
--[no]check-replica默认:yes。检查目标服务器是否为副本(replica);若目标是副本则直接改数据通常不安全,默认会报错。--no-check-replica 可关闭检查,但风险自负(如 --replace 在无唯一索引时无法在源上改,不得不改副本)
--[no]check-triggers默认:yes。检查目标表是否定义了触发器(MySQL 5.0.2+ 才有效)
--chunk-column类型:string。用于分块的列
--chunk-index类型:string。使用此索引进行分块
--chunk-size类型:string;默认:1000。每块的行数或数据量;可为行数,也可加 k/M/G 后缀表示数据大小(按平均行长度换算为行数),用于 Chunk 与 Nibble 算法
-c, --columns类型:array。只比较此逗号分隔的列列表
--config类型:Array。读取逗号分隔的配置文件列表;如指定必须放在命令行第一个选项的位置
--conflict-column类型:string。双向同步中行冲突时比较此列,按 --conflict-comparison/--conflict-value/--conflict-threshold 判定哪行数据正确并成为源。仅与 --bidirectional 配合
--conflict-comparison类型:string。决定 --conflict-column 取哪一行:newest|oldest|greatest|least|equals|matches(见"双向同步"节)。仅与 --bidirectional 配合
--conflict-error类型:string;默认:warn。无法解决或出错时如何报告:warn=向 STDERR 打印警告;die=停止同步并打印警告。仅与 --bidirectional 配合
--conflict-threshold类型:string。两个 --conflict-column 差值低于此值则不算冲突(如时间戳差小于阈值);未达阈值时由 --conflict-error 报告。仅与 --bidirectional 配合
--conflict-value类型:string。供 equals/matches 两种 --conflict-comparison 使用的值。仅与 --bidirectional 配合
-d, --databases类型:hash。只同步此逗号分隔的数据库列表(暂不能在两库间同步表)
-F, --defaults-file类型:string。只从给定文件读取 mysql 选项,必须给绝对路径
--dry-run分析、决定同步算法、打印并退出。隐含 --verbose;输出格式与实际运行一致,但受影响行数为 0(仅表示未比较数据,不代表无改动)
-e, --engines类型:hash。只同步此逗号分隔的存储引擎列表
--execute执行语句使表数据一致。真正同步数据会改动表!除非同时指定 --verbose,改动是静默进行的;若想先确认,用 --print--dry-run
--explain-hosts打印连接信息并退出;展示 pt-table-sync 将如何解析命令行上的 DSN
--float-precision类型:int。FLOAT/DOUBLE 转字符串时保留的小数位数(用 MySQL ROUND()),避免不同版本/硬件浮点表示差异导致 checksum 不一致;默认不四舍五入(用 CONCAT()
--[no]foreign-key-checks默认:yes。开启外键检查(SET FOREIGN_KEY_CHECKS=1);--no-foreign-key-checks 设为 0
--function类型:string。校验和所用的哈希函数;默认 CRC32,也可用 MD5、SHA1,若装了 FNV_64/MURMUR_HASH 用户函数则会优先使用(见 pt-table-checksum
--help显示帮助并退出
--[no]hex-blob默认:yes。对 BLOB、TEXT、BINARY 列用 HEX() 包裹,避免生成非法 SQL;一般不应关闭
-h, --host类型:string。要连接的主机
--ignore-columns类型:Hash。比较时忽略此逗号分隔的列;但若某行被判为不同,该行所有列仍会被同步(目前只能从比较中排除,不能从同步中排除)
--ignore-databases类型:Hash。忽略此逗号分隔的数据库(information_schemaperformance_schema 等系统库默认忽略)
--ignore-engines类型:Hash;默认:FEDERATED,MRG_MyISAM。忽略此逗号分隔的存储引擎
--ignore-tables类型:Hash。忽略此逗号分隔的表(可用库名限定)
--ignore-tables-regex类型:string;group:Filter。忽略表名匹配该 Perl 正则的表
--[no]index-hint默认:yes。给分块与取行查询加 FORCE/USE INDEX 提示强制用所选索引;--no-index-hint 让 MySQL 自选(仅影响工具内部的取数查询,不影响 --print 打印的语句)
--lock类型:int。锁表级别(LOCK TABLES):0=不锁,1=每个同步周期锁一次,2=每张表锁一次,3=每服务器全局锁(FLUSH TABLES WITH READ LOCK)。指定 --transaction 时不使用 LOCK TABLES 而改用事务(除 --lock 3 外)。使用 --replicate/--sync-to-source 时副本不会被锁
--lock-and-rename锁住源表与目标表,同步后交换表名;可作低阻塞的 ALTER TABLE 替代,要求恰好两个 DSN 且在同一服务器,不做复制等待
-s, --mysql_ssl类型:int。创建 SSL MySQL 连接
-p, --password类型:string。连接密码(含逗号需转义)
--pid类型:string。创建指定的 PID 文件;冲突规则与自动清理见官方文档
-P, --port类型:int。连接端口
--print打印将用于解决差异的 SQL 语句。语句是合法 SQL,可手动执行;不信任工具或想先看它做什么时很有用
--recursion-method类型:array;默认:processlist,hosts。查找副本的递归方法:processlistSHOW PROCESSLIST)、hostsSHOW REPLICAS,MySQL 8.1 前为 SHOW SLAVE HOSTS)、dsn=DSN(从表读取)、none(不找副本)。非标准端口时 hosts 成为默认
--replace把所有 INSERT/UPDATE 写成 REPLACE;遇到唯一索引冲突时会自动开启
--replicate类型:string。同步在此表中被标记为不同的表(该表即 pt-table-checksum 的同名结果表,记录源与副本间差异的表与值范围)。自动把 --wait 设为 60 并在源而非副本上改数据;配合 --sync-to-source 时把给定的服务器当副本连其源同步,否则用 SHOW PROCESSLIST/SHOW REPLICAS 找副本再同步
--replica-user类型:string。连接副本所用的用户(需在所有副本上存在,权限可更低)
--replica-password类型:string。连接副本所用的密码(与 --replica-user 配合,所有副本上须一致)
--[no]slave-password类型:string。已废弃,改用 --replica-password
--[no]slave-user类型:string。已废弃,改用 --replica-user
--set-vars类型:Array。以逗号分隔的 变量=值 列表设置 MySQL 变量;默认 wait_timeout=10000;无法设置时打印警告并继续
-S, --socket类型:string。连接使用的 socket 文件
--sync-to-master已废弃,改用 --sync-to-source
--sync-to-source把 DSN 当作副本并同步到其源。检查 SHOW REPLICA STATUS 并连接其源,在源上做改动;默认把 --wait 设为 60、--lock 设为 1,并默认关闭 --[no]transaction
-t, --tables类型:hash。只同步此逗号分隔的表(可用库名限定)
--timeout-ok即使 --wait 超时也继续。风险:若想获得两服务器一致对比,超时后仍继续可能得到不一致结果
--[no]transaction用事务代替 LOCK TABLES;粒度由 --lock 控制。默认开启但 --lock 默认关闭故无效果;多数开启锁的选项会默认关闭事务,要事务锁需显式指定 --transaction。不显式指定时按表决定(InnoDB 用事务,其他用表锁)。开启时隔离级别设为 REPEATABLE READ 并以 WITH CONSISTENT SNAPSHOT 启动事务
--trim在 BIT_XOR 与 ACCUM 模式下对 VARCHAR 列用 TRIM(),便于比较 MySQL 4.1 与 >=5.0(后者保留尾随空格)
--[no]unique-checks默认:yes。开启唯一键检查(SET UNIQUE_CHECKS=1);--no-unique-checks 设为 0
-u, --user类型:string。登录用户(若非当前用户)
-v, --verbose累积型。打印同步操作结果(见"输出"节)
--version显示版本并退出
--[no]version-check默认:yes。检查 Percona Toolkit、MySQL 等软件的最新版本与已知问题版本(详见版本检查
-w, --wait类型:time。比较表前让源等待副本追上(秒);默认把 --lock 设为 1、--[no]transaction 设为 0。超时见 SOURCE_POS_WAIT returned -1 需调大;--wait 0 关闭等待(仅锁等待保留)
--where类型:string。用 WHERE 子句只同步表的一部分(与 --replicate 互斥)
--[no]zero-chunk默认:yes。为取值为零或零等价(如负溢出存为 0)的行单独加一个块,避免首块过大;仅当指定 --chunk-size 时生效

DSN 选项

DSN 部分说明
Acharset默认字符集
Ddatabase待同步表所在的数据库
Fmysql_read_default_file只从给定文件读取默认选项
hhost要连接的主机
ppassword连接密码(含逗号需转义)
Pport连接端口
Smysql_socket连接使用的 socket 文件
t(无前缀)待同步的表(copy: yes)
uuser登录用户(若非当前用户)
smysql_ssl创建 SSL 连接

其他信息

  • 作者:Baron Schwartz

  • 通用说明:已知问题的反馈方式、PTDEBUG 调试安全提示、系统要求基线,见 通用说明

更多细节请阅读 官方文档

Percona Toolkit 中文文档 · 社区维护的第三方学习站