异构数据同步踩坑记,聊聊金仓KFS是怎么解决数据一致性这个老大难的

先记一件旧事。前年一个国产化迁移项目,Oracle迁金仓数据库,存量KDTS、增量KFS(Kingbase FlySync,电科金仓的异构数据同步软件),链路跑了两个多月没出过事,同步延迟长期稳定在几百毫秒。

然后某天夜里,财务说月报里一笔利息对不上,两边差小数点后两位。我们查了两个小时业务代码,一无所获。回头查数据才发现:三天前有一条变更在目标端入库时撞上锁等待,被跳过去了。差异在库里躺了三天,没人知道。

链路没断,监控没报警,但那个基本的问题答不上来:两边的库此刻到底一不一致。本文记录的就是围绕这个问题的实践,前半部分是我们手写的对账方案怎么一步步撞墙,后半部分是KFS内置的校验修复怎么把这些问题逐一解决。代码都是当时的原稿,做了简化。

一、手写对账的三个版本

1. count比对

最初级的做法,两边各数一遍行数:

-- 源端 Oracle
SELECT COUNT(*) FROM biz_order WHERE create_time < DATE '2025-06-01';

-- 目标端 KingbaseES
SELECT COUNT(*) FROM biz_order WHERE create_time < DATE '2025-06-01';

翻车很快。两边count都是8,324,117,一条不差,业务一跑就报错。排查发现有两千多条记录字段内容不一致。行数一致但内容不一致,count完全看不见。这个阶段的认知是:数数不叫校验。

2. 聚合指纹

第二版用MD5聚合值比对内容:

-- 两边各跑一遍,比对 MD5 串
SELECT MD5(STRING_AGG(
    id::text || order_no || amount::text || status, ',' 
    ORDER BY id))
FROM biz_order;

能发现内容差异了,但定位能力为零。指纹对不上只说明这张表有问题,哪一行有问题不知道。要定位就得全表拉到应用层逐行比。几千万行的表,比对脚本跑一次一个多小时,而且源端业务不停,比对期间新数据持续写入,比完的区间随时可能又变了。等于打移动靶。

3. Java分片对账

同事写了第三版,按主键分片拉到应用层比:

// 当时写的简化版对账逻辑,现在看挺糙的
public List<Long> compareRange(String table, long minId, long maxId) {
    List<Long> diffIds = new ArrayList<>();
    long step = 100_000;
    for (long start = minId; start < maxId; start += step) {
        long end = Math.min(start + step, maxId);
        // 源端取这批数据的指纹
        Map<Long, String> srcMap = jdbcTemplate.queryForMap(
            "SELECT id, MD5(concat_ws('|', id, order_no, amount, status)) " +
            "FROM " + table + " WHERE id >= ? AND id < ?",
            start, end);
        // 目标端取同一批
        Map<Long, String> dstMap = targetJdbc.queryForMap(/* 同样的SQL */);
        // 逐个比
        for (Map.Entry<Long, String> e : srcMap.entrySet()) {
            String dst = dstMap.get(e.getKey());
            if (dst == null || !dst.equals(e.getValue())) {
                diffIds.add(e.getKey());
            }
        }
    }
    return diffIds;
}

这版勉强能用了,但维护成本很高。表结构一变就得改代码。高峰期不敢跑,怕影响生产库。还有个坑折腾了很久:Oracle和KingbaseES对特殊字符的编码处理路径不同,中文数据没问题,碰到生僻字就报假差异。有一次对账跑了一夜,报了三万多条差异,两个人排查两天,结论是其中99%是编码路径造成的假差异,真差异只有十几条。假差异淹真差异,比不校验还糟。

这套工具后来还在用,但它的能力边界大家都清楚了。

二、KFS校验能力的逐项对照

后来在另一个项目上把KFS的"数据比对与修复"模块完整用了一遍。它的整体思路和上面第三版Java工具是同源的:分片、行级指纹、逐块比对。差别在于产品化程度。逐项说。

粒度方面。KFS支持库级、模式级、表级、数据级四档。模式级(文档里叫整模式校验)最常用,指定一个schema,其下所有表自动纳入,不需要逐表配置。我们当时拿一张有三百多张表的库试过,配置只花了几分钟。日常巡检库级扫一遍,账务重点表再单独加数据级校验,这个组合用下来最顺手。

分片方面。分片列推荐选选择性好的主键列,整型和时间类型都支持,片大小可配。有一点值得注意:同一行数据始终落在同一个分片内比对,两端切法一致,不存在漏行。我们手写版是写死十万条一片,遇到数据倾斜的大表性能就很差,这个坑KFS用可配分片绕开了。

MD5模式。逐行算指纹,指纹不一致的行直接记录主键。比我们那个聚合指纹强的地方在于定位到行,差异报告拿到手就知道该看哪条记录。

增量校验。这是手写方案完全无解的部分,增量数据每条都可能是差异,不可能无限循环全量比对。KFS的做法是校验流跟随同步流:每条变更写入目标端时同步生成列值哈希指纹和时间戳的组合标识,标识本身就是比对依据,全程不回查源端数据库。这一点有两个后果。其一,校验对源端的干扰极小,官方数据是对源端CPU负载的影响小于3%,正常业务波动的噪声都比这个数大,DBA不需要为校验调整容量规划。其二,校验链路与同步链路相互独立,跑校验不会拖慢同步。

