转载

数据同步利器:实现单向与双向同步,赋能多场景数据流转

背景

在做架构设计服务高可用方便,会涉及到数据同步的情况。而双活建设要求两个数据中心同时承担读写业务,为了保障两边数据一致性,就要求实现实时双向同步。目前公司内部使用的数据源较多,包含 MySQL、Elasticsearch、kafka、redis、doris 等等。然而,现有的开源工具普遍只支持单向同步,无法满足双向同步需求,更不用说同时支持多种数据源的双向同步。因此,我们决定自研一款数据同步工具,以实现多数据源间的实时双向同步,确保双活建设顺利推进。

架构设计

设计目标
• 支持双向同步

当前工具建设初衷是需要支持双活场景,满足在同一时刻对各个可用单元的数据库都能够进行读写操作的要求。为保障两边数据的一致性,需要支持双向同步功能。另外,该工具还需要支持单向同步,以便于支撑其它的如灾备、数据迁移、异构同步等场景。

• 支持多数据源数据同步

目前公司内业务使用到数据源种类繁多。为了能够统一公司内部的数据同步工具,降低运维成本,该工具需要支持多种数据源之间的数据同步,包含同种数据源之间的单、双向同步以及异构数据源之间的单向同步。

• 具备高扩展性

随着业务复杂度的提升以及工具的推广使用,后续可能会不断接入新的数据源。因此该工具需要采用高扩展性的架构,降低新数据源的接入成本。

• 支持数据一致性校验

不论是单向同步还是双向同步,数据一致性始终是数据同步过程中需要重点关注的点。工具需要支持对某一个时刻下源和目标的数据库中数据的一致性校验。这里还包含同构和异构数据的一致性校验。

• 支持高可用性

数据同步工具后续会服务于不同业务,而数据延迟以及数据一致性对于业务来说都是比较关心的指标。工具具备高可用性能够降低数据延迟和不一致的风险,在出现故障的场景下也能够快速恢复并断点续传,减少数据不一致对业务的影响。

整体设计

                                   图 1  系统架构图

图 1 是工具实现的系统架构图,自上而下分为 4 层:管控层、通用能力层、执行层以及存储层。

• 管控层:负责对整个数据同步工具进行集中管理和控制,直接面向工具的使用人员。因此除了基础的数据同步相关配置功能外,还提供了权限管理和日志审计功能;

• 通用能力层:负责提供一些通用能力,如任务调度分配、监控指标采集、woker 集群管理以及流量控制等。

• 执行层:负责具体任务的执行,如执行具体的数据同步、数据校验等等;

• 存储层:负责配置信息以及过程数据的存储。

根据以上系统架构图的分层逻辑,具体实现中是将所有功能放在 3 个微服务中承载,分别为:管控服务、调度服务以及任务执行( worker)服务。管控服务基本承载了整个管控层内的所有功能,主要负责配置和查看以及配置下发。调度服务负责具体的任务调度,也就是某个任务要在哪个节点上完成以及任务的故障转移,保障任务高可用。任务执行服务负责执行分配下来的具体的任务,比如某个 ETL 任务或者是某个一致性校验任务。具体实现的应用架构图如 图 2 所示。如图中所示,每个虚线框都是一套独立的系统,这样设计是为了在多云多可用区场景下,数据同步节点和数据源尽量在同一个可用区中,减少网络延迟以及避免产生额外跨区流量费用。另外再由一套管控服务进行统一管控,方便运维管理。

核心技术点

实时数据同步

当前有不少支持数据同步的工具,它们的实现逻辑大部分都是通过 CDC 方式获实时获取到变更记录,然后经过 ETL 流程将数据同步到目标数据源。数据同步的重点就在于要能够获取到源数据中的变更信息,也就是说只要能够实时获取到某个数据源的变更记录,就能够进行实时数据同步。

但是这种方式只能获取到增量数据,对于存量数据的同步场景不一定能够满足。比如 MySQL 的存量数据对应的 binlog 可能已经因为过期被删除了,这时候就无法获取到变更记录了。因此还需要其它方式来实现存量数据的同步。不同数据源解法不同,有些可以是直接文件拷贝,有些只能用遍历历史数据再逐个迁移。以 MySQL 为例,采用的就是遍历的方式,但并非简单遍历,需要考虑整体性能。具体做法是记录某个时刻下的主键最大最小值以及该时刻对应的 gtid,然后采用多组并发+分页读取的方式将该区间的所有数据遍历,遍历完成后再从指定 gtid 消费这区间产生的变更数据即可完成存量数据的同步,并且还能无缝续上增量数据同步。简易的数据同步过程如图 3 所示。

冲突问题处理
对于单向同步而言,能够实现数据同步就可以了,但是对于双向同步而言,这还不够。因为对于双活而言,每个可用区都会往对应的数据源中写数据,如果某个时刻或者在某个很短的时间区间内,两边都对同一条记录做了更新,那么最终是以谁为准?特别是在某个方向的数据同步存在延迟的情况下,如何保障最终一致性?

