Flink CDC 在京東的探索與實踐

ApacheFlink發表於2023-04-12

來源:Apache Flink

摘要:本文整理自京東資深技術專家韓飛,在 Flink Forward Asia 2022 資料整合專場的分享。本篇內容主要分為四個部分:


    1. 京東自研 CDC 介紹
    2. 京東場景的 Flink CDC 最佳化
    3. 業務案例
    4. 未來規劃



01

京東自研 CDC 介紹


Flink CDC 在京東的探索與實踐

京東自研 CDC 代號為 Fregata,是我們針對資料實時採集和分發場景自研的底層框架。Fregata 是一種動物,叫做軍艦鳥,它是世界上飛行速度最快的鳥,即使在惡劣天氣下也能保持很好的飛行能力及機動性,寓意我們整個實時採集、分發服務的高效穩定。


目前,Fregata 是京東集團資料中臺實時採集和分發的統一入口,服務京東零售、物流、科技、健康和工業等 BGBU,覆蓋訂單交易、商智黃金眼、實時風控、京東白條、實時大屏等核心業務。


Flink CDC 在京東的探索與實踐

目前 Fregata 線上穩定執行任務超過兩萬,大促處理條數峰值為 64.1 億條/min,這個是採集和分發的總資料條數,對應的傳輸資料量峰值為 8.3TB/min。


針對單資料庫例項的採集能力超過 500w 條/min,遠超資料庫主從同步的速率。


Flink CDC 在京東的探索與實踐

Fregata 任務目前總計使用 CPU 資源超過 6 萬核,記憶體使用超過 18wGB。


我們基於京東 JDOS 平臺實現了 Fregata 任務的容器化部署和執行,並且支援任務的跨機房部署,目前任務主要分佈匯天和廊坊兩個機房,兩個機房互為主備。


容災方面,支援任務的一鍵容災切換,如果出現機房大面積故障或者斷網等情況,可以快速將任務切換到備機房,從而保證任務的快速恢復和穩定執行。


Flink CDC 在京東的探索與實踐

上圖左側主要展示了 Fregata 的整體架構。

首先,Fregata 按照功能分為實時採集和實時分發兩部分,實時採集基於資料庫主從複製原理,實時捕獲 Binlog 資料進行解析並按照一定的格式進行封裝,然後傳送到京東自研訊息佇列 JDQ 中,供下游業務實時消費,目前支援的源端資料庫型別有物理 MySQL,京東自研彈性資料庫 JED、京東雲 RDS、京東數科 CDS 及 Oracle,其中 Oracle 是透過 Logminer 來實現對資料庫日誌的實時採集。


實時分發部分,主要是將 JDQ 中多種格式的資料實時同步到不同的目標儲存中,目前支援的訊息格式有 CSV/JSON/ProtoBuf/Xml/Avro 等, 目前支援的目標儲存有 HDFS 或者 Hive(對應離線數倉),OLAP 分析引擎包括 Doris 和 ClickHouse,訊息佇列 JDQ,ElasticSearch 及資料湖儲存 Iceberg。支援的資料來源端、目標端都會根據實際需求不斷進行豐富。


Fregata 做採集和分發的拆分這樣的設計主要是基於“一次採集、多次分發”的思路,這樣的好處是既可以減少對上游資料庫的負載,又可以滿足業務多次消費、多種不同型別消費、以及短期內資料回放的需求,這裡 JDQ 資料一般儲存 7 天。


上圖右側主要展示了 Fregarat 引擎的設計框架,整個引擎主要分為三層,分別是 Source、Parse、Sink 運算元,每層運算元之間透過 RingBuffer 進行連結(我們選用的 disruptor)。


  • Source 運算元根據資料來源型別的不同實現源端資料的拉取並推到 RingBuffer 中。

  • Parse 運算元從 RingBuffer 中拉取資料,對資料進行解析組裝和一些 ETL 加工,然後將資料傳送到下游的 RingBuffer 中。

  • Sink 運算元拉取下游 RingBuffer 中的資料並按照目標資料來源的要求進行一定的資料格式的組裝,然後傳送到不同的目標儲存上。


