前言

互联网时代,每天都会产生海量的数据,这些数据刚开始会被记录到某个存储中。比如最常用的关系型数据库 MySQL,在一家公司发展的业务初期可以满足大部分的需求,但是随着业务发展,单一数据源必然无法满足所有的(包括功能、性能、数据安全性方面)需求,不同数据源之间的数据同步迁移就成了很必要的功能。

我司的数据同步(也可称数据复制或者数据传输)平台,服务着内部各类系统和业务场景,比如:cache 异步更新、数据变更订阅、数据库表迁移同步等,是研发基础设施中重要的组成部分。

本文将介绍我司在落地数据同步平台时的设计与实践,希望能对大家能够有所帮助。

实践背景

我们最开始关于数据同步的需求可以归纳为:

  • 数据变更订阅
  • 数据库表迁移

作为一家生长在云上的公司,最初我们的数据同步需求是基于公有云厂商的数据传输服务实现的,但是随着业务发展,数据同步链路越来越多,云上的这块成本也越来越高。

同时,云上的数据同步不能满足我们内部的一些个性化需求,比如分库分表同步(by sharding column)、压测数据过滤、Kafka 分区投递(by 分区键)等等;

综合成本和未来功能扩展的考虑,我们选择自建数据同步平台来满足内部的功能需求。

目标

基于当时的实际使用场景,对于数据同步平台,我们制定了以下目标:

  • 功能性目标

    • 支持 MySQL -> MySQL 的历史数据同步&增量实时数据同步

    • 支持 MySQL -> Kafka 的历史数据同步&增量实时数据同步

  • 非功能性

    • 支持 数据同步链路的监控告警
    • 高可用保障
    • 数据一致性保障

数据同步平台介绍

方案选型

我们调研了目前比较流行的数据同步开源方案,并且进行了对比,

OtterCanalMaxwell
HA支持支持不支持
数据落地(原生支持的)MySQLKafka、RocketMQ 等消息中间件/MySQLKafka
数据落地时支持分区不支持支持不支持
历史数据同步支持不支持支持
文档较详细详细详细
可视化管理有,但不人性化
监控

最终我们选择了基于 otter 二次开发来搭建我们的数据同步平台。

选择 otter 的原因有几点:

  • 管理 & 运维方便. otter 为纯 Java 开发的系统,提供 web 管理界面,自带同步链路监控,一站式管理整个公司的数据同步链路(不需要自己开发前端页面,节省开发时间)
  • otter 架构设计清晰,由国人纯 Java 开发,关键类的注释也做的很好,便于二次开发,otter 将整个数据同步模块分成了 SETL 四个阶段,我们可以自定义 ETL 的逻辑,来满足不同的需求。

image-20211026115128987

相关改造 & 技术实践

1、支持 通过 Kafka 订阅消息变更

前面提到 otter 的架构非常清晰,模块职责明确,易于二次开发,为了支持 Kafka 订阅消息变更,我们在原有架构上做了如下的改动:

image-20211026144704892

1、定义 Kafka 目标数据源 (DataMediaSource)

2、改造控制面后端逻辑和前端页面,支持 Kafka 的配置

image-20211026143734589

3、改造 Transerformer 模块,支持将数据转换成投递到 Kafka 的固定消息格式

4、改造 Load 模块,支持将数据向 Kafka 发送,并且支持按照指定的分区键路由到不同的 Kafka Partition

2、基于 otter 实现全量数据同步

自由门是 otter 提供的一个功能特性,可以在不修改原始表数据的前提下触发数据表中的数据同步。可以用于:

  • 同步数据订正
  • 全量数据同步. (自由门触发全量,同时 otter 增量同步,需要配置为行记录模式,避免 update 时因目标库不存在记录而丢失 update 操作)

主要原理就是基于 otter 系统表 retl_buffer ,插入特定的数据,包含需要同步的表名,pk 信息,然后 otter 系统感知后会根据表名和 pk 提取对应的数据 (整行记录),和正常的增量同步一起同步到目标库(基于主键的反查源库再插入目标库)。

我们目前的操作方式是由 DBA 手动执行 retl_buffer 表的插入(后期可以工具化),具体的流程如下图:

image-20211027202115082

3、基于 otter 实现分库分表的数据同步

分库分表数据同步主要用于业务表的分库分表拆分,比如由于业务发展,一张大表被拆分成 N 个库 N 个表,存量和增量的数据都需要按照分片规则同步到拆分后的单元表中;

