• 请不要在回答技术问题时复制粘贴 AI 生成的内容
Mannnnning
V2EX  ›  程序员

比对工具:渐进式改造跑得通的隐形基础设施

  •  1
     
  •   Mannnnning · 5h 14m ago · 138 views

    比对工具,我做老系统改造时唯一不省的东西

    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 个例子都做了脱敏。 如果你做过老系统改造,欢迎评论区聊聊你们的比对工具长什么样, 特别想听没搭比对工具直接切,后来踩过的坑。)


    No Comments Yet
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   1396 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 30ms · UTC 17:07 · PVG 01:07 · LAX 10:07 · JFK 13:07
    ♥ Do have faith in what you're doing.