比对工具,我做老系统改造时唯一不省的东西
8 年里我反复总结过一句话:
老系统改造,新老两边的输出如果不能用工具自动 diff,就只能 靠人盯。靠人盯一定会漏。
漏的不是大 bug——大 bug 上线 1 小时就报警了。 漏的是那种"看起来对但不对"的——单跑任何一边都看不出来, 机器把全量数据 diff 一遍,问题才浮出来。
下面是 4 个例子,都是真实经历过的项目,做了脱敏。
例 1 · 订单类系统迁移的 dump 比对
上篇提过的"老语言迁到主流语言"的项目,跨 14 个月切完。
这 14 个月里整个迁移就靠一件事撑着:dump 比对。
老系统的事件流 dump 一份,新系统的事件流 dump 一份, 每天定时跑 diff,差异写报表。
期间发现的 11 个"看起来对但不对"的 bug——全部是 diff 报表先发现,人后看到的。
最典型的一类:同一个业务状态,老系统用字符串表达,新系统用 枚举表达,两边语义其实一样。下游消费方两套都能解析——直到 diff 报表跑出来"同一条记录两种表达",我们才意识到出了问题。
如果当时省了这套——切完后只会有一堆历史数据不一致, **到时只能选:全量重跑(代价大),或者认账(留坑)**。
例 2 · 聚合查询系统的跨结果表对账
某个聚合查询系统有 2 张结果表,逻辑上 1:1 对应。
跑了 5 年,某天我们顺手算了一下—— 有上千条核心业务记录在表 A 有,表 B 没有。
是某个增量更新的边界 bug 。 不大,数据缺失也不影响主业务流程,所以 5 年没人发现。
后来加了个简单工具——
每天 1 次,表 A vs 表 B 跑 diff,差异 > 阈值告警,告警里 带具体写入代码段的指针。
工具上线 1 周,问题暴露;3 周修完。
值得说的是:**前 5 年是"暴露不出来",不是"修不了"**。 代码本身一直在那,bug 不是隐蔽,是没人有手段看见它。
例 3 · 数据匹配的离线 vs 在线 diff
某个数据匹配场景有两条链路:
- 离线全量(每天凌晨重算所有匹配关系)
- 在线增量(数据有变更时,即时算)
逻辑上两条应该一致——但实际经常不一致。
我们的比对方式很简单:取 1% 数据,让两条链路同时跑同一份输入, diff 输出。每周大概能跑出 50-100 条不一致。
有意思的是:这些不一致里只有 30-50% 是真 bug。 剩下的是真实业务边界——比如某些场景在线已经能感知到、离线 还没扫到——这种"差异"是合理的,标"忽略"。
但你必须能看到它,才有资格忽略它。 没有这套工具的时候,我们以为两条链路一致;有了之后才发现:从来 就没一致过,只是没人在比。
例 4 · 多源数据合并时的字段分布对比
某个数据合并场景中,不同来源的数据语义不完全一致。 但字段分布(枚举值频次 / 数值范围)是一种隐含约束。
工具:合并前后跑同一个字段的分布,漂移 > 5% 就告警。
发现过一个特别隐蔽的 case——
枚举值"未知"在 A 来源约定是 0,B 来源约定是 -1 。 单看代码看不出来,0 和 -1 都是合法值,谁也没写错。 但合并后字段分布告警立刻响:**"0"占比从 30% 降到 15%**—— 回头查,才看出两个来源的"未知"约定不一样。
字段分布对比有时比代码 review 更准—— 代码 review 看的是逻辑,字段分布看的是约定。
不是 1 个工具,是 4 件配合用的事
很多人理解"比对工具"是写个脚本跑 diff—— 这是把比对工具理解小了。
我经历的几次大改造,每一次都是这 4 件事一起做:
┌─────────────────────────────────────────┐
│ 老系统流量 新系统流量 │
│ │ │ │
└───────────┼──────────────────┼───────────┘
│ │
▼ ▼
┌──────────────────┐ ┌──────────────────┐
│ ① 数据快照 dump │ │ ① 数据快照 dump │
│ (独立存储) │ │ (独立存储) │
│ 解耦 / 毫秒级 / │ │ 字段冗余比节省强 │
│ 字段冗余 │ │ │
└─────────┬────────┘ └─────────┬────────┘
│ │
└─────────┬──────────┘
▼
┌──────────────────┐
│ ② diff 报表 │
├──────────────────┤
│ ▸ 字段级:A.x≠B.x │
│ ▸ 结构级:有/无 │
│ ▸ 时间级:早/晚 N 秒│
└─────────┬────────┘
▼
┌──────────────────┐
│ ③ 告警分级 │
├──────────────────┤
│ P1 关键字段差异 │ ──→ 立即告警
│ P2 非关键 > 阈值 │ ──→ 邮件
│ P3 时间级 < 容忍 │ ──→ 入归档
└─────────┬────────┘
▼
┌──────────────────┐
│ ④ 回溯链路 │
├──────────────────┤
│ 报表保留 ≥ 30 天 │
│ 改动有版本号 / 时间戳│
│ 时间窗口反查 │
└─────────┬────────┘
▼
根因定位
"哪次改动引入的"
① 数据快照 dump
老新两边都要把关键事件 dump 到独立存储。
3 个细节:
- 跟主链路解耦(dump 故障不能影响业务)
- 时间戳精确到毫秒
- 字段冗余比节省强(多 dump 一些没坏处,少 dump 一个就废了)
② diff 报表
定时全量或采样跑 diff,差异分类:
- 字段级差异(A.x ≠ B.x)
- 结构级差异(A 有 B 没有)
- 时间级差异(A 早 B 晚 N 秒)
不分类的 diff 报表等于没看——一堆条目堆在那,人会习惯性忽略。
③ 告警分级
不是所有 diff 都告警。
- 业务关键字段差异 → P1 立即告警
- 非关键字段差异 > 阈值 → P2 邮件
- 时间级差异 < 容忍窗口 → 入归档,不告警
不分级 = 全告警 = 等于没告警——3 天后没人看了。
④ 回溯链路
发现 diff 后,要能定位具体哪个改动引入的。
要求:
- diff 报表至少保留 30 天
- 所有改动有版本号 / 时间戳
- 按时间窗口能反查
没有回溯能力的 diff,等于"看到 bug 但不知道怎么发生的"。
这 4 件事缺一件,比对工具就只是"看起来在比"。
什么时候不该做
也有反例。
某次小项目,1 个 service 重构,代码量不到 500 行。 我也想搞这套——leader 直接砍了:"投入 vs 产出比不合理。"
事后看是对的——那个 service 切完后没出过故障。
我大概的判断:
- 千行以内 / 周级别的小改造——别上,没必要
- 上万行 / 月级别以上的大改造——必上
- 中间档——看两件事:数据语义复杂吗?并跑会持续 1 个月以上吗? 任何一个"是",就上
写在最后
很多人会把"比对工具"归在测试的事—— 我倾向认为是改造方案的一部分。
测试问"这次发布有没有 bug"。 比对工具问"接下来这 N 个月,新老两边到底一不一致"。 看的时间尺度不一样。
而且时机很重要—— 比对工具要在切第一刀之前就上线。 切完一半再加,你已经分不清"这条差异是改造带来的,还是早就存 在的"。
写到这,我也不太确定这套对所有团队都成立。
我经历的项目都是"业务相对稳定 + 改造期长 + 数据要追溯", 这种场景比对工具几乎是必修。 但如果是高速迭代的创业团队,搞这套可能反而是负担——
这点我自己也还在想。
下一篇打算写 6 阶段验收 SOP—— 比对工具是其中第 4 阶段的核心,正好顺着接。
(以上 4 个例子都做了脱敏。 如果你做过老系统改造,欢迎评论区聊聊你们的比对工具长什么样, 特别想听没搭比对工具直接切,后来踩过的坑。)