您现在的位置是:首页 > 文章详情

一文搞懂 CDC(Change Data Capture)同步原理解析

日期:2025-02-06点击:77

file

CDC简介

CDC(Change Data Capture)是一种用于跟踪数据库库变更事件(插入、更新、删除)中的行级更改,并将事件以发生的顺序通知到其他系统处理。在容灾场景下,CDC主要实现的是主备间的数据同步,即从主数据库到备数据库的数据实时同步。

source ----------> CDC ----------> sink

Apache SeaTunne CDC

SeaTunnel CDC的数据同步分为两种:

  • 快照读:读取表的历史数据

  • 增量跟踪:读取表的增量日志更改数据

无锁快照同步

无锁快照同步阶段,为什么强调无锁,是因为现有的CDC平台在进行历史数据的同步时可能会进行锁表操作,例如Debezium。快照读阶段就是对数据库的历史数据库进行同步的过程,其基本概述流程如下:

storage------------->splitEnumerator----------split---------->reader ^ | | | \-----------------report------------/ 

Split划分

splitEnumerator(split分发器)按照指定的字段(例如表id或唯一键)和步长将表数据划分为多个分片split。

并行处理

每个split通过路由算法分配给不同的reader进行并行读取,一个reader会占用一个连接。

事件反馈

每个reader完成split读取后会向splitEnumerator报告进度。splitEnumerator会发送给reader一个分片,分片的元数据信息如下:

String splitId 路由id TableId tableId 表id SeatunnelRowType splitKeyType 分片基于的字段的类型 Object splitStart 分片读取起点 Object splitEnd 分片读取终点 

reader收到split信息后会生成相关的sql语句,在此之前会记录当前split对应到数据库日志log的开始位置,等处理完当前split后上报report给splitEnumerator,report内容如下:

String splitId 分片id Offset highWatermark 分片对应log的位置,用于后续的校对 

增量同步

增量同步阶段是基于上述快照读取阶段后,在源数据库发生变化时,实时将变更的数据同步到备数据库,不同的是,此阶段监听的是数据库的log日志,例如mysql的bin log。增量跟踪通常是单线程处理,这样可以避免重复拉取bin log,减轻对数据库的压力,因此该阶段只有一个reader工作,只占用一个连接。

data log------------->splitEnumerator----------split---------->reader ^ | | | \-----------------report------------/ 

增量同步会合成快照阶段所有split、table,因此只会存在一个split,增量同步阶段的split信息如下:

String splitId Offset startingOffset 所有split中最小的log start Offset endingOffset log的结束位置,若无则代表是持续的,例如增量阶段 List<TableId> tableIds Map<TableId, Offset> tanleWatermarks 所有split的watermark List<CompletedSnapshotSplitInfo> completedSnapshotSplitInfos 快照阶段读取的split细节信息 

其中CompletedSnapshotSplitInfo的具体字段如下:

String splitId TableId tableId SeatunnelRowType splitKeyType Object splitStart Object splitEnd Offset watermark 对应了report中的highWatermark 

增量阶段的split包含了快照阶段所有split的watermark,会去从其中选出一个合适的位置进行增量同步,这个合适位置就是最小的watermark。

Exactly-once

无论是快照读还是增量读,同步的过程中数据库可能也在经历变化,如何保证exactly-once?

快照读阶段

在快照读阶段,例如某个split在同步的过程中,这段split中的数据发生了变换,例如下图操作,插入一条k3,更新k2,删除k1,如果在读的过程中不做任务标识,那么这部分的更新信息就会丢失,seatunnel的做法是:

在Split读取之前首先去数据库查一下bin log位置:low watermark

读取split{start, end}数据

再记录一下高水位high watermark

如果high = low 说明在读取该split期间,该split的数据没有发生变化;

如果(high - low) > 0,说明在处理的过程中发生了数据变化,会进行如下操作:

  • 将读到的split数据在内存中建立内存表缓存;
  • 将low watermark~high watermark的变更;
  • 按顺序、主键重放操作到内存表

报告report high watermark

 insert k3 update k2 delete k1 | | | v v v bin log --|---------------------------------------------------|-- log offset low watermark high watermark CDC读到的数据: k1 k3 k4 | 重放 v 真实的数据: k2 k3' k4 

增量阶段

在增量阶段开始之前首先会对上一个步骤的所有split做校验,因为在split和split之间的间隙也有可能出现数据更新,例如在split1和split2之间插入了若干条记录,在快照阶段就会遗漏掉,对于这种split之间的数据回捞,SeaTunnel的做法是:

从所有的split的report中找到最小的watermark,作为start watermark,开始读取log。

每读一条log都去completedSnapshotSplitInfos中找该条数据是否在某个split被处理过了,如果没有被处理过,说明是split间隙数据,应该被重新修正。

当表过滤完后,可以从completedSnapshotSplitInfos中删除,继续处理剩余的表。

直到所有的split都校验结束,就进入到了完全的增量阶段。

 |------------filter split2-----------------| |----filter split1------| data log -|-----------------------|------------------|----------------------------------|- log offset min watermark split1 watermark split2 watermark max watermark 

断点续传

如果做到暂停恢复?分布式快照算法(Chandy-Lamport):

假设系统中包含了两个进程p1和p2,p1进程状态包含三个变量X1 Y1 Z1,p2包含了三个变量X2 Y2 Z2,初始状态如下:

p1 p2 X1:0 X2:4 Y1:0 Y2:2 Z1:0 Z2:3 

此时由p1发起全局snapshot记录,p1先记录本身的进程状态,然后向p2发送marker信息。

在marker信息到达p2之前,p2向p1发送message M。

p1 p2 X1:0 -------marker-------> X2:4 Y1:0 <---------M---------- Y2:2 Z1:0 Z2:3 

p2收到p1发送来的Marker信息后,记录自己的状态,然后p1收到p2之前发送来的Message M,由于p1已经做了Local snapshot了,所以p1只需要记录M,所以最终的snapshot如下:

p1 M p2 X1:0 X2:4 Y1:0 Y2:2 Z1:0 Z2:3 

在SeaTunnel CDC的过程中,Marker同发送给所有的Reader、SplitEnumerator、Writer等节点都会保存自己的内存状态。

本文由 白鲸开源科技 提供发布支持!

原文链接:https://my.oschina.net/SeaTunnel/blog/17500993
关注公众号

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。

持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。

文章评论

共有0条评论来说两句吧...

文章二维码

扫描即可查看该文章

点击排行

推荐阅读

最新文章