此外,還有一個 BarrierService 定時產生 Barrier,整個任務透過 Barrier 服務來完成狀態的提交和記錄,其原理跟 Flink 中 Checkponit 機制類似。BarrierService 定時產生 Barrier 並傳遞給 Source 運算元,Source 運算元在拿到 Barrier 之後以廣播的形式傳遞給下游的 Parse,下游的 Parse 拿到 Barrier 之後再以廣播的形式傳遞給所有的 Sink 運算元,當每個 Sink 運算元收到所有 Barrier 之後會向 BarrierService 進行 ack 操作,這時 BarrierService 會進行一系列的狀態提交,例如提交消費位點、記錄的 Binlog 位置等。


Flink CDC 在京東的探索與實踐

我們接著看 Fregata 的技術特性,首先是關於 Binlog 的位點追蹤。


上圖右側主要介紹了實時採集任務啟動執行的整個流程。其中位點服務中記錄任務上次已經消費的 Binlog 位點資訊,主要包括 Binlog 檔名稱,該 Binlog 檔案已經消費到的位置,資料庫例項的 serverid,該 Binlog 位置對應的事務產生時間以及 GTID 資訊。


採集任務啟動時會向位點服務獲取上次記錄的 Binlog 位點資訊,然後將記錄的 BinlogPosition 或者 GTID 資訊傳遞給 Binlog Connector,Binlog Connector 根據 BinlogPostion 或者 GTID 資訊生成 dump 命令併傳送給資料庫例項,然後資料庫例項將 Binlog 日誌推送給 Binlog Connector,Binlog Connector 將接受的 Binlog 日誌進行反序列化並封裝成 Binlog Event 傳遞給 Fregata,Fregata 對 Binlog Event 進行相關處理後傳送給 JDQ。


由於 MySQL 在 5.6.5 版本之後才有 GTID,並且京東線上業務庫存在資料庫版本較低的現象,因此 Fregata 對 BinlogPosition 和 GTID 兩種方式都進行了支援,並且支援從指定時間點、最新位點、起始位點以及指定 Binlog 位置,多種消費模式靈活配置。


此外,當上遊資料庫版本升級至高版本並開啟了 GTID 後,就存在採集任務需要從 BinlogPosition 模式切換成 GTID 模式的場景,所以 Fregata 也支援了任務的位點模式在 BinlogPosition 和 GTID 之間自動切換的功能,並且在切換的過程中保證資料不丟不重。


切換過程如上圖中左下角所示,首先任務從 BinlogPosition 模式重啟,然後查詢並快取在這個重啟過程中已經執行的 GTID 事務。接著任務會先以 BinlogPosition 模式繼續處理 Binlog 中的 GTID EVENT,並判斷前邊快取的 GTID 中是否包含當前已消費的 GTID,如果不包含,則說明消費進度已經追上,此時任務將位點記錄模式直接切換成 GTID 模式。


Flink CDC 在京東的探索與實踐

接著介紹 Fregata 動態感知相關的功能。Fregata 實時採集任務配置是資料庫域名,如果線上資料庫存在故障或者要下線,則會有資料庫例項需要發生變更的場景,Fregata 是可以感知到變更並自動進行切換的。


由於切換前後兩個資料庫例項 Binlog 檔案一般都是不一致的,如果此時任務位點記錄方式是 BinlogPosition 模式,則在切換之後任務需要自動進行 Binlog 對齊操作,進而保證資料的完整性。(GTID 模式是不需要考慮這個問題的)


整個切換過程如上圖右側所示,BinlogPosition 模式下,任務會查詢出新資料例項上全部的 Binlog 檔案,並按照倒序對 Binlog 檔案進行遍歷,然後根據位點服務中記錄的時間戳查詢出對應的 Position,然後任務從查詢出的該 Position 繼續消費。這種倒序查詢的方式主要是針對線上切庫的場景,這種情況下采用倒敘的查詢效率比較高,一般查詢 1-2 分鐘前的 Binlog 即可。


Fregata 動態感知能力還體現在 DDL 變更的感知上,Fregata 能夠識別資料庫中的 DDL 操作並自動進行適配,目前支援的 DDL 變更型別包括,比如新增、刪減欄位,修改欄位型別、欄位順序調整等。


