異構資料來源同步之資料同步 → DataX 使用細節

青石路發表於2024-06-04

開心一刻

中午我媽微信給我訊息

媽:兒子啊,媽電話欠費了,能幫媽充個話費嗎

我:媽,我知道了,我幫你充

當我幫我媽把話費充好,正準備回微信的時候,我媽微信給我發訊息了

媽:等會兒子,不用充了,剛剛有個二臂幫媽充上了

我輸入框中的(媽,充好了)是發還是不發?

有個二臂幫我充了

簡單使用

關於 DataX ,大家可以去看官網介紹:introduction

裡面講到了 DataX 的概況、框架設計、核心架構、外掛體系、核心優勢,由阿里出品,並在阿里內部被廣泛使用,其效能、穩定都是經過了嚴格考驗的。得益於它的框架設計

DataX框架設計
  • Reader:資料採集模組,負責採集源資料來源的資料,並將資料傳送給 FrameWork
  • Writer:資料寫入模組,不斷從 FrameWork 取資料,並將資料寫入目標資料來源
  • FrameWork:核心模組,用於連線 Reader 和 Writer,作為兩者的資料傳輸通道,並處理緩衝、流控、併發、資料轉換等核心問題

我們很容易實現二次開發,當然主要是針對新外掛的開發。DataX 已經實現了非常多的外掛

型別 資料來源 Reader(讀) Writer(寫) 文件
RDBMS 關係型資料庫 MySQL
Oracle
OceanBase
SQLServer
PostgreSQL
DRDS
Kingbase
通用RDBMS(支援所有關係型資料庫)
阿里雲數倉資料儲存 ODPS
ADB
ADS
OSS
OCS
Hologres
AnalyticDB For PostgreSQL
阿里雲中介軟體 datahub 讀 、寫
SLS 讀 、寫
圖資料庫 阿里雲 GDB
Neo4j
NoSQL資料儲存 OTS
Hbase0.94
Hbase1.1
Phoenix4.x
Phoenix5.x
MongoDB
Cassandra
數倉資料儲存 StarRocks 讀 、
ApacheDoris
ClickHouse
Databend
Hive
kudu
selectdb
無結構化資料儲存 TxtFile
FTP
HDFS
Elasticsearch
時間序列資料庫 OpenTSDB
TSDB
TDengine

囊括了絕大部分資料來源,我們直接拿來用就行;如果如上資料來源都未包括你們需要的資料來源,你們也可以自實現外掛,參考 DataX外掛開發寶典 即可

如果只是使用 DataX ,那下載 DataX 工具包 即可,解壓之後目錄結構如下

datax目錄結構

每個資料夾的作用就不做介紹了,大家去看官網看文件就行;透過 bin 目錄下的 datax.py 啟動 DataX

現有 MySQL 資料庫 qsl_datax,其上有表 qsl_datax_source

CREATE TABLE `qsl_datax_source` (
    `id` bigint(20) NOT NULL COMMENT '自增主鍵',
    `username` varchar(255) NOT NULL COMMENT '姓名',
    `password` varchar(255) NOT NULL COMMENT '密碼',
    `birth_day` date NOT NULL COMMENT '出生日期',
    `remark` text,
    PRIMARY KEY (`id`)
);
insert into `qsl_datax_source`(`id`, `username`, `password`, `birth_day`, `remark`) values
(1, '張三', 'z123456', '1991-01-01', '張三'),
(2, '李四', 'l123456', '1992-01-01', '李四'),
(3, '王五', 'w123456', '1993-01-01', '王五'),
(4, '麻子', 'm123456', '1994-01-01', '麻子');

需要將表中資料同步到 MySQL 資料庫 qsl_datax_sync 的表 qsl_datax_target

CREATE TABLE `qsl_datax_target` (
    `id` bigint(20) NOT NULL COMMENT '自增主鍵',
    `username` varchar(255) NOT NULL COMMENT '姓名',
    `pw` varchar(255) NOT NULL COMMENT '密碼',
    `birth_day` date NOT NULL COMMENT '出生日期',
    `note` text,
    PRIMARY KEY (`id`)
);

