企业做数据同步,真正危险的往往不是任务报错,而是任务显示“成功”,数据却已经重复、遗漏或失真。
订单被重复写入,销售额被放大;
历史订单发生修改,增量任务却没有捕获;
金额字段进入目标库后精度被截断;
任务失败后从头重跑,又制造出重复数据。
因此,可靠的数据同步不能只回答“能不能把数据搬过去”,还要回答三个问题:
同一条数据重复执行会不会重复?
源端发生的变化有没有全部到达?
到达后的内容和业务含义是否仍然正确?
在正式展开前,我整理了一份《数据仓库建设解决方案》,涵盖数据建设、数据治理和企业数字化实践等内容,需要的可以自取:https://s.fanruan.com/7igmg(复制到浏览器)
一、先理解“不重、不丢、不错”
“不重”,不是同步结束后再做一次去重,而是同一批数据被重复读取、重复投递或任务重复启动时,目标端最终仍然只有一份。
“不丢”,也不只是源表和目标表总行数相同。新增、修改、删除,以及迟到数据、边界数据、停机期间的数据,都可能被遗漏。
“不错”,则要求字段值、数据类型、编码格式、关联关系和业务口径保持一致。两边各有100万行,不代表金额没有被截断、状态没有映射错误、时间没有因时区变化而偏移。
所以,一条可信的同步链路至少要保证四层一致:
记录完整、主键唯一、字段正确、业务可解释。
二、全量同步:难点不是“搬完”,而是建立一致起点
全量同步常用于首次上线、目标表重建、历史数据回补和故障恢复。
最简单的做法,是清空目标表,再把源表全部写入。但只要源系统仍在产生业务,全量期间就可能出现“动态快照”问题。
例如,全量任务10点开始、12点结束。期间新增或修改的数据,可能被重复读取,也可能被跳过。最后得到的既不是10点快照,也不是12点状态。
更可靠的方案是“一致性快照+增量追平”:
全量开始前记录日志位点、LSN或时间水位;
在同一快照口径下读取历史数据;
全量写入结束后,从已记录位点消费增量;
增量追平后,再切换为持续同步。
关键在于:全量与增量必须共享一个明确的交接点。
没有交接点,两段任务之间会出现空档;范围重叠却没有幂等机制,又会产生重复。
大表全量还要合理分片。按主键区间、日期分区或哈希分桶读取,通常比不断增加offset更稳定,也便于失败后只重跑局部区间。
在FineDataLink中,可以根据场景选择追加写入、清空目标表后写入,或基于标识字段执行新增、修改和删除。初始化阶段先装载历史数据,再以标识字段衔接后续变化,更容易把“第一次搬全”和“后续持续更新”连成一条完整链路。
三、增量同步:关键是找到可靠的变化依据
增量同步只处理上次任务之后发生变化的数据,效率更高,但同步边界也最容易出错。
1、自增主键
按照“主键大于上次最大值”读取,适合只新增、不修改的流水表。
它无法识别历史记录修改和删除;旧数据补录、主键回填或主键不连续,也可能造成误判。
自增主键适合判断“新增了什么”,却不适合判断“什么发生了变化”。
2、更新时间戳
按照update_time读取新增和修改,是定时同步中最常见的方式。
但只使用“update_time>上次最大时间”并不安全。多条数据可能拥有相同时间戳,数据库时间精度也可能不足,位于边界上的记录容易被漏掉。
更稳妥的方法是使用“时间戳+主键”组成复合水位;也可以把起点向前重叠数秒,再依靠目标端幂等消除重复。
例如,上次同步到10:00:00,本次可以从09:59:55开始重新读取。多取几秒的数据并不可怕,只要目标端能够识别同一条记录;真正危险的是把边界卡得过死,导致数据永久遗漏。
增量同步的原则不是“绝不重复读取”,而是“允许少量重叠,但不能留下数据空档”。
3、业务时间
下单时间、发货日期和开票日期描述业务何时发生,却不一定反映数据何时修改。
三个月前的订单今天被修正,如果只按下单时间取增量,这次变化不会进入目标端。
因此,业务时间适合划分分析范围,更新时间才更接近技术增量边界。
4、数据库日志或CDC
CDC直接读取数据库日志中的插入、更新和删除事件,不必反复扫描整张表,适合高频变化和低延迟场景。
但CDC也不是开箱即“绝对不丢”。日志保留时间不足、读取位点失效、表没有稳定主键、DDL变更不兼容,都可能破坏同步连续性。
FineDataLink可基于CDC、Binlog、LogMiner等方式进行实时增量同步。部署时仍要检查日志是否开启、保留多久、断点是否有效,以及目标表用什么字段识别同一条记录。
工具负责捕获变化,工程规则决定链路能否长期稳定。
四、如何保证“不重”:核心不是去重,而是幂等
网络超时、消费端重启、写入确认丢失,都可能让同一批数据再次到达目标端。
工程上更常见的方案是:
允许数据至少投递一次,但要求目标端重复执行后结果不变。
这就是幂等。
实现幂等,需要四个条件。
第一,建立稳定的唯一键。订单ID、客户ID,或者“来源系统+业务单号”,必须能够唯一识别业务对象。
第二,使用upsert或merge,而不是无条件append:
目标端不存在,则插入;
已经存在,则更新;
内容没有变化,则不重复处理。
第三,为事件建立版本。可以使用日志位点、版本号或更新时间判断新旧,避免较晚到达的旧事件覆盖最新状态。
第四,正确安排提交顺序。应先完成目标端写入,再提交同步断点。
假设一批数据已经从源端读取,但还没有写入目标端,系统就提前更新了断点。此时一旦写入失败,任务重新启动后会直接从新断点继续,中间的数据将被永久跳过。
反过来,如果目标端已经写入成功,但断点暂时没有提交,任务重启后最多只是重复读取。只要目标端具备幂等能力,重复数据就能被识别和覆盖。
因此,可靠同步宁可接受“可控重复”,也不能接受“不可恢复的遗漏”。
五、如何保证“不丢”:覆盖数据的完整生命周期
很多同步任务只处理新增和修改,却没有处理删除。
源系统删除一张无效订单,目标表仍然保留,报表就会继续统计,业务结果已经失真。
删除同步一般有三种方式:
从CDC日志中捕获物理删除;
使用is_deleted等字段同步逻辑删除;
周期性执行快照比对,识别源端已不存在的记录。
除此之外,还要处理三类隐性丢数。
1、迟到数据
业务早已发生,但因接口积压、人工补录或上游故障,过一段时间才进入源表。
可以设置回看窗口,定期重刷最近若干天的数据。
2、乱序数据
先发生的事件后到,后发生的事件先到。
目标端不能只看接收顺序,而应结合版本号或业务更新时间判断最新状态,避免旧事件覆盖新结果。
3、断点失效
任务停机时间超过日志保留周期,原来的位点已经无法继续读取。
此时不能直接从最新位置启动,而要重做受影响范围的全量或补数,再建立新的增量起点。
此外,还要记录源端读取位点、目标端写入位点、每批读取量、成功量、失败量和最大更新时间。
如果系统只显示“任务成功”,却无法回答“同步到了哪一条、哪个时间点”,一旦发生问题,就只能依赖人工猜测和整表重跑。
真正的不丢,不是从未失败,而是失败后知道停在哪里、缺了哪一段、怎样准确补回来。
六、如何保证“不错”:校验要从技术层走到业务层
行数校验只能发现“大面积少了或多了”,无法证明内容正确。
一套可靠的校验机制,至少要分五层。
1、数量校验
比较源端读取量、过滤量、成功写入量和失败量。
原则上应满足:
读取量=成功写入量+过滤量+失败量。
任何一项没有明确去向,都意味着链路存在黑箱。
2、唯一性校验
检查主键和业务单号是否重复,一对一关系是否变成一对多。
重复通常说明幂等规则失效,或不同系统的标识发生冲突。
3、汇总校验
按日期、组织、地区、订单状态等维度,比对记录数、金额和数量。
总量一致而分组不一致,通常意味着字段映射或状态转换出错。
例如,总销售额完全相同,但华东地区金额减少、华南地区金额增加,可能是地区编码映射发生了错位。
4、内容校验
对关键字段按固定顺序拼接并计算哈希。同一主键哈希不同,就说明内容不一致;大表可以先按分区校验,再缩小范围。
需要注意的是,计算哈希前必须统一空值、日期格式、小数精度和字符编码,否则同一条数据也可能计算出不同结果。
5、业务规则校验
例如:
订单金额不能为负;
退款金额不能高于实付金额;
发货时间不能早于下单时间;
客户编码必须存在于主数据中;
已关闭订单不能继续产生发货记录。
技术同步成功,只能说明数据被写进去了;业务规则校验通过,才能说明数据可以被使用。
FineDataLink的任务日志能够暴露源端增量读取、目标端写入、连接状态和日志断点等问题。
把这些运行信息与行数差异、金额差异、延迟时长一起纳入监控,校验就能从月底人工对账,转变为同步过程中的持续控制。
七、异常处理:不是简单重跑,而是分类恢复
同步失败后无限重试,可能把临时问题变成系统雪崩,也可能让错误数据被反复放大。
第一类:临时性异常
例如网络超时、数据库短暂不可用、接口限流。
这类问题可以采用指数退避重试,并设置最大次数,避免任务集中冲击上游。
第二类:数据异常
例如字段超长、日期格式错误、必填字段为空。
这类问题反复重试通常没有意义,应进入脏数据表或隔离区,保留原始记录、失败原因和批次号,让正常数据继续执行。
第三类:结构与规则异常
例如源字段被删除、类型变化、主键规则调整、目标表结构不兼容。
这类问题应立即停止相关链路并告警,避免错误继续向下游扩散。
不是所有异常都适合重试。临时故障可以重试,数据错误需要隔离,结构变化必须人工确认。
补数也必须遵循原来的唯一键、版本判断和校验规则,否则可能制造重复,或用旧数据覆盖新状态。
例如,补录三天前遗漏的订单时,不能无条件覆盖目标表中的同一订单。因为这张订单可能已经在今天被修改,三天前的旧版本反而会把新状态覆盖掉。
因此,一套可恢复机制必须同时保留:
原始输入、任务批次、处理断点、失败原因、目标结果和重放入口。
异常处理真正要解决的,不只是“怎样把任务重新跑起来”,而是怎样在恢复任务的同时,不制造新的重复、遗漏和错误。
八、结语
判断一条数据同步链路是否可靠,可以检查六个问题:
全量开始时是否有一致快照和明确的增量交接点?
增量依据能否覆盖新增、修改和删除?
重复投递后,目标结果是否仍然唯一?
断点是否在目标端写入成功后再推进?
校验是否从行数延伸到字段、汇总和业务规则?
失败后能否定位范围、隔离异常并安全补数?
“不重”依靠唯一键、版本控制和幂等写入;“不丢”依靠变化捕获、断点管理和补偿机制;“不错”依靠分层校验与业务规则。
数据同步真正要建设的,不是一条永远不会报错的链路,而是一套即使发生中断、重复和异常,也能够发现问题、恢复数据并证明结果可信的机制。