校验 checksum 不可靠,除非严格控制所有写入路径和时间窗口;需排除易变字段、统一 NULL 处理、防范 GROUP_CONCAT 截断与字符集乱码、避免 row_to_json 字段顺序不稳,并将 checksum 与业务数据同表同事务存储。

插入后立刻校验 checksum 是否可靠
不可靠,除非你控制住所有写入路径和时间窗口。数据库写入不是原子性的“插入+校验”操作,INSERT 提交后、CHECKSUM 计算前,可能已有其他事务修改了刚插的行(比如触发器、级联更新、应用层异步任务)。更常见的是:你用 SUM(CRC32(...)) 算整表校验和,但表里有自增 ID、时间戳、JSON 字段(顺序不固定)、NULL 值隐式转换——这些都会让 checksum 失效。
- 只对明确不变的字段做 checksum,比如业务主键 + 业务状态 + 金额,排除
created_at、updated_at、id - MySQL 中
CRC32()对 NULL 返回 0,而MD5()返回NULL,行为不一致,统一用COALESCE(col, '')预处理 - 如果走应用层计算,务必在同一个数据库事务里查出刚插入的完整行再算,不能依赖
LAST_INSERT_ID()后另起查询
MySQL 中用 GROUP_CONCAT 拼接字段再算 checksum 的陷阱
GROUP_CONCAT 默认长度上限是 1024 字符,超长会被截断,checksum 就完全失真。这不是小概率事件——比如拼接多个文本字段或 JSON 内容时极易触发。
- 执行前先设会话级限制:
SET SESSION group_concat_max_len = 1000000 - 拼接时用分隔符防歧义,比如
CONCAT_WS('|', col1, col2, col3),别直接CONCAT(col1, col2)(两个'ab'和'c'拼成'abc',跟'a'和'bc'结果一样) - 注意字符集:如果字段是
utf8mb4,但连接字符集是latin1,GROUP_CONCAT可能乱码,checksum 必然错
PostgreSQL 中用 md5(row_to_json(...)) 校验整行是否安全
不安全。row_to_json() 输出的 JSON 字段顺序不保证稳定(尤其含 hstore 或 jsonb),不同 PostgreSQL 版本、甚至同一版本不同执行计划下,字段顺序可能变化,导致 md5 值不一致。
- 改用
md5(ROW(col1, col2, col3)::TEXT),绕过 JSON 序列化,直接转元组字符串 - 但要注意:
TEXT转换对 NULL 的表现是(1,,3)这种双逗号,而row_to_json()是{"col1":1,"col2":null,"col3":3},二者无法混用 - 如果字段含浮点数,
float4/float8的文本表示可能有精度差异(如0.1::float4转 text 可能是0.10000000149011612),建议提前ROUND(x, 6)
应用层校验时 checksum 值存在哪最合适
存在数据库里最省事,但必须和业务数据同表、同事务更新。存 Redis 或本地缓存看似快,反而引入一致性风险:缓存写失败、TTL 过期、主从延迟都会导致校验失效。
- 加一个
data_checksum字段到原表,INSERT 时用MD5(CONCAT(...))直接算好写入,不依赖应用层 - UPDATE 时也同步重算该字段,用触发器或应用逻辑确保它永远和当前行数据严格对应
- 避免把 checksum 存成独立校验表——多一次 JOIN 或查询,就多一个不一致的时间窗口
真正麻烦的从来不是怎么算 checksum,而是你怎么确保参与计算的字段集合,在插入、更新、导出、跨库同步时,始终被同一套规则覆盖。漏掉一个字段、错用一个类型转换,校验就形同虚设。