該如何實現?

  1. 配置 job.json

    因為是從 MySQL 同步到 MySQL ,所以我們的 ReaderMySQLWriter 也是 MySQL ,那麼配置檔案從哪複製也就清楚了。從 MysqlReader 複製 Reader 配置,從 MysqlWriter 複製 Writer 配置,然後將相關引數值配置成我們自己的,mysql2Mysql.json 就算配置完成

    {
      "job": {
        "setting": {
          "speed": {
            "channel": 5
          },
          "errorLimit": {
            "record": 0,
            "percentage": 0.02
          }
        },
        "content": [
          {
            "reader": {
              "name": "mysqlreader",
              "parameter": {
                "username": "root",
                "password": "123456",
                "column": [
                  "id",
                  "username",
                  "password",
                  "birth_day",
                  "remark"
                ],
                "connection": [
                  {
                    "jdbcUrl": [
                      "jdbc:mysql://192.168.2.118:3307/qsl_datax?useUnicode=true&characterEncoding=utf-8"
                    ],
                    "table": [
                      "qsl_datax_source"
                    ]
                  }
                ]
              }
            },
            "writer": {
              "name": "mysqlwriter",
              "parameter": {
                "writeMode": "insert",
                "username": "root",
                "password": "123456",
                "column": [
                  "id",
                  "username",
                  "pw",
                  "birth_day",
                  "note"
                ],
                "connection": [
                  {
                    "jdbcUrl": "jdbc:mysql://192.168.2.118:3306/qsl_datax_sync?useUnicode=true&characterEncoding=utf-8",
                    "table": [
                      "qsl_datax_target"
                    ]
                  }
                ]
              }
            }
          }
        ]
      }
    }
    

    mysql2Mysql.json 存放到哪都可以,推薦大家放到 DataXjob 目錄下,方便管理。配置不算複雜,相信大家都能看懂

  2. 啟動 DataX 進行同步

    DataXbin 目錄下啟動命令列視窗,然後執行如下命令

    python datax.py ../job/mysql2Mysql.json

當我們看到如下輸出,就說明同步成功了

簡單使用

需要說明的是

DataX 不支援表結構同步,只支援資料同步,所以同步的時候需要保證目標表已經存在

column

指的就是 job.jsonreaderwriter 節點下的 column ,配置需要同步的列名集合;可以配置表的列名,也可以配置常量、表示式,還可以配置 * ,但不推薦配置 *,因為它不便於我們檢視列之間的對映關係

ReaderWriter 之間的列是根據順序進行對映的,而非根據欄位名進行對映的,以前面的 mysql2Mysql.json 為例,欄位的對映關係如下所示

列對映

相當於是根據陣列的索引進行對映的,reader_column[n] 對映 writer_column[n],那麼問題來了,如果列數不對應會怎麼樣

  1. Reader 列數比 Writer

    去掉 Writer 的列 pw,然後執行下同步任務,會發現同步異常,提示如下資訊

    列配置資訊有錯誤. 因為您配置的任務中,源頭讀取欄位數:4 與 目的表要寫入的欄位數:5 不相等. 請檢查您的配置並作出修改.

  2. Reader 列數比 Writer

    同樣會同步異常,提示資訊類似如下

    列配置資訊有錯誤. 因為您配置的任務中,源頭讀取欄位數:4 與 目的表要寫入的欄位數:5 不相等. 請檢查您的配置並作出修改.