由於下游業務方也會關注資料庫的 DDL 操作,因此 Fregata 在識別到 DDL 操作時,還會自動以郵件或者語音的方式通知管理員及使用者進行關注。


Flink CDC 在京東的探索與實踐

Fregata 也具備一些資料加工及豐富的能力。


Fregata 在採集 Binlog 的過程中,會對每一條記錄增加一個唯一的版本號 Mid(也就是 message id),下游使用者可以根據這個版本號進行去重或者確定最新的變更記錄,比如當將增量資料分發到 Hive 或者其他無主鍵約束的儲存中時,使用者可以根據 Mid 來確定對於同一個主鍵的多條操作記錄,哪條是最新的變更操作。


此外,Fregata 還會將資料庫、表及資料庫例項等資訊作為後設資料封裝到每條訊息體中,方便下游有相關需求的業務用於判斷資料的來源。


在資料加工方面,採集過程中還支援使用多種函式對資料進行加工處理,如敏感欄位加解密、型別轉換、時間轉換等。


在部署方面,如果上游業務庫是分庫分表模式並覆蓋多個例項,Fregata 將會根據資料庫例項個數啟動多個採集任務,採集任務和資料庫例項一一對應。


這樣的好處是任務相互獨立並且資源隔離,單一資料庫例項的變更不影響其他資料庫例項的採集任務,劣勢是如果例項數量較多,配置和維護成本會略高;配置方面,我們透過產品化流程解決這個問題,實現一次配置。


Flink CDC 在京東的探索與實踐

告警方面,Fregata 支援任務存活告警,在任務存活異常的情況下,運維人員會收到語音或者郵件報警資訊。同時,採集任務會按分鐘粒度上報採集延遲、資料庫主從延遲和抽取零值的這些監控指標資訊,供使用者觀測任務執行情況。


全增量資料支援方面,Fregata 目前只支援增量資料的抽取,全量資料的抽取依賴 Binlog 保留時間。


換句話說,如果 Binlog 資料全量保留,則可以抽取全部資料,否則,只能抽取儲存的 Binlog 資料,其他更早的歷史資料需要離線抽取來補償。


02

京東場景的 Flink CDC 最佳化


上邊是關於 Fregata 的內容,整體來講,目前我們對於 Flink CDC 的使用還處在一個多方面驗證和相對初級的階段。針對京東內部的場景,我們在 Flink CDC 中適當補充了一些特性來滿足我們的實際需求。所以接下來一起看下京東場景下的 Flink CDC 最佳化。


Flink CDC 在京東的探索與實踐

在實踐中,會有業務方提出希望按照指定時間來進行歷史資料的回溯,這是一類需求;還有一種場景是當原來的 Binlog 檔案被全部清理,這時需要重置到新產生的 Binlog 檔案上。


針對上述場景,我們透過複用 scan.startup.mode 引數,擴充套件 earliest-offset\timestamp\specific-offset 三種 Binlog 階段的啟動模式。


其中 specific-offset 模式下,需要設定 scan.startup.specific-offset.file 引數指定 Binlog 檔名稱、scan.startup.specific-offset.pos 指定該檔案的某一個位置,根據這兩個引數來確定增量階段要消費的起始位置;earliest-offset 模式下預設會讀取最早的 Binlog 檔案;timestamp 模式,需要設定一個時間引數 scan.startup.timestamp-millis。


如上圖右側所示,在 timestamp 啟動模式下,會根據使用者指定的時間按照倒序的方式去查詢相應的 Binlog 檔案以及 Position,最終底層模式完全複用 specific-offset 的方式。


不管使用哪種模式,最終都會根據不同的啟動模式構建正確的 Start Binlog Offset,並進一步構建 MySQLBinlogSplit。


Flink CDC 在京東的探索與實踐

在低版本 MySQL 的生產中,會存在資料庫例項下線,或者從庫存在顯著的主從延遲(需遷移至其他從庫);在這兩種場景下,一般會進行切庫操作。如何實現自動切庫呢?或者說如何實現在低版本 MySQL 的 Binlogposition 模式下的自動切庫呢?


如上圖右側所示,我們增加了一步 切庫檢查的操作:


首先,在 MySQLBinlogsplit 中增加了對 MySQL 層面的 serverid 資訊的儲存,並修改了 state 儲存&恢復過程中對 MySQLSplitBinlog 物件的處理邏輯。


