应显式指定业务字段并统一处理NULL、过滤自增列、分块流式比对,避免直接用CHECKSUM_AGG(BINARY_CHECKSUM(*));大表校验优先用gt-checksum等工程化工具。

SQL Server里用CHECKSUM_AGG和BINARY_CHECKSUM做全表比对,但别直接套用
直接写 SELECT CHECKSUM_AGG(BINARY_CHECKSUM(*)) FROM t1 看起来简洁,实际在生产环境极容易误判。原因有三:字段顺序稍一变动(比如 ALTER TABLE 调换两列位置),结果就不同;NULL 值处理不一致(BINARY_CHECKSUM(NULL) 返回 0,但多 NULL 行叠加后无法区分);带计算列、默认值、时间戳(TIMESTAMP 或 ROWVERSION)的表,每次查出来的校验值天然不同——这些字段根本不是业务数据,却参与了哈希计算。
实操建议:
- 显式列出所有需比对的业务字段,避开
*,例如CHECKSUM_AGG(BINARY_CHECKSUM(id, name, amount, created_at)) - 对可能为 NULL 的字段统一用
ISNULL(col, '')或COALESCE(col, '')包裹,保证 NULL 处理行为一致 - 过滤掉
ROWVERSION、GETDATE()类自动生成列,它们在双写或迁移中必然不一致 - 若表有
TEXT/NTEXT/IMAGE等大对象类型,BINARY_CHECKSUM会报错,必须先 CAST 成VARCHAR(MAX)或跳过
MySQL 的 CHECKSUM TABLE 只能用于小表校验,千万级表上等于自找麻烦
CHECKSUM TABLE t1 在 MySQL 里看似方便,但它会对整张表加读锁(S lock),执行期间所有写操作被阻塞。一张 2000 万行的 InnoDB 表跑一次,往往要 3–5 分钟,期间业务 UPDATE/INSERT 全部排队等待,超时告警频发。更糟的是,它只校验数据页内容,不关心字符集、排序规则(collation)、时间精度(DATETIME(3) vs DATETIME(6))等语义差异——两个逻辑等价的表,CHECKSUM 值也可能不同。
真实可用的做法是分块流式比对:
- 用
SELECT MIN(id), MAX(id) FROM t1获取主键范围,再按每 1 万行切片:WHERE id BETWEEN ? AND ? - 旧库和新库分别执行
SELECT MD5(CONCAT_WS('|', id, name, amount, IFNULL(created_at, ''))),确保字段顺序、NULL 处理、分隔符完全一致 - 走 JDBC 直连比对,避免导出 CSV 再比对——磁盘 IO 和临时文件压力太大,且无法实时中断
- 校验期间禁止在源表执行
ALTER TABLE,否则 InnoDB 的ROW_FORMAT可能变化,导致 CHECKSUM 值突变,和数据本身无关
gt-checksum 工具里 checkObject=data 模式才是大表校验的合理起点
自己拼 SQL 分块比对可行,但容易漏掉边界 case(如主键空洞、负数 ID、复合主键)。gt-checksum 的 checkObject=data 模式已经封装了这些细节:自动识别主键/唯一键、智能分块、支持跨库映射(比如 db_old.user:db_new.t_user)、并发可控、内存防 OOM。它底层也是逐块算 CRC32,但做了工程化加固。
关键配置项要调准:
-
chunkSize=10000:太小导致 RPC 开销占比高,太大易触发内存抖动 -
parallelThds=8:根据目标库 CPU 核数设,别盲目拉满,否则拖慢线上查询 -
memoryLimit=2048:单位 MB,必须设,否则大表校验可能被系统 OOM Killer 杀掉 - 务必确认
genRollSQL=OFF(除非真需要修复),避免生成冗余回滚语句影响性能
别把 CHECKSUM 当成“相等”判定的金标准,它只是快速筛出差异的信号
CHECKSUM 类方法本质是概率性比对:哈希碰撞虽低,但非零。两个不同数据集算出相同 CHECKSUM_AGG 值虽罕见,但在百亿级数据量下已不可忽略。更重要的是,它完全不反映差异在哪一行——你只知道“不一致”,但不知道哪几行、为什么不对。
所以生产流程里它只能当第一道关卡:
- 先用 CHECKSUM 或 CRC32 快速扫一遍,发现块级不一致
- 再针对不一致的块,用
SELECT * ... ORDER BY pk拉出明细,逐行比对或用EXCEPT/MINUS找差集 - 最终修复前,一定要人工核对几条典型差异行,确认是数据问题,还是字符集转换、时区、精度截断等“假差异”
最常被忽略的一点:时间字段的精度和时区。同一秒插入的数据,在源库存 DATETIME(6),目标库存 DATETIME(3),或者一个用 UTC、一个用本地时区,CHECKSUM 必然不同,但业务上可能完全接受——这种差异不该进修复流水线。