如果列數一致,但列的順序沒有正確對映,會出現什麼情況

  1. 同步異常

    你們是不是有這樣的疑問:列數一樣,怎麼會同步異常?因為存在列型別不匹配,導致資料插不進去,例如我將 Writer 中的 username birth_day 對調下位置,然後執行同步,會發現同步異常,異常資訊類似如下

    Date 型別轉換錯誤

  2. 同步正常,資料卻亂了

    對調下 Writerusernamepw

    datax_使用細節-同步正常_資料亂了

    執行同步任務,會發現同步沒有出現異常,但你們看一眼目標資料來源的資料

    列數相同_資料亂了

    很明顯髒資料了,這算同步成功還是同步失敗?

    示例的髒資料很容易能夠看出來,如果出現兩列很類似的資料,那就麻煩了,等待我們的就是長夜漫漫的 bug 排查之旅

    哪個二臂寫的程式碼

table

Reader 表示從哪讀資料,在 Writer 表示往哪寫資料;ReaderWriter 都支援配置多個表,但需要保證這些表是同一 schema 結構

個人非常不推薦一個 job 配置多個 table,而是一個 job 一個 table,如果需要同步多個 table,那就配置多個 job

splitPk

這個配置只針對 Reader

Reader 進行資料抽取時,如果指定了 splitPk,那麼 DataX 會按 splitPk 配置的欄位進行資料分片,啟動併發任務進行資料同步,從而提高同步的效率

那問題又來了,分成多少片了?我已經給大家總結好了

  1. 若未配置 splitPk,則一個 table 對應一個 task

  2. 配置了 splitPktable 只要 1 個,則分成 job.setting.speed.channel * reader.parameter.splitFactor 片,每片對應一個 task

    splitFactor 未配置的情況下,其預設值是 5

  3. 配置了 splitPk,且 table 多餘 1 個,則對每個 table 分成 job.setting.speed.channel 片,每片對應一個 task

    不推薦大家在一個 job 中配置多個表,所以這種情況瞭解就好

比較可惜的是,目前 splitPk 僅支援整形資料切分,否則會報錯

我們對 mysql2Mysql.json 進行下 splitPk 改造,調整如下 2 項,其他不動

  1. job.setting.speed.channel 調整成 2
  2. reader 節點下新增 2 配置項
    • reader.parameter.splitPk="id"
    • reader.parameter.splitFactor=2

執行同步任務,能看到如下日誌

splitPk_日誌

仔細看 allQuerySql,4 條 SQL 代表 4 個分片,這個我相信你們都能理解,但是 where id IS NULL 這條 SQL 是什麼意思?其實我們仔細思考下就明白了,我們之所以認為 where id IS NULL 沒必要存在的原因是我們知道 id 是主鍵,但 DataX 知道嗎,它不知道,所以需要 where id IS NULL 來保證資料不被遺漏。不過話說回來,資料量少的時候,不分片效率比分片要高,這又回到了那個老生常談的問題了

多執行緒一定比單執行緒效率高嗎

where

同樣只針對 Reader

SQL 中的 WHERE 一樣,是篩選條件,Reader 根據 columntablewhere 拼接 SQL,然後用這個拼接好的 SQL 進行資料抽取。示例演示之前,我們記得將 mysql2Mysql.json 還原成最初的樣子,然後補上 where 條件

{
  "job": {
    "setting": {
      "speed": {
        "channel": 3
      },
      "errorLimit": {
        "record": 0,
        "percentage": 0.02
      }
    },
    "content": [
      {
        "reader": {
          "name": "mysqlreader",
          "parameter": {
            "username": "root",
            "password": "123456",
            "column": [
              "id",
              "username",
              "password",
              "birth_day",
              "remark"
            ],
            "connection": [
              {
                "jdbcUrl": [
                  "jdbc:mysql://192.168.2.118:3307/qsl_datax?useUnicode=true&characterEncoding=utf-8"
                ],
                "table": [
                  "qsl_datax_source"
                ]
              }
            ],
			"where": "id < 3"
          }
        },
        "writer": {
          "name": "mysqlwriter",
          "parameter": {
            "writeMode": "insert",
            "username": "root",
            "password": "123456",
            "column": [
              "id",
              "username",
              "pw",
              "birth_day",
              "note"
            ],
            "connection": [
              {
                "jdbcUrl": "jdbc:mysql://192.168.2.118:3306/qsl_datax_sync?useUnicode=true&characterEncoding=utf-8",
                "table": [
                  "qsl_datax_target"
                ]
              }
            ]
          }
        }
      }
    ]
  }
}