然後,查詢 MySQL 例項獲取 serveid,並與 MySQLBinlogsplit 物件中儲存的 serverid 進行對比。


如果不一致, 則認為發生切庫操作,此時需要根據 Binlogoffset 儲存的消費位點的時間資訊,也就是 timestamp,在新庫中倒序查詢並重新構建 start Binlogoffset 以及進一步構建 MySQLBinlogsplit。


Flink CDC 在京東的探索與實踐

當前 Flink MySQL CDC 支援採集時延、傳送時延、空閒時長的監控指標,在實際生產中,使用者反饋有需要關注上游資料庫主從延遲的需求。同時,所有監控指標都存在視覺化及異常報警需求。


基於上述情況,首先我們新增了資料庫主從延遲的監控指標,並將所有這些監控指標對接到監控系統 Byzer。如上圖所示,整體流程是這樣,Flink JobManager 和 TaskManager 啟動時會攜帶 agent,會透過 agent 將監控資料傳送到 Byzer 系統。


使用者可以在 JRC 平臺(實時計算平臺)配置監控報警規則,這些規則會被同步到 Byzer 系統。另一方面,JRC 平臺會拉取 Byzer 監控系統資料並進行視覺化展示。


Flink CDC 在京東的探索與實踐

最後來看一個偏應用層面的改造,在實際的業務中大量存在分庫分表的場景,並且線上分庫分表基本會分佈在多個 MySQL 例項中。


社群版本 Flink MySQL CDC 如果要在一個作業中支援多例項,需要使用者多次複製 DDL 定義語句並修改 hostname 配置,如果例項數量多的話是比較影響使用者體驗及 SQL 的可讀性。


對此,我們結合平臺實現了多例項的支援。透過 calcite 解析使用者的 SQL 語句,找到 MySQL-cdc 的 DDL 定義,並解析其中 hostname 欄位來判斷是否包含多例項,也就是配置了多個 host。如果包含多個例項,則自動按例項分割,建立不同例項對應的表,最後再 union 為一個檢視。如圖中藍色卷軸示例所示,此時只需要做一次 DDL 的定義。


此外,在採集多例項,寫帶 Primary Key 的 Sink 場景中,我們做了一個最佳化。由於 Flink MySQL CDC 進入 Binlog 階段後只會在 Source 運算元的第一個 subtask 中執行任務,而 Primary Key Sink 會觸發 Flink 引擎最佳化 Sink 運算元增加 NotNullEnforcer 運算元來檢查資料相關的 not null 的欄位,然後再進行 hash 分發到 SinkMaterializer 運算元以及後面的 Sink 運算元。


由於 Source 與 NotNullEnforcer 之間是 forward 關係,因此 NotNullEnforcer 也只有一個 task 處理資料,這在 Source 較多的場景下處理效能可能是不夠的。


為充分利用 NotNullEnforcer 運算元的並行度,我們增加了 table.exec.sink.not-null-enforcer.hash 引數,然後在 commonExecSink 中增加 透過該引數來判斷是否要加速 NotNullEnforcer 運算元 這樣的邏輯。如果開啟加速,則提前使用 Primary Key 進行 hash,然後再分發到 NotNullEnforcer 運算元,從而實現對 NotNullEnforcer 運算元的最佳化。


Flink CDC 在京東的探索與實踐

來看下最佳化前後的對比。


第一個圖中可以看到,如紅框所示,NotNullEnforcer 運算元中只有第一個 Task 在處理資料。


最佳化後,在第二個圖中,可以看到 NotNullEnforcer 運算元的所有 10 個並行度都被利用了起來,並且 Source 運算元和 NotNullEnforcer 運算元之間是 hash 關係。


03

業務案例


在這個案例中,我們結合 Flink CDC、Flink 核心計算能力以及資料湖 Hudi,對我們平臺的一個業務方,京東物流的一個業務資料系統進行了技術架構的試點改造。


Flink CDC 在京東的探索與實踐

這個系統是物流運營資料中心 LDC 中的中小件實時運營監控系統。它在整個京東物流內部被高頻使用,不論是管理者用於決策,還是一線人員用於精細化進度管理。


