直接比对 select count(*) 不可靠,因主从延迟、未提交事务、自增id跳变、replace/insert on duplicate等导致行数一致但内容不同,且mixed模式下now()/uuid()等函数引发隐式不一致;pt-table-checksum通过索引切片+sql层crc32/md5校验绕过上述问题。

为什么直接比对 SELECT COUNT(*) 不可靠
主从延迟、未提交事务、自增 ID 跳变、REPLACE INTO 或 INSERT ... ON DUPLICATE KEY UPDATE 导致行数一致但内容不同——这些都会让简单计数失效。更危险的是,某些 binlog 格式(如 MIXED)在从库回放时可能因函数(NOW()、UUID())产生不一致结果,而行数完全看不出问题。
pt-table-checksum 是什么,它怎么绕过这些坑
Percona Toolkit 的 pt-table-checksum 不依赖行数或时间戳,而是把表按 chunk 切片,在主库计算每个 chunk 的 CRC32 或 MD5 指纹,并把结果写入一张专用校验表(默认 percona.checksums);从库会自动读取该表并复现相同切片逻辑,独立计算指纹再比对。关键点在于:
- chunk 切分基于索引列(通常是主键),保证主从扫描顺序和范围严格一致
- 所有计算都在 SQL 层完成,不依赖客户端时间或随机值
- 支持跳过
replicate-ignore-db或--replicate配置的库/表 - 通过
--chunk-index可指定非主键索引,避免全表扫描
示例命令:
pt-table-checksum --host=master-host --user=admin --password=xxx --databases=mydb --no-check-binlog-format --replicate=percona.checksums
运行后怎么看结果,哪些 DIFFS 真该报警
执行完后查从库上的 percona.checksums 表:
SELECT db, tbl, SUM(this_cnt) AS total_rows, COUNT(*) AS chunks, SUM(diffs) AS diff_chunks FROM percona.checksums WHERE (master_cnt this_cnt OR checksum this_checksum) GROUP BY db, tbl;注意:
-
diffs > 0表示该 chunk 指纹不一致,不是“有差异就立刻修复”,要先确认是否是瞬时延迟导致(检查Seconds_Behind_Master) -
this_cnt = 0且master_cnt > 0:从库缺失整块数据,大概率是复制中断或过滤规则误删 -
checksum IS NULL:主库没写入校验值,可能是权限不足(需INSERT/UPDATE权限到percona库)或 chunk 太大超时被跳过
别忘了 pt-table-sync 只能修“已知差异”,且必须谨慎用
pt-table-sync 读取 percona.checksums 中标记为 diffs=1 的记录,生成 REPLACE 或 DELETE/INSERT 语句同步从库。但它不会自动判断哪边是源——默认以主库为准(--sync-to-master),但如果主库本身已脏,强行同步只会扩散错误。真实场景中容易踩的坑:
- 没加
--dry-run就直接执行,语句误删从库数据 - 跨版本 MySQL(如主 8.0 / 从 5.7)触发隐式类型转换,
pt-table-sync生成的语句在从库报错 - 表含
JSON字段时,CRC32(JSON_COLUMN)在不同版本行为不一致,导致假阳性差异
真正上线前,务必在从库只读模式下先跑 pt-table-sync --print ... 看生成的 SQL 是否合理,再人工抽样验证几条。
校验不是一劳永逸的事——pt-table-checksum 的 chunk 切分依赖当前索引统计信息,如果主库刚 ANALYZE TABLE 而从库没跟上,切片边界就可能错位,造成漏检。定期校验前,最好先确保主从的 innodb_stats_persistent 和统计信息状态一致。