執行同步程式,會在日誌中看到如下資訊

where日誌

如果再加上 splitPk ,你們能想到 DataX 的處理邏輯嗎,我給你們看一段日誌

where_splitPk_日誌

這段日誌,你們看明白了嗎

如果不配置 where 或者 where 的值配置空,那麼就相當於全量同步;如果正常配置了 where 則相當於增量同步,而這個增量同步是在實際專案中用的比較多的。一旦涉及得到增量,我們是不是得把增量列的值以變數的形式傳入值,而 DataX 正好實現了該功能,類似如下進行配置

"where": "id > $startId"

透過啟動命令來傳入變數值,類似如下

python datax.py ../job/mysql2Mysql.json -p"-DstartId=1"

同步任務出現如下日誌,說明變數的值傳入正常

where增量日誌

再結合排程平臺,那麼定時增量同步就實現了

有興趣的可以去看看 datax-web

querySql

只針對 Reader

tablewhere 能配置的篩選條件還是比較有限,join 也沒法滿足,所以 querySql 應運而生。querySql 允許使用者自定義篩選 SQL

當使用者配置 querySql 時,Reader 直接忽略 tablecolumnwhere 條件的配置,querySql 優先順序大於tablecolumnwhere 選項

tablequerySql 只能配置一個,不能同時配置

querySql 同樣支援變數,類似如下

{
  "job": {
    "setting": {
      "speed": {
        "channel": 3
      },
      "errorLimit": {
        "record": 0,
        "percentage": 0.02
      }
    },
    "content": [
      {
        "reader": {
          "name": "mysqlreader",
          "parameter": {
            "username": "root",
            "password": "123456",
			"splitPk": "id"
			"splitFactor": 2
            "connection": [
              {
				"querySql": ["select id,username,password,birth_day, 'remark' AS remark from qsl_datax_source where id > $startId"]
                "jdbcUrl": [
                  "jdbc:mysql://192.168.2.118:3307/qsl_datax?useUnicode=true&characterEncoding=utf-8"
                ],
              }
            ]
          }
        },
        "writer": {
          "name": "mysqlwriter",
          "parameter": {
            "writeMode": "insert",
            "username": "root",
            "password": "123456",
            "column": [
              "id",
			  "username",
              "pw",
              "birth_day",
              "note"
            ],
            "connection": [
              {
                "jdbcUrl": "jdbc:mysql://192.168.2.118:3306/qsl_datax_sync?useUnicode=true&characterEncoding=utf-8",
                "table": [
                  "qsl_datax_target"
                ]
              }
            ]
          }
        }
      }
    ]
  }
}

同步日誌中會出現如下資訊

querySql日誌

大家可以看到,如果配置了 querySql,那麼 splitPk 配置就不生效了

總結

  1. 大家如果細心的話,會發現我講得基本都是關於 Reader ,實際也確實是 Reader 配置要複雜很多,至於 Writer 配置嘛,我相信你們都能看懂,也都會配置,我就不嘮叨了
  2. column 不推薦配置 *,推薦配列名,能更直觀的反應對映關係
  3. table 模式下,單 job 推薦只配一個 table,如果是同步多個 table, 推薦配置多個 job
  4. splitPk 只支援 table 模式,實現分片併發獲取資料,提高查詢效率,但這不是絕對的,小資料量的情況下,可能單任務效率更高
  5. where 只支援 table 模式,給查詢增加過濾條件,支援變數,可以實現增量同步
  6. querySql 模式下,table 模式不能配置,否則異常,columnwheresplitPk 即使配置了也不生效;querySql 可以實現使用者自定義 SQL,非常靈活,join 查詢就可以用 querySql 來實現

相關文章