数据集成产品接上以后,报表为什么还是对不上?聊聊金仓 KFS 的落地细节
选数据集成产品,通常先看数据库支持列表,再看同步速度。可如果需求是把几套业务系统的数据汇到一起,我觉得还有一件事应该更早讨论:数据到了目标库,准备怎么算?
拿订单来说,两套系统里都有订单编号、金额和状态,字段看起来也差不多。把表同步过来,写一条汇总 SQL,似乎就能得到公司总销售额。但订单编号可能重复,金额单位可能不同,甚至"已完成"在两套系统里都不是同一个意思。
这些问题不会全部表现为同步报错。有时候,任务一直正常运行,报表也能打开,数字却从第一天就算错了。
这篇文章以商城和门店两套系统的订单汇聚为例,讨论电科金仓 KFS 的使用思路。场景是为说明问题设计的,不是实际客户案例。 
一、需求里最含糊的,往往是"汇总"两个字
假设运营提出一个需求:商城和门店的订单分开管理太麻烦,希望在一个后台查看每天的销售情况。
开发很容易直接把它翻译成"接两个数据库"。接着开始申请账号、确认端口、整理表名,最后安排同步任务。
这些工作都需要做,但"销售情况"到底指什么,还没说清楚。
商城系统可能把支付成功的订单计入销售额,门店系统则按收银完成的流水统计。一笔商城订单今天支付,明天退款,昨天的销售额要不要跟着变化?门店补录上周的交易,应该归入原交易日期,还是补录当天?
如果这几个问题没有答案,后面即使每条记录都同步正确,也很难保证报表正确。
我会先拿几笔具体订单,把业务过程过一遍。最好包含正常支付、部分退款、整单取消和补录,而不是只拿两条正常数据做字段映射。
例如下面这份对照,就比一张单纯的字段类型表更有用:
| 内容 | 商城系统 | 门店系统 | 接入时需要确认的事 |
|---|---|---|---|
| 订单编号 | 本系统内唯一 | 本系统内唯一 | 汇聚后如何区分来源 |
| 金额 | 以元保存 | 可能以分保存 | 转换位置及精度 |
| 完成状态 | 可能指交易结束 | 可能指收银结束 | 是否能放进同一个统计条件 |
| 退款 | 独立退款流水 | 可能修改原记录 | 如何计算净收入 |
| 日期 | 下单、支付等多个时间 | 收款时间、营业日期 | 报表采用哪个日期 |
这只是设计示例,实际情况要以源系统定义为准。
把这些事情确认下来,看起来比直接建任务慢一点,但能少掉很多"为什么少了几笔"的来回核对。尤其是财务类报表,开发理解的"差不多",通常过不了业务验收。
二、我更愿意让两套数据先各自落地
电科金仓异构数据同步软件 Kingbase FlySync,简称 KFS,采用增量日志解析方式进行数据同步,官方产品介绍覆盖数据集中共享、分发和数据库迁移等场景,也支持多对一等同步拓扑。用于多个业务库向中心库提供数据,是它的应用方向之一。www.kingbase.com.cn
不过,多对一说的是同步关系,并没有替我们决定目标端的数据模型。
对于本文这种两套系统各有一套订单规则的情况,我会先保留两张来源接入表,在查询或加工时统一口径。这样不是为了多设计一层,而是为了以后能把问题查回去。
比如运营发现门店收入少了,可以先看门店接入表里有没有对应订单。如果明细齐全,再检查金额换算、状态过滤和关联逻辑。排查范围比较明确,不需要先从一张混合表里猜来源。
数据库名称不同,不代表必须部署两套目标实例。这里讨论的是逻辑上的数据分开保存,具体使用不同模式还是不同表名,应结合管理规范及映射能力选择。
大致关系如下:

