pt-table-checksum 刚启动就退出或卡住:副本发现、复制过滤与延迟等待的排查笔记
pt-table-checksum 的定位很窄:在主库上执行 checksum 查询,让这些查询顺着复制流在副本上再执行一遍,然后比对 percona.checksums 里的结果。差异本身用 pt-table-sync 修。工具文档:
- pt-table-checksum:
- pt-table-sync:
这篇不讲切块算法,也不讲 CRC32 碰撞和 collation 造成的假阳性。只讲一个阶段:命令敲下去之后,工具在真正开始校验数据之前就退出、或者看起来不动了,该怎么定位。
退出码先记住一条
工具的退出码只有两类语义:0 表示没有发现差异、也没有警告或错误;非零表示发现了差异,或者过程中出现了警告/错误。 也就是说脚本里 if [ $? -ne 0 ] 命中时,不能直接判定"数据不一致",得看输出里是 Differences on ... 还是 Error / Warning。启动阶段挂掉的情况,绝大多数落在后者。
一、自动发现副本失败
默认的 --recursion-method 是 processlist,hosts。它先在自己连上的那台服务器上执行 SHOW PROCESSLIST,从里面找 Binlog Dump 线程——这是副本 IO 线程连过来时留下的线程。找到之后再用线程里的 host 去连副本,端口默认 3306。
几个常见的断点:
SHOW PROCESSLIST里根本没有Binlog Dump线程。可能是副本 IO 线程没起来、连的是别的实例,或者当前账号没有PROCESS权限、看不到别的用户的线程。- 有线程,但 host 是内网另一个地址 / 走了代理,
hosts方法按 3306 直连连不上。 - 级联拓扑。A → B → C 的结构里,在 A 上只能看到 B 的
Binlog Dump,C 完全不可见。想把 C 也纳入校验,必须从 B 上再发现一次,或者干脆用 DSN 把所有副本一次列全。
这时用 --recursion-method=dsn=... 显式指定,绕开自动发现:
pt-table-checksum \
--host=10.0.0.10 --port=3306 \
--user=checksum --password='***' \
--databases=app \
--recursion-method=dsn=h=10.0.0.12,P=3306,u=checksum,p='***' \
--replicate=percona.checksums
DSN 形式一次只能给一个副本。副本多了就写进一张 DSN 表,用 dsn=D=库,t=表 引用:
CREATE TABLE percona.dsns (
id int NOT NULL AUTO_INCREMENT PRIMARY KEY,
parent_id int DEFAULT NULL,
dsn varchar(255) NOT NULL
);
INSERT INTO percona.dsns (dsn) VALUES
('h=10.0.0.12,P=3306,u=checksum,p=***'),
('h=10.0.0.13,P=3306,u=checksum,p=***'),
('h=10.0.0.14,P=3306,u=checksum,p=***');
pt-table-checksum \
--host=10.0.0.10 --user=checksum --password='***' \
--databases=app \
--recursion-method=dsn=D=percona,t=dsns
--recursion-method=none 表示不找副本,只校验源端。这个值的用途是"先确认源端能不能跑通":如果 none 能跑完、dsn 跑不完,问题就锁定在副本连接或复制状态上,跟表结构和数据无关。
级联拓扑要注意方向:dsn 里列出的每一台,工具都会连上去读它的复制状态和校验结果。中间层 B 如果同时是 C 的源,B 自己也得能连上 C,否则 C 上的结果永远等不到。
二、多源复制必须给 --channel
副本上配了多源复制时,SHOW SLAVE STATUS 会返回多行,一行一个通道。工具默认不知道该看哪一行,延迟判断、复制过滤判断、结果比对全都会对错通道。用 --channel 指定:
pt-table-checksum \
--host=10.0.0.10 --user=checksum --password='***' \
--databases=app \
--channel=ch_app \
--recursion-method=dsn=h=10.0.0.12,P=3306,u=checksum,p='***'
通道名和 CHANGE MASTER TO ... FOR CHANNEL 'ch_app' 里写的完全一致,大小写敏感。如果同一台副本上有多个通道分别对应不同的源,那这台副本要按通道拆成多条 DSN 分别校验,不能指望一个 --channel 覆盖全部。
三、复制过滤会让工具直接拒绝运行
源端或副本上配了 replicate-ignore-db、replicate-wild-ignore-table 这类过滤时,工具默认会检查出来并拒绝启动。原因是它的 checksum 语句是 REPLACE ... SELECT,写入 percona.checksums;如果过滤规则恰好把 percona 库(或某张表)挡掉,这条语句在副本上根本不执行,结果行不存在。工具在源端等一个永远不会出现在副本上的结果,最后表现为超时或"副本上找不到对应行"。
--no-check-replication-filters 只是跳过这项检查,不改变过滤规则本身。跳过之后,如果 percona 库真的被过滤,问题照旧,只是从"启动被拒"变成了"跑不完"。正确的处置顺序是:
- 先把过滤规则列出来,确认
percona库和--replicate指定的表不在过滤范围内; - 确认要校验的业务库/表也不在过滤范围内——被过滤的表在副本上本来就没有数据,校验它没有意义;
- 上面两条都满足,才用
--no-check-replication-filters放行。
# 先看清楚过滤规则,再决定是否放行
mysql -e "SHOW VARIABLES LIKE 'replicate%'"
pt-table-checksum \
--host=10.0.0.10 --user=checksum --password='***' \
--databases=app \
--no-check-replication-filters \
--recursion-method=dsn=D=percona,t=dsns
--replicate 可以把校验结果表挪到别的库,但挪走并不解决问题——只要那个库被过滤,结果一样到不了副本。
四、权限清单
源端需要:SELECT、PROCESS、SUPER、REPLICATION SLAVE,以及对 percona 库的 CREATE、INSERT、UPDATE、DELETE(建结果表、写结果、清理旧行)。
GRANT SELECT, PROCESS, SUPER, REPLICATION SLAVE ON *.* TO 'checksum'@'%';
GRANT CREATE, INSERT, UPDATE, DELETE ON percona.* TO 'checksum'@'%';
副本侧需要同类权限,因为工具要连上去读 percona.checksums 做比对,还要读复制状态判断延迟:
GRANT SELECT, PROCESS, SUPER, REPLICATION SLAVE ON *.* TO 'checksum'@'%';
GRANT SELECT ON percona.* TO 'checksum'@'%';
PROCESS 缺失的典型症状是自动发现阶段"看不到副本"——SHOW PROCESSLIST 只返回自己的线程,自然找不到 Binlog Dump。这种情况不会报权限错,只会安静地认为没有副本。用 --recursion-method=dsn 能绕过发现,但如果副本上权限也不足,会在读校验结果时失败。
五、副本延迟或复制停止导致的安静等待
工具会持续监控副本。任何副本落后太多就暂停校验等它追上;任何副本报错或复制停止,也会暂停等待。这个等待没有明显的进度输出,表现为命令挂住不动。
三个参数决定等待行为:
--max-lag:副本延迟超过这个值就暂停。默认 1 秒。--check-interval:多久检查一次延迟和负载。默认 1 秒。--skip-check-slave-lag(新版本也叫--skip-check-replica-lag):对指定的副本 DSN 跳过延迟检查。
pt-table-checksum \
--host=10.0.0.10 --user=checksum --password='***' \
--databases=app \
--max-lag=5s \
--check-interval=5s \
--recursion-method=dsn=D=percona,t=dsns
取值上有取舍。--max-lag 太小会让校验频繁暂停,一张大表跑几个小时;太大则副本可能落后到校验结果已经过期。--check-interval 调大能减少对副本的探测压力,代价是延迟变化的响应变慢。--skip-check-slave-lag 要慎用:它等于对某台副本放弃"追上再校验"的保护,只适合那台副本已经明确要下线、或者只做旁路读的场景。跳过之后校验结果仍然会写进去,但那台副本上的数据可能压根没追上,比对结论不可信。
复制停掉时 Seconds_Behind_Master 会变成 NULL,工具不会把它当 0,只会继续等。所以卡住时先手动看一眼副本:
SHOW SLAVE STATUS\G
-- 关注 Slave_IO_Running / Slave_SQL_Running 是否为 Yes
-- Seconds_Behind_Master 是否为 NULL
-- Last_SQL_Error 是否有内容
六、--replicate-check-only 复用已有结果
如果源端已经跑完一轮,percona.checksums 里有结果,只是当时副本延迟太高、或者中途断过,不需要重跑整个校验。--replicate-check-only 只做比对,不执行任何 checksum 查询:
pt-table-checksum \
--host=10.0.0.10 --user=checksum --password='***' \
--replicate=percona.checksums \
--replicate-check-only \
--recursion-method=dsn=D=percona,t=dsns
前提是结果表里的 source_cnt / source_crc 来自源端,this_cnt / this_crc 来自副本,且副本确实执行过那一轮的 REPLACE ... SELECT。如果那一轮的语句被过滤掉了,副本上的行是空的,这个模式只会把"缺行"再报一遍。
想手工确认哪些表有差异,直接查结果表:
SELECT db, tbl, SUM(this_cnt) AS total_rows, COUNT(*) AS chunks
FROM percona.checksums
WHERE source_cnt <> this_cnt
OR source_crc <> this_crc
OR ISNULL(source_crc) <> ISNULL(this_crc)
GROUP BY db, tbl;
七、--explain 先看切块计划
真正跑之前,用 --explain 让工具只打印每张表打算怎么切、用哪个索引、预计多少个 chunk,不执行校验语句:
pt-table-checksum \
--host=10.0.0.10 --user=checksum --password='***' \
--databases=app \
--explain \
--recursion-method=none
这一步能提前暴露两类启动期问题:表没有可用索引、只能整表当一个 chunk;以及过滤条件写错、压根没选中要校验的表。确认计划合理,再去掉 --explain、换上真实的 --recursion-method 正式跑。
现象、原因、处置对照
| 现象 | 原因 | 处置 |
|---|---|---|
| 报找不到副本,或显示 0 个副本 | SHOW PROCESSLIST 无 Binlog Dump 线程;账号缺 PROCESS;副本不在 3306 | 补 PROCESS 权限;改用 --recursion-method=dsn=... |
| 级联拓扑里下游副本完全没被校验 | 默认发现只看当前服务器一层 | 逐层发现,或用 dsn=D=...,t=... 一次列全所有副本 |
| 多源复制副本上延迟判断、结果比对错乱 | 未指定通道,工具读了错误的 SHOW SLAVE STATUS 行 | 加 --channel=,按通道拆分 DSN |
| 启动即报复制过滤相关错误并退出 | 存在 replicate-ignore-db 等规则 | 先把 percona 库和业务库排除在过滤外;确认无影响后再 --no-check-replication-filters |
| 跳过过滤检查后跑不完,副本结果表无对应行 | REPLACE ... SELECT 被过滤,结果没到副本 | 调整过滤规则,仅跳检查不解决问题 |
报 Access denied / 无法写结果表 | 缺 SUPER、REPLICATION SLAVE,或 percona 库无写权限 | 按源端/副本权限清单补齐 |
| 命令长时间无输出,像卡死 | 副本延迟超 --max-lag,或复制已停止在等待 | 查 SHOW SLAVE STATUS;按需调 --max-lag / --check-interval,确认副本可弃用时才 --skip-check-slave-lag |
| 想复核但不想重跑整轮 | 已有结果,只是当时没比对成功 | 用 --replicate-check-only 复用 percona.checksums |
| 不确定会怎么切块 | 索引缺失或过滤条件写错 | 先 --explain 看计划 |
| 退出码非零 | 发现差异,或过程有警告/错误 | 看输出区分 Differences on 与 Error / Warning,再决定用 pt-table-sync 修还是先排错 |
启动阶段的问题基本都落在连接、发现、过滤、权限、等待这五类里。挨个排除一遍,比一开始就去怀疑数据本身要快得多。