操作层面,图形化路径是在同步管理平台KFSMC上建校验任务、关联调度计划;命令行路径是配置table.properties后执行cmdcompare命令,可以嵌进自动化运维脚本。两种方式覆盖同一套能力,看团队习惯选。

三、不停机校验与SCN

前文的假差异问题,根源是比对的两端取数时刻不一致。源端count是上午十点的数,目标端count是十点零三分的数,中间三分钟的增量正在路上,数字对不上是正常的同步延迟,不是数据不一致。

KFS的解法是快照点。以Oracle到金仓的组合为例,全量迁移完成时记录一个SCN号,也就是Oracle内部的日志位点。校验任务直接指定这个SCN,源端按该时间点的快照取数,目标端比对同步落地的数据,两端对的是同一时刻的账。移动靶变成静靶。

配合差异数据二次校验,流程完整了。第一轮全量比对报出的差异里可能混着同步延迟造成的假差异;二次校验只针对差异记录重跑,目标端追平校验期间的增量之后,剩下的就是真差异,才进入修复环节。整个二次校验在界面上就是差异结果处理里的一个选项,2024年1月以后的版本支持,只有主键表可用。

一个实测数据:浙江省人民医院LIS系统升级期间,检验科反馈某批次报告状态异常,运维用KFS增量校验排查,17秒定位到原因是Oracle临时表残留数据干扰。此前同类问题的平均排查时间是6.5小时。差距主要在定位粒度,报告直接给出表名、主键值、字段名,和自己拉数据逐行啃是两种工作量。

四、修复环节的CRUD

对出差异后要把账平掉。我们当年的手写版长这样:

-- 1. 缺失的记录:从源端补插
INSERT INTO kingdb.biz_order
SELECT * FROM oradb.biz_order@dblink s
WHERE s.id IN (SELECT id FROM diff_result WHERE diff_type = 'MISSING');

-- 2. 内容不一致:以源端为准更新目标端
UPDATE kingdb.biz_order t
SET (order_no, amount, status) = (
    SELECT s.order_no, s.amount, s.status
    FROM oradb.biz_order@dblink s WHERE s.id = t.id)
WHERE t.id IN (SELECT id FROM diff_result WHERE diff_type = 'MISMATCH');

-- 3. 目标端多出的记录:删除
DELETE FROM kingdb.biz_order
WHERE id IN (SELECT id FROM diff_result WHERE diff_type = 'EXTRA');

三段SQL,增、改、删各一。写起来不难,用起来全是问题:dblink跨库性能差,大事务容易锁表,WHERE条件写错就是生产事故,还必须挑窗口期执行。更实际的问题是这套流程没法全自动,半夜跑完校验,早上还得有人来执行修复,自动化只完成了一半。

KFS的修复分自动和手动两种模式。自动模式下,校验任务关联修复策略,确认差异后系统以源端为准做记录级修正,缺的INSERT、错的UPDATE、多的DELETE,即上述三段SQL的工作由系统完成,无需人介入。解放军总医院的汇聚项目就是这个模式:每天业务低谷时段自动校验当天增量、自动修复,白天临床系统照常运行。手动模式则先出差异清单,人工确认后再触发修复,或只修指定记录。核心账务类表建议走手动,机器改数之前留一道人工确认,这个谨慎有必要。

数据修复的正确形态就是CRUD,但应该由机器执行,人负责定规则、看报告。发现差异到修复完成的耗时从天级压到分钟级。

五、项目数据

几个公开项目的数据,都做过核实。

国家电网智慧计量。山东、河南两省计量实验室数据实时汇聚总部,每天700GB增量,延迟要求秒级。迁移环节由KDTS用9小时完成Oracle中间库6.8T存量搬迁,KFS基于SCN衔接全量与增量。一致性保障采用自动定时校验加实时校验,检测和修复全程无人工干预。

海南移动故障管理系统。同城双中心,按GB/T 20988-2007容灾标准最高第6级建设,日均变更380G。灾备场景对一致性的要求高于普通同步:平时不一致无人察觉,真到灾难切换时,灾备中心里是一份错账。KFS承担存量分块并行比对和增量实时校验,双中心实测同步延迟亚秒级。

解放军总医院医疗云。八个中心近400个系统,HIS、LIS、PACS、EMR、体检、ICU全部纳入,存量60TB以上,日增量500GB、一亿多条,时延小于1秒,近四百条临床核心链路全程不停机建设。校验策略是每天低谷时段自动校验修复当天增量。临床数据对错误的容忍度极低,这个机制在这里是刚需。

三个行业互不相关,做法完全一致:校验交给调度,修复交给策略,人看报告。

六、写在最后

做国产化替代选型时,同步性能很容易被摆在第一位,跑分数据也确实直观。但从我们几个项目的经验看,切换那天有没有底气,取决于能不能随时证明两边数据一致。KFS把存量校验、增量校验、差异修复做成了内置能力,校验过程不打扰生产(对源端CPU影响不到3%),这些能力的实际价值比吞吐量数字更直接。

开头那笔差小数点后两位的利息,后来是人工修的,三个人查了半宿。如果当时有每天自动校验加自动修复,那笔差异会在产生当晚被发现并修掉,轮不到业务在三天后撞上。这就是我对这套机制的完整评价。

posted @ 2026-09-02 10:23  正在走向自律  阅读(3)  评论(0)    收藏  举报