CHECKSUM TABLE不准是因为其校验逻辑受MySQL版本、排序规则、NULL处理、行序不确定性及TEXT/BLOB截断等影响,本质不可靠;应改用显式字段+IFNULL+ORDER BY的CRC32行级校验。

MySQL主从或迁移后,CHECKSUM TABLE 为什么不准?
CHECKSUM TABLE 默认只计算行数据的校验和,不包含隐式排序、NULL 处理、字符集 padding 等细节。在不同 MySQL 版本(如 5.7 vs 8.0)、不同排序规则(utf8mb4_0900_as_cs vs utf8mb4_general_ci)或启用了 SQL_MODE 差异时,结果可能不一致——不是数据错了,是校验逻辑本身不可靠。
- 它对
TEXT/BLOB字段只取前 1024 字节参与计算 - 表有自增主键但未显式排序时,行顺序不确定 → 校验和必然漂移
- 使用
CHECKSUM TABLE t EXTENDED也解决不了顺序和 NULL 行为问题
用 SELECT CRC32(CONCAT(...)) 手动构造可比校验值
核心思路:把每行转成确定格式的字符串再哈希,强制统一 NULL 表示、字段顺序、编码和截断行为。
- 必须显式写出所有字段名,避免
SELECT *导致列序/新增字段干扰 - 用
IFNULL(col, 'NULL')统一 NULL 显式表示,不能依赖隐式转换 -
CRC32比MD5快,且足够用于一致性初筛;真要防碰撞,迁完再抽样SELECT ... FOR UPDATE对比几条 - 示例(假设表
users(id,name,email)):SELECT CRC32(CONCAT(IFNULL(id,'NULL'),':',IFNULL(name,'NULL'),':',IFNULL(email,'NULL'))) AS row_crc FROM users ORDER BY id;
对比两库结果时,别直接 diff 文本文件
文本导出含换行、空格、时区时间格式等噪声,diff 容易误报。应让数据库自己比,或用确定性中间格式。
- 用
mysqldump --skip-extended-insert --order-by-primary导出,确保每行一条 INSERT 且顺序固定 - 更稳的方式:在源库执行校验查询写入临时表,再用
SELECT COUNT(*) FROM src_crc LEFT JOIN dst_crc USING(row_id) WHERE dst_crc.row_crc IS NULL查漏行 - 如果迁移跨版本(如 5.7→8.0),注意
TIMESTAMP字段默认值行为变化,建议导出前统一转成UTC并显式 cast
大表做行级比对,怎么避免锁表和 OOM?
全表扫 + CONCAT 会吃光内存,尤其 TEXT 字段多时;加 ORDER BY 还可能触发 filesort。得拆分、限流、跳过非关键字段。
- 按主键分段查:
WHERE id BETWEEN ? AND ?,每次处理 1w 行,配合SQL_NO_CACHE - 跳过不参与业务逻辑的字段(如日志型
json_log),只校验主键 + 业务字段 + 更新时间戳 - 在从库或备库上跑,避免拖慢线上;确认
innodb_buffer_pool_size足够,否则磁盘 IO 成瓶颈 - 别信「只要 checksum 一样就一定一致」——checksum 相同只能说明「这批行的拼接字符串一致」,不代表业务语义没变(比如金额字段被错误乘了 100)


















