不可靠;COUNT(*)仅能发现明显行数差异,无法识别字段值篡改、NULL与空字符串互换、时区偏移等隐性损坏,且受MySQL版本升级、引擎迁移、sql_mode等影响,需配合CHECKSUM TABLE等细粒度验证。

直接对比表行数是否可靠?
单纯比对 COUNT(*) 在多数场景下是“假安心”——它能快速发现明显缺失或重复,但无法捕获字段值被静默篡改、NULL 与空字符串互换、时区转换导致的时间偏移等隐性损坏。尤其在 MySQL 升级(如 5.7 → 8.0)或跨引擎迁移(MyISAM → InnoDB)后,sql_mode、默认字符集、时间类型处理逻辑变化都可能让数据“看起来一样,实际不同”。
建议作为第一轮粗筛,但必须配合更细粒度验证:
- 对每个表执行
SELECT COUNT(*) FROM table_name,源库和目标库分别查,结果不一致立即中止后续操作 - 排除带
WHERE条件的统计(如软删除标记),确保比对口径完全一致 - 注意大表
COUNT(*)可能锁表或耗时长,可改用SHOW TABLE STATUS中的Rows字段作估算参考(仅限 InnoDB,且非精确值)
用 CHECKSUM TABLE 验证整表一致性
CHECKSUM TABLE 是 MySQL 原生支持的轻量级校验方式,适合中小表(单表
实操要点:
- 执行
CHECKSUM TABLE table_name,两库分别运行,比对Checksum列数值 - 该命令会加读锁,生产环境避开高峰期;对大表慎用,可能引发长时间阻塞
- 不支持分区表全表校验(需逐个分区执行),也不校验表结构、索引、外键约束
- MySQL 8.0.26+ 默认禁用该功能(
skip_checksum_table=ON),需确认配置已开启
逐行比对字段值:pt-table-checksum + pt-table-sync
Percona Toolkit 的 pt-table-checksum 是生产环境最常用的方案,它将表按主键/唯一键分块,每块计算 CRC32 校验和并写入专用校验表,支持主从、跨实例、断点续传,规避了单次锁表风险。
关键配置和避坑点:
- 必须确保源库开启 binlog(
binlog_format=ROW最佳),目标库为 source 的从库或逻辑复制下游 - 运行前先用
pt-table-checksum --replicate=test.checksums --no-check-binlog-format h=source_host,u=user,P=3306测试连通性和权限 - 校验结果存于
test.checksums表,通过SELECT * FROM test.checksums WHERE this_crc != master_crc OR is_drift = 1查异常分块 - 发现差异后,用
pt-table-sync修复,但务必加--print先看 SQL,禁止直接--execute
字符集与排序规则是否真正一致?
升级或迁移后最容易被忽略的是 collation 层面的“隐形不一致”。例如源库用 utf8mb4_unicode_ci,目标库误配成 utf8mb4_0900_as_cs,会导致相同字符串比较结果不同,进而影响去重、排序、JOIN 结果,但 CHECKSUM 和 COUNT 完全无法发现。
检查方法:
- 查库级:
SELECT DEFAULT_COLLATION_NAME FROM information_schema.SCHEMATA WHERE SCHEMA_NAME = 'db_name' - 查表级:
SELECT TABLE_COLLATION FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'db_name' AND TABLE_NAME = 't1' - 查字段级:
SELECT COLUMN_NAME, COLLATION_NAME FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'db_name' AND TABLE_NAME = 't1' AND COLLATION_NAME IS NOT NULL - 特别注意
TEXT类型字段,其 collation 可能与表默认不同,且影响GROUP BY和ORDER BY行为
真正的完整性不是“数得上”,而是“用起来结果一致”。字符集、时区、SQL 模式、隐式类型转换规则这些底层设定,往往比某一行数据多或少更难排查。