它覆蓋物流的三大核心操作環節,攬收、分揀、配送, 並在不同的維度進行下鑽,來提供物流各環節操作單量的監控以及視覺化。


Flink CDC 在京東的探索與實踐

上游是彈性資料庫 JED,分庫分表並且分佈在多個例項上。


在上邊的離線鏈路中,首先透過 plumber 將資料抽取到離線數倉的 BDM 層,plumber 是京東離線異構資料交換的基礎服務,負責將不同資料來源的資料抽取至數倉或者將數倉計算結果推送到不同的資料來源中。


在資料抽取到 BDM 層後,資料會經過 FDM 層的拉鍊以及後邊幾層的資料加工,最後業務資料的結果彙總至 APP 層,再透過 plumber 將結果推送至 ES 中,LDC 的使用者使用的產品底層查詢 ES。還存在另外一種方式,OLAP 引擎 StarRocks 會匯入 app 層的資料,然後供使用者查詢。


下邊實時鏈路中,Fregata 採集資料庫 Binlog 傳送至 JDQ,Flink 消費 JDQ 資料繼續寫入 JDQ,以此往復,對應於離線數倉的分層邏輯,構建了基於 JDQ 的實時數倉, 最終的結果透過一個叫 syncer 的同步工具,將資料從 JDQ 同步到 ES 和 StarRocks 中。


同時,還存在另一條鏈路,最上游的 JDQ 透過 Fregata 直接分發資料到離線的 BDM 層,構建準實時的 BDM 表。整體來看,屬於典型的 Lambda 資料架構。

當前的架構存在幾個痛點:


  • 離線鏈路存在 SLA 撞線的問題,當上遊鏈路計算資源擁擠或者出現異常重試的情況時資料的時效性有可能不如按時達成。

  • ES 伺服器的儲存成本比較高,一年在 100 萬左右。

  • 典型 Lambda 架構的一些問題,由於流批割裂導致的伺服器資源無法複用,技術棧不同,開發效率低,資料口徑不一致等問題。


Flink CDC 在京東的探索與實踐

由於這個業務的實時資料接受端到端分鐘級別的時延,因此對這個資料架構做了些改造。


首先基於我們改造後的 Flink CDC 能力, 實現了一個 Flink 作業,對上游多例項的 JED 分庫分表資料,進行全增量一體化採集。


在資料加工層面,結合 FlinkSQL,為使用者提供了低程式碼的開發方式,也就是拖拽+SQL,計算的結果寫入資料湖 Hudi。


然後再基於 Hudi 的增量讀取能力,進一步加工,完成 FDM、GDM、APP 等不同層的加工邏輯,結果透過 StarRocks 掛載 Hudi 外部表 ,提供給終端 LDC 使用者查詢。透過這樣的改造,最終構建了一條端到端準實時的資料鏈路。


總結:首先,結合 Flink CDC、Flink 核心計算能力及 Hudi 首次實現端到端流批一體。可以看到,覆蓋採集、儲存、計算三個環節。最終這個鏈路是端到端分鐘級別資料時延(2-3min),資料時效的提升有效驅動了新的業務價值,例如對於物流履約達成以及使用者體驗的提升。資料時效成本方面,解決離線撞線問題,一條準實時鏈路,不存在離線撞線;Hudi+StarRocks 的組合成本相較於 ES 顯著降低(經評估,約為原來的 1/3)。相較於 Lambda 架構,在伺服器成本、開發效率及資料質量方面都有顯著的提升。


04

未來規劃


Flink CDC 在京東的探索與實踐

未來規劃包含以下幾個方面:


  • 嘗試實現任務不停止的 Schema Evolution。例如針對 Hudi、針對 JDQ。

  • 繼續基於京東場景的 Flink CDC 改造。比如資料加密、全面對接實時計算平臺 JRC 等。

  • 嘗試將部分 Fregata 生產任務切換 Flink CDC。好處是技術棧統一,符合整體技術收斂的趨勢。

  • 結合流批一體的儲存來提升端到端的整體時效性。例如結合 Table Store 去嘗試實現端到端更低的,例如秒級別的時延。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70024923/viewspace-2945256/,如需轉載,請註明出處,否則將追究法律責任。

相關文章