迁移后第一件事是执行COUNT(*)和COUNT(DISTINCT id)比对,可快速暴露主键NULL、过滤条件遗漏等问题;大表必须用pt-table-checksum分块校验,需确保连接主库、ROW格式binlog、有主键、从库同步正常。
迁移后第一件事不是跑哈希,而是执行 COUNT(*) 和 COUNT(DISTINCT id)。很多问题在这一步就暴露了:比如目标库因字符集不兼容把某几条记录写成 NULL 导致主键冲突被跳过,或者迁移脚本漏掉了 WHERE is_deleted = 0 条件,结果行数对得上但数据残缺。
注意三点:
COUNT(*) 必须在相同过滤条件下执行——如果源库导出时加了 WHERE created_at >= '2024-01-01',目标库也要查同一范围COUNT(DISTINCT ...) 会失效,改用业务唯一字段(如订单号)或组合字段(如 user_id, order_time)TINYINT(1) 被目标库映射为 BOOLEAN 后可能丢精度,COUNT 看不出差异CHECKSUM TABLE 是 MySQL 内置命令,适合单表行数 ≤ 500 万的场景。它计算的是整表数据页的 CRC32 值,快、轻量、不用额外工具。
但它有硬伤:
FLOAT 或 DOUBLE 字段,不同版本 MySQL 对二进制表示的处理可能不同,哪怕值一样,校验和也会变——这时必须跳过该表,改用逻辑字段拼接哈希AUTO_INCREMENT 值、外键约束,结构差异要另查 SHOW CREATE TABLE
单表超千万行时,CHECKSUM TABLE 和手写 MD5(CONCAT(...)) 都会内存溢出或锁表太久。pt-table-checksum 的核心价值是分块 + 复制流 + 事务快照,能准确定位到哪几行不一致。
启动前确认四件事:
binlog_format = ROW,否则工具无法生成可靠校验点SELECT、PROCESS、SUPER 权限,以及对 percona.checksums 表的写权限Seconds_Behind_Master 应尽量接近 0最小安全命令示例:
pt-table-checksum --host=master_ip --user=check_user --password=xxx --databases=mydb --replicate=percona.checksums --chunk-size=1000 --ignore-databases="information_schema,mysql"
全字段拼接 MD5(CONCAT(*)) 是陷阱。NULL 值、空格、时区转换、字符集隐式截断(比如 emoji 在 utf8 下变 ?)都会让哈希值不同,但实际业务可能完全不受影响。
正确做法是:
order_no、amount、status、created_at
COALESCE(field, '') 统一 NULL 处理,避免因 NULL 参与拼接导致哈希错乱ORDER BY id LIMIT 100)、中部(LIMIT 100000, 100)、尾部(ORDER BY id DESC LIMIT 100),覆盖不同数据分布CONVERT_TZ(created_at, '+00:00', '+08:00') 显式转成统一时区再比,否则源库 UTC、目标库 CST 会导致值差 8 小时真正容易被忽略的,是那些“看起来一样”的字段:DECIMAL 精度丢失、JSON 字段键顺序不同、TEXT 字段末尾空格是否保留——这些差异不会影响 COUNT 或 CHECKSUM TABLE,但会让业务逻辑出错。