DolphinScheduler 調度 DataX 實現 MySQL To ElasticSearch 增量數據同步實踐

數據同步的方式

數據同步的2大方式

  • 基於SQL查詢的 CDC(Change Data Capture):
    • 離線調度查詢作業,批處理。把一張表同步到其他系統,每次通過查詢去獲取表中最新的數據。也就是我們說的基於SQL查詢抽取;
    • 無法保障數據一致性,查的過程中有可能數據已經發生了多次變更;
    • 不保障實時性,基於離線調度存在天然的延遲;
    • 工具軟件以Kettle(Apache Hop最新版)、DataX爲代表,需要結合任務調度系統使用。
  • 基於日誌的 CDC:
    • 實時消費日誌,流處理,例如 MySQL 的 binlog 日誌完整記錄了數據庫中的變更,可以把 binlog 文件當作流的數據源;
    • 保障數據一致性,因爲 binlog 文件包含了所有歷史變更明細;
    • 保障實時性,因爲類似 binlog 的日誌文件是可以流式消費的,提供的是實時數據;
    • 工具軟件以Flink CDC、阿里巴巴Canal、Debezium爲代表。

基於SQL查詢增量數據同步原理

我們考慮用SQL如何查詢增量數據? 數據有增加、修改、刪除 刪除數據採用邏輯刪除的方式,比如定義一個is_deleted字段標識邏輯刪除 如果數據是 UPDATE的,也就是會被修改的,那麼 where update_datetime >= last_datetime(調度滾動時間)就是增量數據 如果數據是 APPEND ONLY 的除了用更新時間還可以用where id >= 調度上次last_id

結合任務調度系統 調度時間是每日調度執行一次,那麼 last_datetime = 當前調度開始執行時間 - 24小時,延遲就是1天 調度時間是15分鐘一次,那麼 last_datetime = 當前調度開始執行時間 - 15分鐘,延遲就是15分鐘

這樣就實現了捕獲增量數據,從而實現增量同步

DolphinScheduler + Datax 構建離線增量數據同步平臺

本實踐使用 單機8c16g DataX 2022-03-01 官網下載 DolphinScheduler 2.0.3(DolphinScheduler的安裝過程略,請參考官網)

DolphinScheduler 中設置好DataX環境變量 DolphinScheduler 提供了可視化的作業流程定義,用來離線定時調度DataX Job作業,使用起來很是順滑

基於SQL查詢離線數據同步的用武之地 爲什麼不用基於日誌實時的方式?不是不用,而是根據場合用。考慮到業務實際需求情況,基於SQL查詢這種離線的方式也並非完全淘汰了 特別是業務上實時性要求不高,每次調度增量數據沒那麼大的情況下,不需要分佈式架構來負載,這種情況下是比較合適的選擇 場景舉例: 網站、APP的百萬級、千萬級的內容搜索,每天幾百篇內容新增+修改,搜索上會用到ES(ElasticSearch),那麼就需要把 MySQL內容數據增量同步到ES DataX就能滿足需求!

DolphinScheduler中配置DataX MySQL To ElasticSearch工作流

工作流定義

工作流定義 > 創建工作流 > 拖入1個SHELL組件 > 拖入1個DATAX組件 SHELL組件(文章) 腳本

echo '文章同步 MySQL To ElasticSearch'

DATAX組件(t_article) 用到2個插件mysqlreader、elasticsearchwriter^[1] 選 自定義模板:

{
    "job": {
        "content": [
            {
                "reader": {
                    "name": "mysqlreader",
                    "parameter": {
                        "connection": [
                            {
                                "jdbcUrl": [
                                    "jdbc:mysql://${biz_mysql_host}:${biz_mysql_port}/你的數據庫?useUnicode=true&zeroDateTimeBehavior=convertToNull&characterEncoding=UTF8&autoReconnect=true&useSSL=false&&allowLoadLocalInfile=false&autoDeserialize=false&allowLocalInfile=false&allowUrlInLocalInfile=false"
                                ],
                                "querySql": [
                                    "select a.id as pk,a.id,a.title,a.content,a.is_delete,a.delete_date,a.create_date,a.update_date from t_article a.update_date >= '${biz_update_dt}';"
                                ]
                            }
                        ],
                        "password": "${biz_mysql_password}",
                        "username": "${biz_mysql_username}"
                    }
                },
                "writer": {
                    "name": "elasticsearchwriter",
                    "parameter": {
                        "endpoint": "${biz_es_host}",
                        "accessId": "${biz_es_username}",
                        "accessKey": "${biz_es_password}",
                        "index": "t_article",
                        "type": "_doc",
                        "batchSize": 1000,
                        "cleanup": false,
                        "discovery": false,
                        "dynamic": true,
                        "settings": {
                            "index": {
                                "number_of_replicas": 0,
                                "number_of_shards": 1
                            }
                        },
                        "splitter": ",",
                        "column": [
                            {
                                "name": "pk",
                                "type": "id"
                            },
                            {
                                "name": "id",
                                "type": "long"
                            },
                            {
                                "name": "title",
                                "type": "text"
                            },
                            {
                                "name": "content",
                                "type": "text"
                            }
                            {
                                "name": "is_delete",
                                "type": "text"
                            },
                            {
                                "name": "delete_date",
                                "type": "date"
                            },
                            {
                                "name": "create_date",
                                "type": "date"
                            },
                            {
                                "name": "update_date",
                                "type": "date"
                            }
                        ]
                    }
                }
            }
        ],
        "setting": {
            "errorLimit": {
                "percentage": 0,
                "record": 0
            },
            "speed": {
                "channel": 1,
                "record": 1000
            }
        }
    }
}

reader和writer的字段配置需保持一致

自定義參數:

biz_update_dt: ${global_bizdate} 
biz_mysql_host: 你的mysql ip
biz_mysql_port: 3306
biz_mysql_username: 你的mysql賬號
biz_mysql_password: 你的mysql密碼
biz_es_host: 你的es地址帶協議和端口 http://127.0.0.1:9200
biz_es_username: 你的es賬號
biz_es_password: 你的es密碼

配置的自定義參數將會自動替換json模板中的同名變量

reader mysqlreader插件中關鍵配置: a.update_date >= '${biz_update_dt}' 就是實現增量同步的關鍵配置 writer elasticsearchwriter插件中關鍵配置: ``

"column": [
	{
		"name": "pk",
		"type": "id"
	},
	......
]

type = id 這樣配置,就把文章主鍵映射到es主鍵 _id 從而實現相同主鍵id重複寫入數據,就會更新數據。如果不這樣配置數據將會重複導入es中

保存工作流

全局變量設置 global_bizdate: $[yyyy-MM-dd 00:00:00-1]

global_bizdate 引用的變量爲 DolphinScheduler 內置變量,具體參考官網文檔^[2] 結合調度時間設計好時間滾動的窗口時長,比如按1天增量,那麼這裏時間就是減1天

最終的工作流DAG圖爲:

by 流水理魚|wwek

參考

1. DataX ElasticSearchWriter 插件文檔 2. Apache DolphinScheduler 內置參數 本文首發於流水理魚博客,如要轉載請註明出處。 歡迎關注我的公衆號:流水理魚(liushuiliyu),全棧、雲原生、Homelab交流。 如果您對相關文章感興趣,也可以關注我的博客:www.iamle.com 上面有更多內容

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