这也是同步工具和业务加工之间需要划清的地方:哪些转换交给同步配置完成,哪些留给目标端 SQL,哪些必须由业务程序处理,都应有明确归属。
不能因为工具能够传输和映射数据,就默认它知道公司怎么定义收入。
两个编号相同的订单,不一定是重复数据
假设商城有订单 1001,金额 100 元;门店也有订单 1001,金额 80 元。
这两笔订单在各自系统里都没有问题。如果直接写进一张以订单编号为主键的表,就会发生冲突。
这时最不该急着做的,是把冲突统一处理成覆盖或忽略。两个订单本来就不是同一个业务对象,忽略任何一个都会少算。
KFS 公开管理手册有冲突处理相关机制,但选择策略之前,必须先知道冲突的原因。正常重放造成的重复,与不同来源编号碰撞,需要分别处理。help.kingbase.com.cn
下面用一组小数据演示来源识别。SQL 只验证目标端汇聚逻辑,不包含 KFS 的安装、权限和同步配置,也没有在真实同步链路中实测。
在独立测试模式下,使用尚未占用的对象名创建两张表:
sql
CREATE TABLE demo_web_order (
order_id BIGINT PRIMARY KEY,
pay_amount NUMERIC(12, 2) NOT NULL
);
CREATE TABLE demo_store_order (
order_id BIGINT PRIMARY KEY,
pay_amount NUMERIC(12, 2) NOT NULL
);
INSERT INTO demo_web_order VALUES (1001, 100.00);
INSERT INTO demo_web_order VALUES (1002, 50.00);
INSERT INTO demo_store_order VALUES (1001, 80.00);
COMMIT;
为了把问题集中在来源识别上,这里的金额已经统一为元。如果实际系统使用不同单位,需要先完成转换。
再创建一个汇聚视图:
sql
CREATE VIEW demo_order_all AS
SELECT
CAST('WEB' AS VARCHAR(10)) AS source_system,
order_id,
pay_amount
FROM demo_web_order
UNION ALL
SELECT
CAST('STORE' AS VARCHAR(10)) AS source_system,
order_id,
pay_amount
FROM demo_store_order;
查询明细:
sql
SELECT source_system, order_id, pay_amount
FROM demo_order_all
ORDER BY source_system, order_id;
预期得到三条记录:
| source_system | order_id | pay_amount |
|---|---|---|
| STORE | 1001 | 80.00 |
| WEB | 1001 | 100.00 |
| WEB | 1002 | 50.00 |
两条 1001 都保留了,而且能明确区分来源。后续如果落到统一明细表,可以考虑以来源和源订单编号共同标识记录。
这里使用 UNION ALL,并不是忽略去重,而是不能用查询结果去重代替业务去重。真实的重复订单应该根据业务标识、来源和处理规则判断。
三、表里的数据都对,合计数字也可能暂时不对
来源和字段规则确定后,还会遇到一个不太显眼的问题:两条同步链路未必走在同一个进度上。
商城已经同步到上午十点,门店因为网络波动还停在九点四十分。此时查询总收入,SQL 可以正常执行,返回结果也不一定有任何明显异常,但门店最近二十分钟的数据还没到。
如果这是运营实时看板,可以允许一定延迟,但页面应说明数据新鲜度。否则业务看到销售额突然下降,可能先去排查经营情况,最后才发现是一条链路落后了。
如果这是结算报表,就不能只靠"等一会儿再查"。需要约定什么时候生成、什么时候确认,以及迟到的数据如何补算。
这还涉及一个区别:同步已经追平,不等于业务已经录完。
门店今天补录昨天的订单,对数据库而言是今天发生的变更,对报表而言却可能属于昨天。即使同步完全及时,昨天的数字仍然会变化。是否允许追溯调整、调整后是否保留原报表版本,都属于业务规则。
因此,我不太建议用目标表最大的业务更新时间来代表同步进度。源端暂时没有写入,这个字段自然不动;时间由应用生成时,还可能受服务器时钟影响。应该结合经过验证的同步进度指标判断,再由业务规则决定报表是否完整。
另外,多个独立源库之间本来就可能没有共同的事务边界。不能因为每条同步链路都按自己的规则处理事务,就认为几套系统在目标端形成了同一时刻的全局快照。
这些细节平时不一定影响看板,但一旦拿报表对账,就会成为必须解释的问题。
四、发现差异后,别马上把数据重导一遍
假设门店后台显示今天收款 8 万元,汇总报表只有 7.6 万元。
这时候说"可能同步漏了,重跑一下",很自然,却未必有效。
我会先找几笔明确有差异的订单,而不是先比较两个总数。源端是否存在记录,目标端是否收到,金额字段是否一致,是否被报表过滤掉,沿着这条路径查,通常能更快缩小范围。
还有一种常见错误,跟同步无关:订单表关联商品明细后,直接对订单金额求和。
一笔 100 元的订单有三条商品明细,关联结果变成三行,金额也出现三次。最后统计成 300 元,所有源数据和目标数据都可能是正确的。把同步任务重跑十遍,也修不好这条 SQL。
因此,至少要分开检查两件事:
接入的数据有没有保持正确,以及报表有没有正确使用这些数据。
KFS 官方资料介绍了数据校验与修复能力,可以用来处理同步数据的一致性问题。业务汇总、关联关系和统计口径,则仍需要自己的验证。www.kingbase.com.cn
对前面的示例,可以先按来源核对数量和金额:
sql
SELECT
source_system,
COUNT(*) AS order_count,
SUM(pay_amount) AS total_amount
FROM demo_order_all
GROUP BY source_system
ORDER BY source_system;
预期商城两笔、合计 150 元,门店一笔、合计 80 元。
这类汇总适合发现问题,但不能证明每条记录都正确。两笔金额一增一减,合计可能恰好不变,所以关键范围还需要明细检查。
核对时间也得一致。源库持续写入,目标库存在延迟,却在两个不同时间点分别统计,出现差异并不一定是故障。静态测试可以停止写入后比较,生产环境则应采用适合当前版本和场景的校验方式。
真正需要补数时,先确认补哪一段、以哪一端为准,以及如何与增量衔接。尤其不要在增量持续运行时,随手把旧快照覆盖到目标端。旧数据和新变更相互交错,可能让问题变得更难追踪。
五、运行几个月后,考验才慢慢出现
首次接入时,大家会认真检查字段和样例。后续一次普通发版,反而容易打破原来的约定。
例如商城增加了"部分退款"状态,报表仍然只识别"已支付"和"已退款";门店金额字段扩大了精度,目标端没有同步调整;原来物理删除的记录改为软删除,但报表没有增加删除标记过滤。
任务可能继续运行,数字却开始偏离。
我会把同步影响检查放进数据库变更单:涉及哪些接入表,下游有哪些使用者,需不需要调整映射和报表,执行顺序如何安排。内容不用写得复杂,但不能没人负责。
KFS 公开手册包含 DDL 过滤相关配置。不过,是否允许某条 DDL 通过,与它能否在异构目标端正确执行,是两个问题,仍需按实际版本和数据库组合验证。help.kingbase.com.cn
目标接入表也不适合成为随手修数据的地方。运营想加备注,可以放到扩展表;确实需要订正源业务数据,则应走源系统的处理流程。否则人工改过目标值,后续同步又覆盖回来,很容易被误认为工具不稳定。
上线前还应该做一次中断实验。让其中一条测试链路短时中断,期间写入一组已知的新增、更新和删除,恢复以后检查结果和追赶耗时。顺便看看另一条链路是否受影响,告警能否指向具体来源,报表有没有提示数据暂时不完整。
做这个实验,得到的不只是"恢复成功"四个字,还能知道故障期间积压增长有多快,以及恢复后需要等多久。
当然,恢复依赖所需日志或中间数据仍然可用。保留策略应覆盖预计的故障处理与追赶时间,不能默认任何时长的中断都能直接续上。超出恢复条件时,可能需要重新建立数据基线。
写在最后
如果只验收"目标端能查到数据",多源汇聚项目很容易提前宣布完成。
我更在意的是,报表上的一笔金额能否查回具体来源;某条链路落后时,业务是否知道结果还不完整;历史数据被订正以后,系统能不能按约定重新计算。
KFS 可以承担数据库之间持续同步的工作,但来源标识、统计口径和差异处理规则,需要团队自己明确。把这些内容做好,后面再增加第三套、第四套系统,才不至于每接一个来源,就重新解释一遍所有数字。
对数据集成产品的评价,也应该落在这些具体工作上:它是否让数据的流转更可靠,问题更容易发现,恢复过程更容易验证。最终业务拿着报表来问时,我们应该能够顺着记录查出原因,而不只是回答一句"同步任务没有报错"。