金仓 KFS 探秘 | 异构数据同步的核心技术
异构数据同步从「能同步」到「敢切换」之间,隔着四道工程难关。今天不谈功能列表,只谈金仓 KFS 在每个难关上做了什么技术选择,以及这些选择在真实场景里意味着什么。
凌晨三点,电话响了。核心库 12 TB,日增 300 G,月末跑批会在 40 分钟内灌进 100 G 以上的单次日志增量。迁移窗口给了 4 小时,业务方的原话是: 「可以慢一点,但不能丢一条,也不能回不来。」
这种场景,我见过太多次。方案讨论会上,大家争的往往不是「用哪个工具」,而是三个更朴素的问题: 变更怎么捕获?峰值怎么扛住?一致怎么证明? 如果再加上一句「万一不行怎么退回去」,就是四个。
今天我想拆解一下金仓的 Kingbase FlySync(简称 KFS) , 它为了回答上面四个问题采用了哪些核心技术:旁路日志解析、KUFL 中间文件、seqno 时间轴、事务级并行入库。
本文速览
捕获层选「旁路日志解析」 —— 因为只有物理日志里同时存在事务边界、提交顺序和行级前项/后项,这是触发器、时间戳、批量 ETL 都给不了的东西。 传输层选「KUFL 落盘中间文件」 —— 不是为了缓存,而是为了把「源库故障」「网络中断」「目标库故障」三类问题解耦,让断点续传成为默认能力。 应用层选「事务级 + 分区并行」 —— 并行的前提是保序,KFS 用 seqno 与分区器把「无序的并行」收敛回「有序的结果」。 治理层选「可证明一致」 —— 四级校验 + 不停机快照校验 + 自动修复,把「我觉得一致」变成「我能出具报告」。
01 · 先说真正的痛点:数据同步的五道坎
很多人以为数据同步的难点是「写得快不快」。不是。真正的难点是在不打扰业务的前提下,把一件有状态的事持续做对。这中间有五道坎,每一道都能让项目翻车。
坎一:怎么「看见」变更——四条路线,四种代价
捕获变更有四种思路。它们不是「好坏之分」,而是「代价放在哪里」的区别:
触发器 / UDF 双写:在源表上挂触发器,把变更写进中间表。代价是改表、加锁、递归风险,以及每次 DML 都要多一次写入——源库性能直接掉一档,而且触发器一旦漏配,就是静默丢数据。 时间戳 / 自增列轮询:按 update_time > ?拉增量。便宜,但物理上看不见 DELETE,也看不见「同一秒内的多次修改」,时间戳精度和时钟漂移就是天花板。批量 ETL:定时全量或增量抽取。适合 T+1 的数仓,不适合「切换窗口只有 4 小时」的迁移——它的粒度天生就是批,不是事务。 日志解析:直接读 redo / binlog / WAL / xlog。这是数据库自己为崩溃恢复写的流水账,天然带事务边界、提交顺序、行级前后镜像。
图 1|四条捕获路线的代价分布。注意最后一列的「全绿」不是营销话术,而是原理决定的:物理日志是数据库为自身崩溃恢复写下的账本,事务边界与提交顺序是它天生具备的字段。走日志,等于借用了数据库的原子性与持久性。
这张表最值得记住的是 「断点与重放」 。触发器方案里,你没有一个全局坐标能说「我已经同步到这里了」;而日志解析有——Oracle 是 SCN,MySQL 是 binlog 位点,KES 是 LSN,KFS 在它们之上统一抽象成 seqno。没有这个坐标,「断点续传」这个词就是空谈。
数据库的日志,是唯一一份既记录「发生了什么」、又记录「以什么顺序、在哪个事务里发生」的数据。所有不走日志的同步方案,本质上都在事后重新猜测事务语义。
——这就是为什么 KFS 把捕获建在日志层
坎二:怎么「不丢不重」——断点必须落在可重放的坐标上
同步软件最怕的不是慢,是不知道自己做到哪儿了。网络抖一下、目标库重启一次、源库归档被 DBA 清掉一轮,如果状态只存在内存里,你唯一的选择就是「重来」。而重来意味着:目标端要么重复应用(主键冲突),要么先清空(业务停摆)。
所以第二个技术选择几乎是被逼出来的: 变更必须先落成持久化的中间文件,再谈传输。这就是 KFS 的 KUFL。
坎三:怎么「扛住峰值」——大事务不是慢,是内存与时延的雪崩
日常 20 MB/s,跑批时 100 G 一次性涌入。传统的实现崩溃原因几乎一样:读到超大事务 → 整个事务在内存里攒着等提交 → 内存打满 → GC 抖动 → 解析线程卡死 → 时延从秒级变成小时级 → 用户以为「同步挂了」。
这不是调大堆内存能解决的。堆内存调大只是把雪崩推迟十分钟。 必须让一个事务可以被切开、被分段确认——这是后文 KFS「大事务分片」要展开的核心。
坎四:怎么「证明一致」——不停机,还要能定位到行
迁移项目里最难回答的一句话是甲方问的: 「你凭什么说两边一样?」
停机比对是最容易的答案,也是最无法被用户接受的答案——它要求业务停摆,而且要停两次(比对 + 修复)。不停机比对则要面对一个悖论:你一边比,数据一边在变。所以真正的难点不是「比不比得了」,而是在一个会变化的数据集上,如何给出一个可以被复现、被定位、被修复的结论。
坎五:怎么「活着回来」——回切不是再同步一次
国产化替代的现场,最怕的不是切不过去,是切过去之后发现不对,却回不来。回切的本质是同步方向反转 + 断点接续 + 过滤器对称,缺一不可。很多项目在这一步翻车,不是因为工具有问题,而是因为方案设计时没人把它当一等公民。
02 · 架构:一条被设计成「可观测管道」的流水线
KFS 的组件划分很有讲究,但每个组件的边界划得也很有意思:
这张表里藏着两个架构判断,值得单独说:
第一,管理面与数据面分离。 Console 不碰数据流,只做 JMX 观测与指令下发。所以你在监控界面卡住的时候,同步并不会停——这对生产环境是保命的。
第二,比对服务被单独拆出来。 因为比对会消耗大量 CPU,和管控平台放一起会让监控变慢。KFS 把「一致性校验」当成一件重活来对待,而不是监控面板上的一个按钮。
图 2|组件与数据流。蓝色是数据面(真正搬数据的那条线),下方灰色是管理面。把这两层分开画,是因为它们的故障模式完全不同:管理面挂了只是「看不见」,数据面挂了才会「丢数据」。KFS 让管理面只做 JMX 观测,本质上是在限制故障的传播半径。
为什么需要一个「中间文件」
第一次看到 KUFL 的人常问:为什么不直接在内存里传?落盘不是更慢吗?
答案是: 慢一点,但活得久一点。 KUFL 的存在,把三类本来会纠缠在一起的故障彻底解耦了:
源库出问题(重启、归档被清、日志切换):源端 KUFL 已经落盘,解析恢复后从 seqno 接着写,不需要重新问数据库要日志。 网络出问题:目标端 KUFL 有断点,重连后从断点续传,而不是「整段重放」——这正是「网络抖动不丢数据」的实现基础。 目标库出问题:目标端 KUFL 变成缓冲区,故障期间持续堆积,恢复后按 seqno 追平。这也解释了手册里那条容易被忽略的容量公式——KUFL 需要预留磁盘,而且至少要放得下 7 天的日增量。
KUFL 里到底装了什么?我从 KFS 手册与运维命令中似乎能反推(注意我不是用 AI 逆向出来的, 真的是纯从用户角度反推出来的)出它的最小结构: 事务坐标(seqno)+ 事务边界 + 行级变更(含前项 BIC、后项)+ 分片号(fragno)+ 校验和(checksum) 。这五个字段决定了后面所有能力:能断点、能跳过、能校验、能冲突裁决、能定位到「某事务的某条语句」(fsrepctl online -frag-sql 14_0 这种精度,就是靠 fragno 撑起来的)。
图 3|seqno 是一条可以被追溯的时间轴。红色是断点表 trep_commit_seqno,黄色是心跳表 heartbeat——前者回答「做到哪儿了」,后者回答「链路还活着吗」。断点续投之所以可靠,是因为它不只是记住了一个数字,还记住了这个数字对应事务的完整内容。
这里有个容易被忽略的工程细节:KFS 在源端每解析出一个事务,除了写 KUFL,还要更新数据库里的断点表。手册在性能调优章节专门提到,如果源库访问特别慢,可以把断点信息改存到文件(replicator.store.kufl.dataSource=file_metadata),代价是失去手动调整断点的能力。这种「用一致性换性能」的开关存在,本身就说明设计者对代价是清楚的。
03 · 六个硬问题,六种工程解法
前面都是铺垫。这一节开始,每一个小节都对应一个真实项目里会卡住人的问题:症状是什么、原理是什么、KFS 用什么手段解决、实施者要注意什么。
3.1 大事务:把一个 100 G 的事务切成能消化的块
症状很好认。 源端业务压力很大,同步服务一直是 ONLINE,但你连着敲几次 kufl list -last,最后一行的 TIME 死活不往前走。基本上就能判定:有大事务一直解析不出来,处于阻塞状态。
为什么会这样? 因为事务是原子单位。一个跑批事务写了 4 亿行,它就是一个事务;在它提交之前,任何严谨的实现都不能把「半个事务」投给目标端——那会破坏原子性。于是这半个事务的内容只能待在内存或临时区里等着,越等越大。
KFS 的做法是把「原子」和「不可分割的传输单位」这两件事拆开: 原子性由提交标记保证,传输与解析的粒度可以更细。具体是三层手段叠加:
大事务分片:按行数切片( replicator.extractor.dbms.minRowsPerBlock,例如 1000 行一片),每片带一个fragno;一个事务从「一坨」变成「一串」。这直接压住了内存峰值。多线程并行解析:切片之后,解析天然可以并行,多核才真正被用上。 智能过滤前置:这是金仓的一项专利思路——在日志解析动作启动之前就把不需要同步的表/列过滤掉,而不是「先把所有日志还原成行,再逐行判断要不要」。跑批场景里,往往只有少数几张表需要同步,这一步省下的是数量级的 CPU 与内存。
三层叠加后能达到什么效果: 单次数据库日志增量 100 G+ 的跑批场景,同步时延 < 30 分钟。这个数字的意义不在于「快」,而在于它把「跑批期间同步会不会雪崩」变成了一个可以写进方案里的确定值。
图 4|切开、并行、再收敛。这张图想说明一件事:从「一个大事务」到「四条通道」,中间必须有两次收敛——一次在 KUFL(按 seqno 有序化),一次在分区器(相关事务归并到同通道)。少了任何一次,并行就会变成数据错乱。另外注意 fragno 与 sqlno 的存在,它们让运维可以精确跳到「某事务的第 14 片、第 0 条语句」。
3.2 并行入库:并行是假命题,除非你解决了顺序
这是我认为 KFS 设计里最值得学的一段,因为它没有回避矛盾。
目标端入库往往是全链路瓶颈:源端解析再快,目标库一条一条 apply 也追不上。直觉解法是「多开几个线程并行写」,但立刻撞上一个问题——一旦并行,事务的先后顺序就没了。而事务顺序恰恰是业务正确性的一部分:先插主表再插子表,顺序反了就是外键错误;先扣款再入账,顺序反了就是对账不平。
KFS 的答案是「分而治之,但按相关性分」,落在三个配置项上:
RoundRobinPartitioner | ||
ShardListPartitionershard.list | (*) 统一映射 > (hash-method) | |
shardbytable |
还有一个细节容易被忽略:每个通道前面有个 barrier(屏障) 。状态里能看到 store.queue.barrier.0: false 这样的字段,含义是「该队列用量曾经满过,且还没降到一半以下」。它的作用是防止抖动——队列在临界点反复启停,比偶尔慢一点更伤性能。
顺便提一个场景, 也可能是你会踩到的“坑”:如果一个事务里频繁切换表名,且每个事务的变更行数很少(事务 1 改表 A、事务 2 改表 B、事务 3 又改表 A),批量提交就失效了,入库性能下降是正常现象,不是 bug。这类「多表混合小事务」的场景,堆通道数也没用,因为瓶颈在批量粒度而不在并发度。
注意 · 实施顺序有讲究
一定要「先完成初始搬迁,再做通道拆分」。如果你先拆了多通道再去做存量迁移,需要针对每个目标端服务分别做迁移,工作量翻倍且极易漏表。
还有一处细节, 逆向不可行: 单通道可以
fspm update升到多通道,但多通道不能直接退回单通道(可能丢数据)。真要退,只能保留多通道配置、把channels=1。
3.3 冲突:before image 才是钥匙
双活、双轨、灰度切换——只要目标端同时还有别的写入,冲突就是必然的。这时候大多数方案的应对是「报错、停下、等人」,而 KFS 选择把冲突变成一种可声明、可裁决的事件。
关键在于它能在源端把 before image(前项) 一并带出来。所谓前项,就是 UPDATE/DELETE 时那些用来做 WHERE 条件的旧值。有了它,「这一行在我这边到底是什么状态」才可判断。开启方式是源端 getBeforeCols=true 加一份 beforeColsRule.json,逐表指定要取哪些列的前项。
KFS 把冲突归成五类,并为每一类提供独立策略:
其中 USEMAX:COL / USEMAXEQ / USEMIN / USEMINEQ 这四个策略特别值得说:它们把裁决权交给某一列的大小关系——「如果中间文件里这行的版本号大于目标库,就覆盖;否则丢弃」。这就是最经典的「最后写入者胜」的手工实现,但做成了配置。要注意两个限制:参与比较的列只支持整数、浮点、时间类型;列名不能带 : 或 ,。
但作为老司机, 我必须强调一句经验: 冲突处理是最后一道闸,不是第一选择。 当你在方案里大量使用 USEMAX 时,说明业务侧的写入边界没划清。真正的做法是通过分片键、单向写、或者业务改造让冲突不发生;工具的能力是用来兜底的,不是用来当架构的。
3.4 错误处理:别让一条脏数据拖垮整条链路
错误处理和冲突处理经常被混为一谈,其实两者层次不同: 错误处理是技术性的(这一条 SQL 目标库执行不了,怎么办),冲突处理是业务性的(这条数据谁说了算)。
默认情况下,目标端遇到主键冲突、非空约束、表不存在,同步会进入 ERROR:OFFLINE 等着人来处理。KFS 允许你把它变成策略:STOP(抛出,交旧策略)、IGNORE_ROW(只丢出错语句)、IGNORE_TRANSACTION(丢整个事务)、DISCARD_ROW / DISCARD_TRANSACTION(丢弃并单独记日志)。
这套机制的工程感体现在两个地方:
按错误码配置,而不是一刀切。 failure_policy_code.json里可以写"*.*": {"errorCode": "DISCARD_ROW"},粒度细到「模式.表 + 错误号」。标准流程:先全量 DISCARD_ROW 跑一轮 → 从discard.log里把错误号抄出来 → 再改成只对特定错误号跳过。防抖。 如果冲突数据太多,服务会反复 offline/online,性能断崖式下降。所以有个 error_handle_threshold(1~1000)控制连续报错的容忍度——这是个典型的「踩过坑才会想到」的参数。
还有一个必须知道的边界: Oracle 一个事务里出现出错的 DDL,只能整事务忽略,无法只跳过那一条 DDL。 这种限制不是实现偷懒,而是 DDL 与事务语义纠缠的本质所致。知道边界,方案才不会写错。
3.5 一致性:把「我觉得一致」变成「我能证明」
这一节是我认为 KFS 相对同类产品最有分量的一块,因为它把「校验」从一个动作做成了一条流水线。
先看它提供的四种比对模式,成本与能力差异很大,选错了不是慢一点的问题:
真正解决「一边比一边变」这个悖论的,是快照校验:对 KingbaseES V8 目标端,可以手动把目标端服务 offline,让数据静止在一个时间点上做校验;比对并修复完成后,再用 online -skip-event [断点] 从校验结果里给出的断点恢复。这样既不需要停业务(只停了同步投递),又能拿到一个确定的、可复现的结论。
再往上,是任务组合策略(命令行 -ploy 0~5):精简 + MD5 先筛,详细比对定位,最后自动修复。这就是把「发现—定位—修复」串成一条可调度的流水线,而不是三次人工操作。
注意 · 支持单表级别刷新重来
reload-table可以在不做校验的情况下强制把某张表刷成一致,代价是:1:N 场景会把所有 N 端一起刷;级联场景会刷完整条链;N:1 如果汇集到同一张表,会冲掉目标表全部数据;而且它不能与校验、初始化搬迁并发,也没有高可用保护(刷新中断会导致不一致,只能再刷一次)。
最后是修复的闭环。 手动修复支持按表、按页、按行、按操作类型(INSERT/UPDATE/DELETE)分别执行;自动修复则绑定比对任务,配置「等待时间」与「差异条数阈值」,差异数达到阈值才触发,避免为几行差异启动一整套修复流程。这一层设计的意义是:一致性维护从「项目交付时的一次性动作」变成了「运行期的常态能力」。
3.6 网络与隔离:当 TCP 只剩一个方向
如果你善于用第一性原理思考问题, 那么你一定会问前面所有的能力, 到底基于什么假设?
OK , 前面所有能力,都建立在一个默认前提上: 两端能互相说话。而在电力调度、军工、政务这类场景里,这个前提经常不成立——中间横着一台单向隔离装置(光闸),物理上只允许数据从左往右流,连 TCP 的回包都没有。
这时候你会发现,很多「实时同步产品」直接失效了:握手失败、确认收不到、重传无从谈起。
KFS 在这些场景里的做法很朴素,也很正确: 既然没有会话,那就不要会话。它把数据落成 KUFL 文件,让文件穿过隔离设备,对端再按文件消费。文件天然带边界、带校验和、带序号,不需要「确认」也能保证不丢不重——这是一种把可靠性从「协议层」下移到「数据层」的思路。
图 5|没有会话的同步。单向隔离环境里没有 TCP 回包,也就没有握手、确认与重传——可靠性只能从「协议层」下移到「数据层」:文件即消息,序号即顺序,校验和即确认。代价是能力必须被裁剪:实时同步可用,管控平台只能看到 III 区的部分指标,比对服务不可用,数据迁移只能用命令行的特殊模式。这不是产品缺陷,而是物理约束的诚实映射。
官方给的网络侧指标也值得放在方案里: 广域网传输约 4 倍压缩比,2 M 带宽即可支撑实时容灾。这个数字对分支机构、边缘节点、跨省专线场景是决定性的——它意味着你不必为了同步扩一条专线。
04 · 架构师的取舍:没有最优,只有匹配
做到这一步,你大概能感觉到 KFS 的设计哲学: 它不追求「一种方式做所有事」,而是把选择权连同代价一起交给你来判断, 相当灵活, 按需搭配。 既然要你选, 那我就深入一点, 这一节把最容易选错的三个决策点摊开。
取舍一:源端怎么抽——六种方式,能力边界完全不同
binlog_format=ROW | ||||
binlog_format=ROW | ||||
这张表很重要。因为选了能力边界不匹配的方式 —— 比如用 LogMiner 去同步一张带 BLOB 的表,跑到 90% 才发现不支持,那已经不是调优问题,是返工问题。
另外,源端还包括 Oracle RAC、MariaDB、PostgreSQL、PolarDB、DB2、达梦、OceanBase,以及 RabbitMQ / RocketMQ 这类消息中间件;目标端另有 Kafka、KADB 与 SQL 文件落盘。这个「源端多、目标端也多」的组合能力,这些才是 KFS 支持「异构」两个字的实际含量。
取舍二:装在一起还是分开装
这里有个容易被忽视的连带影响: 抽取方式决定了部署位置,部署位置又决定了网络带宽预算。LogMiner 可以分离,但意味着全量日志要走网络;Redo 解析快但要同机,意味着你得接受「同步进程和生产库共享一台机器」的心理门槛。这两件事必须在方案阶段一起定,不能等到实施。
取舍三:存量数据怎么搬
两条经验值: 存量小于 200 GB 时,用 KFS 自带的表数据迁移工具是划算的;超过这个量级,专业迁移工具的并行度和容错通常更值得。而且要注意 KFS 自带的 ddlscan 只搬表结构,不包含 PL/SQL 对象与外键——所以复杂 schema 的场景,结构迁移要么用 KDMS,要么用备份还原,KFS 只负责「增量追平」这一段。
注意 · 迁移工具的容错差异要提前说清
KFS 自带的表数据迁移目前没有可靠的断点容错,一个任务出错就得整体重跑。所以手册建议「少量多次」,每批最多 50 张表——这样出错时只需重跑一小批。这不是缺陷,是使用方式的约束,写进实施计划就不会出事。
05 · 双轨与回切:给「切换」留一条退路
前面讲的都是「怎么把数据搬过去」。但国产化替代真正的分水岭,是敢不敢把业务切过去。切换的勇气不来自信心,来自退路。
KFS 在这件事上的设计,是把「单向同步」变成「双向双轨」,并且承认它有三个阶段:
旧库为主、新库为备:数据实时一致,新库可以先承接一部分只读流量,用来验证兼容性与性能。 新库为主、旧库为备:稳定运行一段时间后切换主备角色,写流量进入新库,同时反向同步回旧库。这一阶段是「双轨」的核心——两边数据都是活的。 旧库下线:新库能力被充分验证后,拆除反向同步,旧库退役。
而回切,就发生在第二阶段:新库暴露了稳定性或性能问题,需要紧急撤回。 回切的速度与数据完整性,取决于你前面有没有把它设计进去。
图 6|双轨并行的三个阶段与回切的最后两步。注意第 ⑤ 步:反向同步不是「重新配一条链路」,而是同一套服务把角色互换——所以两端都必须按 role=master 配置。这也是为什么过滤器必须对称:反向时 rename 的映射要对偶、casetransform 的方向要相反,否则数据会在两个方向上来回「变形」。
回切的五个动作,一个都不能少
停业务:先停止源端业务系统的写入,让数据静止。 反转方向:用 repswitch完成同步方向切换,而不是手工改配置——手工改配置最容易漏掉断点。验证一致:用预先准备好的比对方案验证两端数据。这一步不能省,因为「方向反了」不等于「数据齐了」。 启动备用系统:拉起旧库侧的业务系统。 确认反向同步:确认新方向的数据流正常,才算回切完成。
还有一个常被忽略的后续: 回切之后怎么再切回来。仅需做一次存量差异迁移、验证一致后再切。原因很简单:回切期间产生的增量,可能有一部分没被覆盖到;如果不重新对齐就切,错误会以「数据不一致」的形式在后续爆发。
一个成熟的迁移方案,不是证明「一定能成功」,而是证明「失败了也能在 30 分钟内回到原点」。
—— 回切能力,是国产化替代项目里最难, 但最容易被低估的一项需求
06 · 把经验变成公式:容量、判据与调参顺序
这一节是给要写实施方案的人准备的。手册里散落着一些公式和判据,我把它们集中起来,因为它们决定你的方案是「看起来合理」还是「算得出来」。
三个容量公式
举个算例:最大事务 5 GB、日增 100 GB、KUFL 保留 7 天,最低配置是每服务 8 核 / 15 GB 内存 / 700 GB 磁盘,推荐配置是 13 核 / 25 GB / 1.5 TB。这些数字比「建议 8C16G」这种拍脑袋的说法有用得多——因为它可以复核。
三段定界:先搞清楚慢在哪,再谈调参
性能问题最大的浪费,是在错误的地方调参数。KFS 给了一套非常工程化的定界方法: 把链路拆成解析、传输、入库三段,逐段量出「处理一份 KUFL 需要多久」,再和「业务生成这份数据用了多久」比。
图 7|三段定界。这套方法最妙的地方是它只用了一条命令和一次计时:用 kufl list -last 判断解析是否落后,用 du 算传输速率,用 fsrepctl wait -applied 量入库耗时。不需要压测工具,也能在客户现场 20 分钟内给出结论。
定界之后才是调参:
加大缓冲: replicator.global.buffer.size从默认 10 提到 100,减少目标库 commit 次数。几乎零成本。增加入库通道: channels=4+ 内存队列 + 分区器。前提是「事务之间没有顺序依赖」,否则会踩冲突。拆成多个服务:一个服务只同步一组表,彼此完全独立。这一招不仅加通道,还能让每个服务用更小的批量、更高的 PBE 效率。 组合:M 个服务 × 每个服务 N 个通道。到这一步基本是「用资源换吞吐」了。
源端解析慢的情况另有一套:大事务导致解析卡住 → 调 minRowsPerBlock 强制切片;业务量太大 → 源端多服务按表拆分;源库访问慢 → 把断点改存文件(dataSource=file_metadata)或开启元信息缓存(cacheMeta=true,仅 Oracle Redo 支持)。
一份可以直接抄的配置
下面这份 flysync.ini 是源端(Oracle → KES)的骨架,注释里标了每个参数「为什么在这里」:
# ---- 全局:安装路径与 RMI 端口 ----
[defaults]
install-directory=/home/flysync/kfs
rmi-port=11000
# ---- 源端服务 ----
[ora2kes]
role=master
master=node-a
members=node-a
kufl-port=3112
replication-host=10.0.0.11
replication-port=1521
replication-user=FLYSYNC
replication-password=******
datasource-type=oracle
oracle-extractor-method=logminer # 换成 redo 可支持 LOB/DDL,但需同机
datasource-oracle-service=ORCL
# 只同步这两个模式;注意心跳表所在模式必须显式包含
property=replicator.extractor.dbms.tablePatterns=APP.*,FLYSYNC.*
# 大事务切片:每 1000 行一片,防止单事务撑爆内存
property=replicator.extractor.dbms.minRowsPerBlock=1000
# 取前项列,供目标端冲突裁决(双轨/双活必备)
property=replicator.extractor.dbms.getBeforeCols=true
# 默认不同步 DDL;需要同步时改用 ddlSupport 过滤器
svc-extractor-filters=dropstatementdata
目标端则是另一套逻辑——这里的每一个参数,都对应前面讲的某个「硬问题」:
# ---- 目标端:并行入库 + 分区 + 冲突裁决 ----
[kes-slave]
role=slave
master=node-a
datasource-type=kingbase8
# 1) 四个通道,事务按 shard 固定到通道(有外键必须用 ShardList)
svc_parallelization_type=memory
channels=4
property=replicator.store.parallel-queue.partitionerClass=\
com.kingbase.flysync.replicator.storage.parallel.ShardListPartitioner
property=replicator.store.parallel.partitioner.shardListFile=/home/kfs/conf/shard.list
# 2) shard 值从 schema 改成表名,粒度才够细
svc-remote-filters=shardbytable,ddlSupport
# 3) 冲突裁决(DML 冲突按 5 类分别处理)
property=replicator.applier.dbms.conflictResolve=true
property=replicator.applier.dbms.beforeColsFile=\
/home/kfs/support/filters-config/conflictResolveRule.json
# 4) 批量与缓冲:先给足,再按定界结果回调
property=replicator.applier.dbms.maxRowBatchSize=1000
property=replicator.global.buffer.size=100
如果只让我留一条排障命令,我会留这个——它把每个阶段、每个通道的耗时一次摊开:
$ fsrepctl -service kes-slave perf
Stage | Seqno | Latency | Events | Extraction | Applying | Total
remote-to-kufl | 114 | 0.071s | 102 | 32.900s | 0.035s | 32.937s
kufl-to-q | 114 | 0.105s | 114 | 32.907s | 0.061s | 32.970s
q-to-dbms | 84 | 0.098s | 51 | 32.862s | 0.050s | 32.914s
q-to-dbms1 | 114 | 0.108s | 43 | 32.928s | 0.046s | 32.975s
q-to-dbms2 | 14 | 13.873s | 3 | 29.861s | 0.019s | 29.881s
q-to-dbms3 | 35 | 0.068s | 23 | 32.792s | 0.033s | 32.825s
看第四列 Events 与 Latency:q-to-dbms2 只处理了 3 个事件,却积压了 13.8 秒。这就是典型的通道倾斜——要么分区规则把热点表都指到了同一个通道,要么那条通道上有慢 SQL。不用猜,数字直接告诉你。
总结
不同身份的读者, 今天能从这篇文章带走啥?
刚入门的 DBA:先记住两件事—— seqno是这条链路的唯一时间坐标,KUFL是它的物理载体。所有「同步卡住了」的排查,都从kufl list -last看 TIME 是否前进开始。实施工程师:把三段定界法背下来,再记住三条顺序约束——先搬迁后拆通道、单通道可升多通道但不可逆、改 ini 必须 stop → fspm update→ start。这三条能省掉你一半的返工。架构师:先定「抽取方式」,它同时决定了部署位置、能力边界(LOB/DDL)和网络预算。三个约束是一体的,不能分开拍板。 资深专家:把「并行、冲突、校验」当成三个正交问题分别调。它们的耦合点只有一个:事务粒度。粒度选错,三个方向怎么调都是白费。
最后说一句我自己的判断。
看一个数据同步产品,我通常不看它支持多少种数据库,而看它在边界条件下的表现:大事务怎么办、断点怎么存、并行怎么保序、没有回包怎么传、一致怎么证明。这五个问题,是没有捷径的——每一个都要用具体的技术手段去换。
KFS 在这五个问题上的答案,指向同一种设计趣味: 把不可靠的部分(网络、对端、进程)都推到边界之外,把可靠性收敛到一个可以被持久化、被编号、被校验的中间层。KUFL 就是那个中间层。理解了这一点,你再看它那些参数,就不会觉得是零散的功能开关,而是一套自洽的工程取舍。