文章目录
- 一、先掰扯一下"一致"这两个字
- 二、不停机校验,这活儿是真的难
- 三、冲突这块我态度比较保守
- 四、断了以后能不能自己爬起来
- 五、跑批来了才见真章
- 六、无主键表,这次收获最大的一块
- 七、存量搬迁里那些脏东西
- 八、那些听起来不可能的网络
- 九、翻案例数字的时候我在想什么
- 十、还差点意思的地方
- 十一、最后
上篇装完跑通之后我本来打算收工,结果第二天早上九点不到被打电话叫过去了。甲方那边拿了个自己写的脚本跑了张 count(*),源端 1289304,目标端 1289304,特别高兴,说这就可以验收了。我当时没敢接话,因为我知道这张表头天晚上跑过一次批量 update,改的是结算金额。
行数一毛钱没变。
那次之后我花了差不多两个礼拜在这套数据集成软件的一致性、容错、跑批、迁移这几块上,越挖越觉得上篇写的那些其实都是皮毛。这篇文章就讲这些真正费劲的地方。写得比较乱,想到哪写到哪,有些地方是我事后补的,时间线可能对不上。
一、先掰扯一下"一致"这两个字
这个词的麻烦之处在于,所有人都觉得自己知道它是什么意思,但每个人指的都不是一回事。我在项目上撞见过三种互相打架的说法。
最省事的一种叫行数一致。源端多少行,目标端多少行,对上就完事。自研脚本基本都这么干,包括开头那个甲方的脚本。它的漏洞不用我多说,一条 update 改掉金额,行数纹丝不动。严格讲这不叫校验,这叫自我安慰。
第二种是内容一致,逐行逐字段比。这个才叫真校验,代价也真大——全表扫一遍,每个字段算摘要,大表上跑一次几个小时起步,生产库那点余量根本不够你折腾。
第三种是事务一致。这个跟前两个压根不在一个维度上。它不管你某个瞬间两端长得像不像,它管的是"源端发生过的事务,目标端有没有按原顺序、一条不落地重放一遍"。它是过程保证,不是结果验证。
做数仓的那帮人认第二种,理由很实在,结果错了报表就错了,报表错了要背锅。做容灾的认第三种,他们的逻辑是:只要重放逻辑没错,任意时刻的差异都只是"还没追上",那不叫错,那叫延迟。两边谁也说服不了谁,我见过会上吵这个吵了半小时。
最坑的是厂商说"100% 一致"的时候,你压根不知道他说的是哪一种。
我自己最后是三层都上:实时链路靠事务一致兜底(顺序检查、I/O 顺序检查、日志连续性检查这三样),周期性再跑内容比对做验证,行数统计只留着做快速体检,比如每天早上扫一眼。代价肯定不小,跑一次全量比对要占资源,但至少不会在哪个维度上瞎掉。你要是只想选一样,我的建议是别选行数。
还有句话我憋了挺久。材料里那些"数据 100% 完整性与一致性"“错误率控制在 0.00001%”,我个人的看法是这属于营销话术,不是工程承诺。倒不是说它做不到,是这种数字脱离限定条件就等于没说——多大的事务?多烂的网络?目标端有没有约束?全都不提,你拿去跟老板汇报的时候心里得有数。我自己判断一个同步工具好不好,只看两件事:它脏数据会不会把链路堵死,以及它的校验能不能在不影响业务的前提下跑起来。这两条比任何百分比都实在。
二、不停机校验,这活儿是真的难
讲个真实场景,某大医院的数据汇聚。存量 5T 多,每天新增 300G 往上,源端 Oracle,目标端是 KES 加一套数仓。他们一开始的想法很常规:找业务低峰期,凌晨,定时跑校验。
第一个问题:医院没有低峰期。凌晨两点急诊照样在写,你半夜三点去扫表,一样在动。
第二个问题更要命,而且是所有在线校验都躲不掉的:你这边扫前半张表的时候,后半张表已经被改了。扫完拼出来的结果,前后压根不是同一时刻的数据,比对出来全是假差异。这不是哪家产品的毛病,是在线校验的天性。
它的解法我第一次看到的时候愣了一下——用快照。同步程序把源端事务解析出来并编号,让编号跟数据库快照建立映射,目标端装载到对应编号的事务时暂停装载,抓一份快照。这样两端的快照就落在同一个事务点上,可以比了。
说白了就是把"动态的比对"偷换成"同一时刻的静态切片比对"。绕开了数据一直在变这个死结。挺巧的思路,我觉得比堆参数有意思多了。
那家医院最后的数据是校验速率 98MB/s,准确性 100%(这是他们自己的口径,我照抄),业务机 CPU 负载涨了不到 3%,内存不到 4G。这个侵入度我认,因为 3% 的 CPU 在业务侧基本感知不到。
(插一句,那天我在现场,机房冷得要命,穿了件外套还是冻得手抖,敲命令敲错了三次。)
另一个案例正好反过来。某金融机构的融资服务平台,存量 4T 多,日增才 50G。他们的痛点是历史数据太多、增量太小——想验证增量数据的一致性,只能全表校验,跑一次六个小时以上,IO 还占得死死的。
解法是增量校验:从 KUFL 里捞指定时间段内变化的数据,塞进内存哈希表,再按键值去目标端把对应记录捞出来比。改完之后从 6 小时以上干到 10 分钟以内,CPU 增加不到 3%,内存不到 2G。
这两个案例放一起特别有意思。一个卡在"不能停",一个卡在"不能全比",困境完全不同,靠的是两套机制——快照解决时间对齐,KUFL 增量解决比对范围。我认为这才是这类工具的技术含量,比跑分有意义得多。当然也有人会说这不就是常规的 CDC 玩法吗,我不太同意,常规 CDC 只管搬不管对账,对账这块很多工具是让用户自己写 SQL 的。
不给图形界面权限的环境可以用命令行:
# 快照模式的存量校验/opt/kfs/bin/cmdcompare--modesnapshot--source_dbORCL--target_dbAPPDB# 只比几张表,缩小范围,我一般这么干/opt/kfs/bin/cmdcompare--modesnapshot\--source_dbORCL--target_dbAPPDB\--tablesAPP1.ORDERS,APP1.CUSTOMER# 增量校验,指定时间段/opt/kfs/bin/cmdcompare--modeincremental\--source_dbORCL--target_dbAPPDB\--start'2026-08-11 02:00:00'--end'2026-08-11 04:00:00'我自己的几条土经验:
大表别上来就全量详细比对,先用抽样加摘要(MD5)缩范围,锁定了可疑区间再细比,省时间也省 IO。校验任务跟同步链路是独立跑的,这点挺重要,意味着校验不抢同步的带宽。差异出来之后可以自动修复也可以人工确认,账务类系统我一律人工过一遍,不交给自动修复,理由后面说。
对了,分区表有个坑,我只踩过一次就记住了:默认只比分区主表,要比子表得手动勾上。我漏了一次,某些子表的数据差异差不多俩礼拜没人发现,最后是业务那边反馈报表不对才倒查出来的,挺丢人。
三、冲突这块我态度比较保守
单向同步基本没冲突的概念,双向和汇聚才有,绕不过去。
做法是这样:源端解析的时候可以选择拿列的"前项"——insert 取主键列,update 和 delete 取变更之前的值。有了前项,目标端应用的时候才能判断"我要改的这行,现在还是不是我以为的样子"。没这个就没法判断冲突,就这么简单。
# 源端:允许获取列前项 property=replicator.extractor.dbms.getBeforeCols=true property=replicator.extractor.dbms.beforeColsFile=${replicator.home.dir}/support/filters-config/beforeColsRule.json # 目标端:开启冲突处理 property=replicator.applier.dbms.conflictResolve=true property=replicator.applier.dbms.beforeColsFile=${replicator.home.dir}/support/filters-config/conflictResolveRule.json这里有个硬约束,我第一次配的时候没看见,冲突处理死活不生效,查了大半天日志才发现:开冲突处理必须先开错误处理,不然不生效。这种前置依赖写在文档角落里,挺烦人。
策略上分两档。简单档就两条路:碰到冲突中止同步等人来处理,或者忽略继续走。专业档可以按值大小、最后修改时间、作用范围来判断,处理方式有自动忽略、执行 UPDATE、记录日志几种。
我的态度很明确:账务类业务一律用最保守那档,中止加告警。自动冲突处理听着高级,但两个中心同时改了同一笔账,你不管用什么规则自动裁决,都可能做出一个业务上错误的决定。机器能判断谁的时间戳更新,判断不了哪次修改是业务上有效的。这个判断权得留给业务方,出了事也得是他们签字。
我知道有人会说我这是推卸责任,把问题丢给业务。也许是吧。但同步工具做仲裁这件事本身就有点越界,它拿到的信息只有数据,没有上下文。
四、断了以后能不能自己爬起来
同步系统会遇到的故障,掰着手指头数就四类:服务器挂、数据库挂、同步程序挂、网络断。它对这四类都有说法,我按自己的理解重排一下,顺便写写我测出来的东西。
网络和数据库故障,靠重连加断点续传。断点续传的意思是从故障那个 seqno 接着走,不用从头重来。这个能力看着天经地义,其实市面上不少工具做不到,一断就得重做全量,你想象一下 12T 的库重做全量是什么心情。我上篇那个拔网线测试,二十分钟,一条没丢,这个结果是实的,我盯着 kufl 序号数过。
同步程序自己挂了,靠守护进程拉起来,不用人管。这块没什么好说的,属于基础操作。
服务器挂了才是真麻烦,程序跟着机器一起没了。这时候要靠多实例热备,主同步节点故障、备节点接管。它是两个组件配合的:一个 HA 监控组件,一个同步实例本身。部署形态有一对一(一个 HA 实例盯一个同步实例,合并部署),也有一对二(盯一整条链路,分离部署,源端目标端一起切)。切换时间由监听检测超时决定,我实测是秒级,一般 10 秒内完成。
两个坑我记下来了。一是 2 节点高可用得有第三个可 ping 通的 IP 做虚拟 IP,这个条件经常被忽略,部署的时候才发现网络那边没给。二是检测超时别设太低,太敏感会导致实例来回切,反而更糟。这个道理跟数据库集群防脑裂是一回事,太激进的探活本身就是故障源,这话我觉得值得加粗。
# 看当前有哪些服务fsrepctl services# 看 HA 状态(参数名以你手上版本的手册为准,我这个是老版本的)fsrepctl-servicekes-source-app1 status-nameha# 手动切一次,演练用fsrepctl-servicekes-source-app1 ha-switch它支持跨网段检测,也支持双轨并行下的一键切换。我只强调一件事:演练的时候一定要真切一次,别看文档说支持就信了。我见过太多"支持高可用"但从来没演练过的系统,真出事的时候才发现切换脚本是错的,那种场面的尴尬程度你想象一下。
五、跑批来了才见真章
日常同步延迟低不叫本事,跑批来了还能扛住才叫本事。
跑批就两个特点:事务巨大、时间集中。我手上那个金融项目,每天早上六点到七点这一个小时要吃掉 350G 以上的数据变更,客户给的要求是日间(9 点到 18 点)延迟小于一分钟,日终其他时段小于一小时。翻译过来就是七点跑批结束,八点前必须追平,否则开门营业的数据就是错的。
它应对的办法是几招叠在一起。
大事务分片,把一个含几百万条变更的巨型事务化整为零,切成若干片段处理,内存不爆,还能并行。智能过滤,注意这个过滤发生在日志解析之前——先把不需要同步的数据在日志层面就滤掉,而不是解析成数据之后识别再丢弃。顺序不一样,省下来的 IO 是数量级的差别,这个我在上篇提过一次,这里再说一遍因为它真的值钱。多通道并行入库,也是上篇讲过的。
这几招有个共同点让我很满意:全部跟应用和生产库解耦,不用改业务代码,不用改生产库参数。在真实项目里"让业务方改代码"这句话基本等于"这事别做了",懂的都懂。
最后那家七点完成 350G 跑批,七点零五分前追平,同步延迟小于 5 分钟,源端解析速率 90MB/s 以上。另有一份材料里还有个指标:单次 100G 以内的跑批同步时延小于 30 分钟。
(那段时间的监控截图我手机里还存着,早上在地铁上刷,看那条延迟曲线往下掉,比看股票有意思。)
我在这儿必须加一句批判。我在不同材料里看到的跑批指标是互相打架的——有的项目写高峰期延迟小于 10 秒,有的写 1800 秒,还有写"大于 3 天"的。一开始我以为有人数据造假,后来想明白了:统计口径不一样。有的是平均延迟,有的是峰值延迟;有的业务跑批几万笔,有的是千万级大事务;有的目标端是 KES,有的是 Kafka。口径不同的数字之间没法横向比,硬比就是自欺欺人。所以看案例一定要问两件事:大事务到底多大、延迟是哪个口径。不然你拿别人的 10 秒去承诺自己的业务,验收那天会非常难看。
六、无主键表,这次收获最大的一块
接着说无主键表。这块是我这次体验里觉得最值的部分,因为之前我对它的认知是错的。
同步软件在目标端应用 update 和 delete 的时候,得先定位到具体的行。有主键,一行 SQL 搞定;没主键,只能全表扫着去匹配,效率随数据量线性恶化。更糟的是一张大表的无主键 update 能把整条链路拖死,后面的事务全部排队,延迟一路涨上去,从秒级涨到小时级,然后你就得解释为什么。
那个金融项目的数字很直观:5000 多张业务表里,1200 多张没有主键。这个比例我觉得在老系统里相当常见,尤其是年头久、经历过好几轮改造的系统,谁还记得十几年前那张表为什么不加主键。
他们的解法我觉得特别聪明——不硬扛,分流。按两个条件把无主键表筛出来:表里有大对象的、日更新删除量大于 10 万条的。符合的,改成 T+1 定时同步,跟实时链路彻底解耦;剩下那些变更量小的无主键表,照样走实时。
最后筛出来需要特殊处理的只有 55 张。
每套环境这些表每天变化量大约 70G,早上五点半跑 T+1 同步,五分钟搞定,速率约 240MB/s。
这思路我总结成一句话吧,虽然我不太爱总结:把"难同步的数据"和"必须实时的数据"分开处理。大部分同步项目出事,不是技术不行,是把两类数据混在一条链路上,然后用实时性的标准要求所有数据。实际上一堆表根本不需要秒级,给它 T+1 就够,谁也不难受。这个结论听着像常识,但在项目上就是有人想不明白,非要把 5000 张表全塞进实时链路里,然后天天救火。
-- 我自己排查无主键表用的(Oracle)SELECTt.owner,t.table_name,(SELECTCOUNT(*)FROMall_constraints cWHEREc.owner=t.ownerANDc.table_name=t.table_nameANDc.constraint_typeIN('P','U'))ASpk_cnt,t.num_rowsFROMall_tables tWHEREt.ownerIN('APP1','APP2')ANDt.num_rows>100000ORDERBYpk_cnt,t.num_rowsDESC;-- KES / PG 系SELECTc.relname,c.reltuples::bigintASrows,(SELECTCOUNT(*)FROMpg_index iWHEREi.indrelid=c.oidANDi.indisunique)ASuniq_idxFROMpg_class cJOINpg_namespace nONn.oid=c.relnamespaceWHEREn.nspname='app1'ANDc.relkind='r'ORDERBYuniq_idx,c.reltuplesDESC;迁移之前先把这张清单拉出来,能省掉后面一半的麻烦。这话我说第二遍了,因为它真的能省一半。
七、存量搬迁里那些脏东西
顺带说说存量搬迁本身。原理特别朴素:拿源库当前数据做个快照整体搬到目标端加载,再把搬迁过程中新产生的增量解析同步过去,最后跟持续运行的业务无缝衔接。
听着简单,实际上最容易出问题的恰恰是这堆存量里的脏东西——某张表有超长字段、某个字段有非法字符、某张表被锁着搬不动。这些玩意儿在测试环境里一个都碰不到,生产上一抓一把。
迁移这块它有个演进过程,三个阶段,我觉得挺有代表性,能看出这类工具是怎么被项目逼着长大的。
1.0 最朴素:全量迁移加增量同步,中间要部署临时节点,依赖短暂停机和环境配置。能干,但要停。
2.0 进步了一大截:基于 SCN(Oracle)或者 LSN(MySQL、DB2)做全量迁移,KFS 同时把增量数据缓存下来,不需要临时节点了,对源端侵扰小很多。停服时间不再随存量大小线性增长,TB 级能做到分钟级停服。
3.0 是我最推荐的:全无感。原生产库一秒钟都不用停,也不用改源库配置,冲突由它自动检测处理。据说比旧方案迁移时间还能缩短 30%,这个数字我没法验证,听听就好。它不再依赖数据库闪回机制,也不需要备份还原和临时节点——旧方案那两个痛点(依赖闪回、增量大时 UNDO 容量吃紧;要备临时节点还要短暂停机)在新方案里都没了。
完整流程大概是四步:结构迁移用评估工具做,全量数据迁移用迁移工具基于 SCN/LSN 快照做,全量启动的同时同步软件从那个 SCN/LSN 之后开始解析增量并缓存在本地,全量完成后把缓存的增量灌进目标库,直到两边 SCN/LSN 追平,最后把应用的 JDBC 指过去。
-- Oracle 取当前 SCN 作为全量迁移的一致性点SELECTCURRENT_SCNFROMV$DATABASE;SELECTSCN_TO_TIMESTAMP(CURRENT_SCN)FROMV$DATABASE;-- KES / PG 系取 LSNSELECTpg_current_wal_lsn();SELECTsys_current_wal_lsn();每一步都有反向同步兜底。工期长,但对于那种"任何模块都不能出事"的核心系统,我认为这是唯一负责任的做法。
有个数据能佐证双轨的价值:某电信运营商的一级营销资源系统,存量 16TB,做了"北京—呼和浩特"双轨,历经三轮回退测试,延迟都在秒级。三轮回退测试这个动作本身比那个 16TB 更能说明问题。
八、那些听起来不可能的网络
这块我原本以为没机会遇到,后来发现国内挺常见的。
它能跨的网络环境包括局域网、广域网、正向和反向网络隔离设备、单向通信光闸、物理隔离网络、防火墙和各种网络安全设备。某省发改委的项目是省级中心往 16 个地市分发,地市之间带宽只有 2M,还丢包,靠的是传输压缩加断点续传。2M 带宽扛实时分发,我第一次听说的时候以为写错了。
还有个运营商的案例更有意思:网络环境里有承载网,两端数据库根本没法直连。解法是支持域名解析方式传输,绕开不能直通的限制。这种招数看着有点野,但现场就是需要这种野路子。
单向光闸我想多说一句。单向意味着没有回执,源端发出去就不知道目标端收到没有。这种网络下同步本质上只能靠"顺序编号加本地持久化"保证不丢——发出去的 KUFL 带序号,目标端按序号收,缺了就说明中间丢了,回头重发。这也是为什么 KUFL 那层设计在这种场景里特别关键,上篇我说它是三段式中间层,当时没意识到它在这儿还有这个用处。
九、翻案例数字的时候我在想什么
材料里那些案例我翻来覆去看了几遍,有几个观察。
医疗特别集中。301 医院八个院区 400 多个业务系统、60TB 存量、300G 日增;浙江省人民医院三个数据中心双向同步、3T 存量、110G 日增;西安市第一人民医院、天津宝坻、东莞六院、上海国际妇幼婴……医疗是主战场之一,原因也好理解,系统多、异构严重、一分钟都停不了。
金融类难度最高。嘉实基金 TA 系统 60TB 存量、每天 1500 万笔同步事务、还有跑批,最后做到业务切换时长小于 30 分钟,跑批延迟小于 30 分钟,日常延迟小于 1 秒。
冷水还是要泼的:这些数字全部来自厂商材料,没有第三方复现,我看到的所有"效果"都在"价值呈现"那一栏。这不代表数字是假的,但你做自己的容量规划时,别直接拿这些数当上限,留出余量,最好自己压一遍。
十、还差点意思的地方
夸了这么多,也说几条我认为不足的,不然这文章就成软文了。
文档和知识的颗粒度不均匀。热门数据源写得细,冷门的基本就几行,遇到边界问题只能自己试或者找人问。这个上篇提过,这里再说一次,因为它直接影响你的排期——我按文档估了三天,实际干了八天。
DDL 的语义翻译仍然是难题。它能同步 DDL,也能做一定程度的改写转换,但不同数据库之间的 DDL 语义差异是天然的,索引组织表、分区语法、存储参数这些,翻译不了就是翻译不了。我的建议是 DDL 别指望全自动,重要对象的结构变更还是人工过一遍,反正也不多。
AI 相关的能力目前还偏概念。新那个数据集成平台里提到了 AI 做资源调度、智能助手辅助部署和运维诊断,还有向量数据类型的同步支持。方向我认同,但这类能力的成熟度取决于样本积累,短期内我持观望态度,不会为这个买单。
生态绑定。它跟自家数据库的组合体验最好,这是事实也是优势;反过来说你的目标端不是那家的库,这个优势就没了,得重新评估。这条不算缺点,算选型的时候要算进去的账。
十一、最后
这两篇写下来,我对这类工具的评价是:它解决的是一个很具体的工程问题——在不打扰生产库的前提下,把数据按事务顺序、低延迟、可校验、可回退地搬到另一个地方。它在这个问题上做得相当扎实,尤其 KUFL 中间层、不停机校验、大事务分片、无主键表分流这几处,能看出是真的在大量项目里磨过的,不是拍脑袋设计的。
但工具永远只是工具。真正决定成败的是你对业务的理解:哪些表必须实时、哪些可以 T+1、哪些数据错了要人看一眼、哪些故障要演练过才算数。这些工具替你回答不了。
如果你也在做类似的事,动手前先想清楚三件事:你的一致性是哪种一致、你的大事务有多大、你的无主键表有多少张。这三个答案基本就决定了方案长什么样,剩下的都是体力活。