要解决这个问题可以借鉴 CRDT 的思路,将同步过程拆分成 可交换顺序、可随意组合以及幂等的操作。数据更新过程中,可以拆解为字段更新,可以细分为某个字段在什么时间段进行更新,在遇到冲突时,通过比较更新时间确定这个字段应该取哪个值(Last Write Win)。这样同样一组数据,不论是进行怎么组合,最终得出的结果都是一致的。这种方案的注意事项是要确保源和目标数据库的时间同步。图 4 呈现了不开启同步和以 Last Write Win 方式开启双向同步的效果。( 注:图中 01 和 02 表示两个机房中的 DB,向右的箭头表示时间轴。此外这两个 DB 对同一条记录进行更新操作,这条记录只有两个字段 x 和 y,--表示对于字段没变更)

      图 4 双向同步效果图

回环问题处理

在双向同步过程中,还需要解决回环问题。回环问题见图 5,简单来说就是 DB1 中更新的数据被同步工具同步到 DB2,而因为同步过程在 DB2 也会产生变更日志,因此又会被同步工具同步到 DB1。要解决回环问题就是要对数据进行打标。不同数据源打标的方式不太一样,可以是对数据本身进行打标,也可以是在事务中加入标识。这里打标有个注意点是只有业务主动写入的才会更新标识,而数据工具同步过去的数据是不能改变标识的,否则就无法真正做到对变更做溯源。在数据同步工具中通过识别变更日志带有的原始标识与目标数据源的标识是否一致,来判断数据是否需要在目标源上执行,从而打断回环。

数据一致性校验

由于存在网络延迟,只要两边数据在不断更新,那么数据就无法做到实时一致。因此这里的一致性更多是指某个时刻内的数据一致性,或者是停写状态下的最终一致性。不同的数据源和不同的使用场景其适用的一致性校验方式不一样,比如有些只需要做数据量校验,有些适合做整体哈希校验,而有些则需要逐条记录对比。此外,为保障数据一致性,在大部分场景下需要定时对增量的数据做校验,在这场景下数据同步在不断进行,哪怕指定了对比数据所处的时间区间,但还需要考虑在对比期间该时间区间内的数据被更新但又未同步到目标数据库的情况,避免出现大量不一致的无效告警。

任务模型抽象

数据同步的核心流程是 ETL 流程,这个流程可以适配大部分数据同步场景。但还是有些场景不适合,比如 ES 存量数据同步是采用直接文件拷贝效率最高,不适合用走 ETL 流程。此外数据校验也是比较特殊的流程。为了保证整体架构一致性,降低系统复杂度,需要将各种流程场景抽象成任务模型。对于整个系统而言,不论是走哪个流程,都是当做任务执行,而且不同任务类型的配置是独立的。这样不仅能够支持各类流程的个性化需求,也能确保整体结构统一,方便后续的功能扩展和代码维护。

扩展性考虑

考虑到随着业务发展,后续工具还会不断接入新的数据源,因此扩展性也是我们重点考量的。为了实现这一目标,我们采用了微内核+插件式的架构设计,使得新增数据源变得简便且高效。能采用这种架构主要是考虑到各种不同的数据源从整个数据同步过程来看,基本流程都是一致的,比如大部分都可以走 ETL 模式,那么利用微内核+插件架构,不同数据源接入只需要完成对应阶段的代码实现即可实现数据同步功能。比如工具已经支持了 MySQL 到 kafka 的数据同步,现在要新增 MySQL 到 Elasticsearch 的数据同步,只需要完成 Elasticsearch L 阶段相关实现即可,无需其他操作。

线上实践

数据同步工具上线至今已稳定运行了一年多,现已支持的数据源有:Elasticsearch、MySQL、Redis、Kafka、S3、Doris、Clickhouse 等,其中源端支持的数据源有:Elasticsearch、MySQL、Redis 和 Kafka。为了能够支持双活场景的需要,我们也对部分数据源的源码进行改造,使其支持双向同步。数据同步工具在公司内部的使用场景覆盖了双活、主备、异构数据同步和数据迁移等等。图 6 呈现的是线上 ES 数据同步组的使用情况。从图中可以看出单单在 ES 数据同步组中,目前使用到的场景就有主从同步、双活以及 ES 到 Kafka 的异构数据同步。

为了提升工具的稳定性和可靠性,我们在工具中增加了多项关键指标,以便能够实时追踪系统运行状况。这些指标不仅涵盖了容量监控、任务状态、数据整体延迟、资源使用情况、异常事件等核心性能参数,还补充了对各个核心流程的延迟与处理性能监控,部分监控指标如图 7 所示。通过这些指标再配置合理的告警策略,我们能够提前识别潜在风险,在问题还未对业务造成影响之前采取预防措施。同时,若出现故障,告警系统能够即时通知相关人员,通过指标快速定位问题,缩短排查和修复的时间,从而保障系统稳定性。

正文到此结束
Loading...