前面提到了 otter 将整个数据同步模块分成了 SETL 四个阶段,其中的 Extract 模块负责数据抽取,其中的 EventProcessor 类是一个开放接口,可以通过扩展 EventProcessor 自定义数据处理。EventProcessor 支持在 manager 页面发布源码,动态编译。

image-20211027203541115

我们基于 EventProcessor 实现了分库分表的数据同步功能,拿 1 张表被拆成 16 个库举例,EventProcessor 的源码可以写成:

public class DescMessageEventProcessor extends AbstractEventProcessor {

    String tableName = "dtle_";
    String columnName = "sn";

    @Override
    public boolean process(EventData eventData) {
        if (eventData.getEventType().isDdl()) {
            return true;
        }

        EventColumn eventColumn = getColumn(eventData, columnName);
        Integer strLength = eventColumn.getColumnValue().length();
        String numStr = eventColumn.getColumnValue().substring(strLength - 4, strLength);
        Integer num = Integer.parseInt(numStr) % 16;
        String targetTableName = tableName + num;
        eventData.setTableName(targetTableName);
        return true;
    }

}

如果涉及到多个库的同步,区别就在于配置多条关系映射来同步不同的数据源,分表的规则大致类似,另外就是对于不属于当前分库的数据过滤,只同步属于当前库的数据即可。

业务开发同学也可以根据示例来修改 EventProcessor 的逻辑,从而灵活实现不同的分库分表规则。

4、数据对比平台

初期的数据同步平台上线之后,随着数据同步链路的持续增加,业务侧和 DBA 都需要知道数据同步是否可靠,数据是否有丢失,亟需要有工具来对比、校准链路上下游的数据是否一致。

基于以上的需求,我们为数据同步平台增加了数据校验模块(内部称数据对比平台),来为上下游同步数据的准确性提供保证,数据校验模块目前支持以下功能。

a. 数据对比

数据对比任务支持全库/全表的行记录比对。由于数据对比任务原理是从上下游数据库表中查出行记录逐行进行对比,所以在配置时一般配置成连接到专门的读库,并且要求业务侧在业务低峰期执行数据对比任务。

image-20211028101455651

b. 日常数据巡检

由于数据对比任务一次执行任务长,并且可能会业务造成影响,所以我们提供更轻量级的日常数据巡检功能,业务可以根据数据巡检结果,再决定是否进行数据对比。

image-20211028103502563

c. 日志&报表&告警

上述的数据巡检和数据对比任务,执行的结果会以三个形式体现出来:

  • 数据巡检/对比日志
  • 数据报表
  • 数据不一致告警

数据巡检和对比任务每次的执行都会留下对应的日志记录,以供查询,

image-20211028102247123

image-20211028102330705

当数据对比/巡检任务出现不一致的情况,数据对比平台会向对应的告警群发送异常告警。

image-20211028104845494

同时会产生数据报表供 DBA 下载,DBA 可以执行产生的报表中的修复 SQL 来修正数据。

数据校准其实也可以做成自动化,但是由于 DBA 侧有人工核对的需求,所以做成了这种模式。

image-20211028104349476

5、其他

除了上述的改造以外,我们做了其他的一些改造,比如通过修改底层源码实现压测影子表的过滤,支持了gh-ost 修改表结构的同步,监控告警模块支持飞书/邮件告警等等,具体的改造点总结如下:

image-20211028140346435

阶段性成果

经过了一系列的技术实践与改造,数据同步平台目前的整体架构可以用下图表示:

image-20211028141416749

自建的数据同步平台上线以来,顺利完成了预期目标,完全替换了云上的数据同步链路。成本方面线上集群月均节省20W+

性能方面,端到端数据同步延时能控制1s之内,单条链路数据订阅吞吐量最大能到达 1.5w/s

image-20211028133626517

image-20211028134452673

总结 & 规划

本文介绍了我司基于 otter 自建数据同步平台的相关改造与实践,关于 otter 系统性的介绍限于篇幅和主题没有过多介绍,感兴趣的读者可以移步官方 wiki。

在整个过程当中我们面临特性化需求多、开发资源少、时间紧的问题,otter 强大的功能和简洁清晰的架构模块设计大大节省了我们的开发时间,如果你也在调研数据同步的方案,不妨研究一下。

未来,我们将规划支持更多数据源,围绕数据平台支持更多通用的数据异构场景(Cache/ES 等),为业务侧创造更大的价值。

参考文档