圖解 DataX 核心設計原理

DataX 是阿里巴巴開源的一個異構數據源離線同步工具,致力於實現包括關係型數據庫(MySQL、Oracle 等)、HDFS、Hive、ODPS、HBase、FTP 等各種異構數據源之間穩定高效的數據同步功能。

前段時間我在 K8s 相關文章中有提到過數據同步的項目,該項目就是基於 DataX 內核構建的,由於公司數據同步的需求,還需要在 DataX 原有的基礎上支持增量同步功能,同時支持分佈式調度,在「使用 K8s 進行作業調度實戰分享」這篇文章中已經詳細描述其中的實現。

基於我在項目中對 DataX 的實踐過程,給大家分享我所理解的 DataX 核心設計原理。

設計理念

異構數據源離線同步是將源端數據同步到目的端,但是端與端的數據源類型種類繁多,在沒有 DataX 之前,端與端的鏈路將組成一個複雜的網狀結構,非常零散無法將同步核心邏輯抽象出來,DataX 的理念就是作爲一個同步核心載體連接連接各類數據源,當我們需要數據同步時,只需要以插件的形式接入到 DataX 即可,將複雜的網狀結構鏈路變成了一個星型結構,如下圖所示:

架構設計

用過 IDEA 的小夥都知道,IDEA 有很多非常棒的插件,用戶可根據自身編程需求,下載相關的插件,DataX 也是使用這種可插拔的設計,採用了 Framework + Plugin 的架構設計,如下圖所示:

有了插件,DataX 可支持任意數據源到數據源,只要實現了 Reader/Writer Plugin,官方已經實現了主流的數據源插件,比如 MySQL、Oracle、SQLServer 等,當然我們也可以開發一個 DataX 插件。

核心概念

DataX 核心主要由 Job、Task Group、Task、Channel 等概念組成:

1、Job

在 DataX 中用來描述一個源端到一個目的端的同步作業,是 DataX 數據同步面向用戶的最小業務單元。一個Job 對應 一個 JobContainer, JobContainer 負責 Job 的全局切分、調度、前置語句和後置語句等工作。

2、Task Group

一組 Task 的集合,根據 DataX 的公平分配策略,公平地分配 Task 到對應的 TaskGroup 中。一個 TaskGroup 對應一個 TaskGroupContainer,負責執行一組 Task。

3、Task

Job 的最小執行單元,一個 Job 可根據 Reader 端切分策略,且分成若干個 Task,以便於併發執行。

Job、Task Group、Task 三者之間的關係可以用如下圖表示:

根據切分策略將一個 Job 切分成多個 Task,根據分配策略將多個 Task 組成一個 TaskGroup。

4、Channel

DataX 會單獨啓動一條線程運行運行一個 Task,而 Task 會持有一個 Channel,用作 Reader 與 Writer 的數據傳輸媒介,DataX 的數據流向都是按照 Reader—>Channel—>Writer 的方向流轉,用如下圖表示:

Channel 作爲傳輸通道,即能充當緩衝層,同時還能對數據傳輸進行限流操作。

5、Transformer

DataX 的 transformer 模式同時還提供了強大的數據轉換功能,DataX 默認提供了豐富的數據轉換實現類,用戶還可以根據項目自身需求,擴展數據轉換。

調度流程

DataX 將用戶的 job.json 同步作業配置解析成一個 Job,DataX 通過 JobContainer 完成全局切分、調度、前置語句和後置語句等工作,整體調度流程用如下圖表示:

1、切分策略

1)計算併發量(即 needChannelNumber 大小)

DataX有流控模式,其中,可以設置 bps 限速,tps 限速:

  • bps 限速:needChannelNumber = 總 byteLimit / 單個 Channel byteLimit

  • tps 限速:needChannelNumber = 總 recordLimit / 單個 Channel recordLimit

如果以上都沒有設置,則會根據用戶在 job.setting.speed.channel 配置的併發數量設置 needChannelNumber。

2)根據 needChannelNumber 將 Job 切分成多個 Task

這個步驟的具體切分邏輯交由相關插件去完成,例如 Rdb 對數據的拆分主要分成兩類:

  • 如果用戶配置了具體的 Table 數量,那麼就按照 Table 爲最小單元進行拆分(即一個 Table 對應一個 Task),並生成對應的 querySql;
  • 如果用戶還配置了 splitPk,則會根據 splitPk 進行切分,具體邏輯是根據 splitPk 區間對 Table 進行拆分,並生成對應的 querySql。

2、公平分配策略

DataX 在執行調度之前,會調用 JobAssignUtil#assignFairly方法對切分好的 Task 公平分配給每個 TaskGroup。

在分配之前,會計算 TaskGroup 的數量,具體公式:

int taskGroupNumber = (int) Math.ceil(1.0 * channelNumber / channelsPerTaskGroup);

channelNumber 即爲在切分策略中根據用戶配置計算得到的 needChannelNumber 併發數量大小,channelsPerTaskGroup 爲每個 TaskGroup 需要的併發數量,默認爲 5。

求出 TaskGroup 的數量之後,就會執行公平分配策略,將 Task 平均分配個每個 TaskGroup,最後執行調度,完成整個同步作業。舉個公平分配策略的例子:

假設 A 庫有表 0、1、2,B 庫上有表 3、4,C 庫上有表 5、6、7,如果此時有 4 個 TaskGroup,則 assign 後的結果爲:

taskGroup-0: 0,  4,
taskGroup-1: 3,  6,
taskGroup-2: 5,  2,
taskGroup-3: 1,  7

舉個例子來描述 Job、Task、Task Group 之間的關係:

用戶構建了一個數據同步作業,該作業的目的是將 MySql 的 100 張表同步到 Oracle 庫中,假設此時用戶設置了 20 個併發(即 channelNumber=20):

  1. DataX 根據表的數量切分成 100 個 Task;
  2. DataX 默認給每個 TaskGroup 分配 5 個 Channel,因此 taskGroupNumber = channelNumber / channelsPerTaskGroup = 20 / 5 = 4;
  3. 根據 DataX 的公平分配策略,會將 100 個 Task 平均分配給每個 TaskGroup,因此每個 TaskGroup 處理 taskNumber / taskGroupNumber = 100 / 4 = 25 個 Task。

以上的例子用如下圖表示:

由於一個 Channel 對應一個線程執行,因此 DataX 的線程模型可以用如下圖表示:

作者簡介

作者張乘輝,擅長消息中間件技能,負責公司百萬 TPS 級別 Kafka 集羣的維護,作者維護的公號「後端進階」不定期分享 Kafka、RocketMQ 系列不講概念直接真刀真槍的實戰總結以及細節上的源碼分析;同時作者也是阿里開源分佈式事務框架 Seata Contributor,因此也會分享關於 Seata 的相關知識;當然公號也會分享 WEB 相關知識比如 Spring 全家桶等。內容不一定面面俱到,但一定讓你感受到作者對於技術的追求是認真的!

公衆號:後端進階

技術博客:https://objcoding.com/

GitHub:https://github.com/objcoding/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章