使用 Flink Hudi 構建流式資料湖平臺

ApacheFlink發表於2022-02-23

摘要:本文整理自阿里巴巴技術專家陳玉兆 (玉兆)、阿里巴巴開發工程師劉大龍 (風離) 在 Flink Forward Asia 2021 的分享。主要內容包括:

  1. Apache Hudi 101
  2. Flink Hudi Integration
  3. Flink Hudi Use Case
  4. Apache Hudi Roadmap

FFA 2021 直播回放 & 演講 PDF 下載

一、Apache Hudi 101

提到資料湖,大家都會有這樣的疑問,什麼是資料湖?為什麼資料湖近兩年熱度很高?資料湖其實不是一個新的概念,最早的資料湖概念在 80 年代就已經提出,當時對資料湖的定義是原始資料層,可以存放各種結構化、半結構化甚至非結構化的資料。像機器學習、實時分析等很多場景都是在查詢的時候確定資料的 Schema。

img

湖儲存成本低、靈活性高的特性,非常適用於做查詢場景的中心化儲存。伴隨著近年來雲服務的興起,尤其是物件儲存的成熟,越來越多的企業選擇在雲上構建儲存服務。資料湖的存算分離架構非常適合當前的雲服務架構,通過快照隔離的方式,提供基礎的 acid 事務,同時支援對接多種分析引擎適配不同的查詢場景,可以說湖儲存在成本和開放性上佔了極大優勢。

img

當前的湖儲存已經開始承擔數倉的功能,通過和計算引擎對接實現湖倉一體的架構。湖儲存是一種 table format,在原有的 data format 基礎上封裝了 table 的高階語義。Hudi 從 2016 年開始將資料湖投入實踐,當時是為了解決大資料場景下檔案系統上的資料更新問題,Hudi 類 LSM 的 table format 當前在湖格式中是獨樹一幟的,對近實時更新比較友好,語義也相對完善。

Table format 是當前流行的三種資料湖格式的基礎屬性,而 Hudi 從專案之初就一直朝著平臺方向去演化,擁有比較完善的資料治理和 table service,比如使用者在寫入的時候可以併發地優化檔案的佈局,metadata table 可以大幅優化寫入時查詢端的檔案查詢效率。

下面介紹一些 Hudi 的基礎概念。

img

Timeline service 是 Hudi 事務層的核心抽象,Hudi 所有資料操作都是圍繞著 timeline service 來展開的,每次操作通過 instant 抽象繫結一個特定的時間戳,一連串的 instant 構成了 timeline service,每一個 instance 記錄了對應的 action 和狀態。通過 timeline service,Hudi 可以知道當前表操作的狀態,通過一套檔案系統檢視的抽象結合 timeline service,可以對 table 當前的 reader 和 writer 暴露特定時間戳下的檔案佈局檢視。

img

file group 是 Hudi 在檔案佈局層的核心抽象,每一個 file group 相當於一個 bucket,通過檔案大小來來劃分,它的每次寫入行為都會產生一個新的版本,一個版本被抽象為一個 file slice,file slice 內部維護了相應版本的資料檔案。當一個 file group 寫入到規定的檔案大小的時候,就會切換一個新的 file group。

Hudi 在 file slice 的寫入行為可以抽象成兩種語義, copy on write 和 merge on read。

img

copy on write 每次都會寫全量資料,新資料會和上一個 file slice 的資料 merge,然後再寫一個新的 file slice,產生一個新的 bucket 的檔案。

img

而 merge on read 則比較複雜一些,它的語義是追加寫入,即每次只寫增量資料,所以不會寫新的 file slice。它首先會嘗試追加之前的 file slice,只有當該寫入的 file slice 被納入壓縮計劃之後,才會切新的 file slice。

二、Flink Hudi Integration

img

Flink Hudi 的寫入 pipeline 由幾個運算元構成。第一個運算元負責將 table 層的 rowdata 轉換成 Hudi 的訊息格式 HudiRecord。 接著經過一個 Bucket Assigner,它主要負責將已經轉好的 HudiRecord 分配到特定的 file group 中,接著分好 file group 的 record 會流入 Writer 運算元執行真正的檔案寫入。最後還有一個 coordinator,負責 Hudi table 層的 table service 排程以及新事務的發起和提交。此外,還有一些後臺的清理角色負責清理老版本的資料。

img

當前的設計中,每一個 bucket assign task 都會持有一個 bucket assigner,它獨立維護自己的一組 file group。在寫入新資料或非更新 insert 資料的時候,bucket assign task 會掃描檔案檢視,優先將這一批新的資料寫入到被判定為小 bucket 的 file group 裡。

比如上圖, file group 預設大小是 120M,那麼左圖的 task1 會優先寫到 file group1和 file group2,注意這裡不會寫到 file group3,這是因為 file group3 已經有 100M 資料,對於比較接近目標閾值的 bucket 不再寫入可以避免過度寫放大。而右圖中的 task2 會直接寫一個新的 file group,不會去追加那些已經寫的比較大的 file group 了。

img

接下來介紹 Flink Hudi 寫流程的狀態切換機制。 作業剛啟動時,coordinator 會先嚐試去檔案系統上新建這張表,如果當前表不存在,它就會去檔案目錄上寫一些 meta 資訊,也就是構建一個表。 收到所有 task 的初始化 meta 資訊後,coordinator 會開啟一個新的 transaction,write task 看到 transaction 的發起後,就會解鎖當前資料的 flush 行為。

Write Task 會先積攢一批資料,這裡有兩種 flush 策略,一種是當前的資料 buffer 達到了指定的大小,就會把記憶體中的資料 flush 出去;另一種是當上遊的 checkpoint barrier 到達需要做快照的時候,會把所有記憶體中的資料 flush 到磁碟。每次 flush 資料之後都會把 meta 資訊傳送給 coordinator。coordinator 收到 checkpoint 的 success 事件後,會提交對應的事務,並且發起下一個新的事務。writer task 看到新事務後,又會解鎖下一輪事務的寫入。這樣,整個寫入流程就串起來了。

img

Flink Hudi Write 提供了非常豐富的寫入場景。當前支援對 log 資料型別的寫入,即非更新的資料型別,同時支援小檔案合併。另外對於 Hudi 的核心寫入場景比如更新流、CDC 資料也都是 Hudi 重點支援的。同時,Flink Hudi 還支援歷史資料的高效率批量匯入,bucket insert 模式可以一次性將比如 Hive 中的離線資料或者資料庫中的離線資料,通過批量查詢的方式,高效匯入 Hudi 格式中。另外,Flink Hudi 還提供了全量和增量的索引載入,使用者可以一次性將批量資料高效匯入湖格式,再通過對接流的寫入程式,實現全量接增量的資料匯入。

img

Flink Hudi read 端也支援了非常豐富的查詢檢視,目前主要支援的有全量讀取、歷史時間 range 的增量讀取以及流式讀取。

img

上圖是一段通過 Flink sql 寫 Hudi 的例子,Hudi 支援的 use case 非常豐富,也儘量簡化了使用者需要配置的引數。通過簡單配置表 path、 併發以及 operation type,使用者可以非常方便地將上游的資料寫入到 Hudi 格式中。

三、Flink Hudi Use Case

下面介紹 Flink Hudi 的經典應用場景。

img

第一個經典場景是 DB 匯入資料湖。目前 DB 資料匯入資料湖有兩種方式:可以通過 CDC connector 一次性將全量和增量資料匯入到 Hudi 格式中;也可以通過消費 Kafka 上的 CDC changelog,通過 Flink 的 CDC format 將資料匯入到 Hudi 格式。

img

第二個經典場景是流計算的 ETL (近實時的 olap 分析)。通過對接上游流計算簡單的一些 ETL,比如雙流 join 或雙流 join 接一個 agg,直接將變更流寫入到 Hudi 格式中,然後下游的 read 端可以對接傳統經典的 olap 引擎比如 presto、spark 來做端到端的近實時查詢。

img

第三個經典場景和第二個有些類似, Hudi 支援原生的 changelog,也就是支援儲存 Flink 計算中行級別的變更。基於這個能力,通過流讀消費變更的方式,可以實現端到端的近實時的 ETL 生產。

img

未來,社群兩個大版本主要的精力還是放在流讀和流寫方向,並且會加強流讀的語義;另外在 catalog 和 metadata 方面會做自管理;我們還會在近期推出一個 trino 原生的 connector 支援,取代當前讀 Hive 的方式,提高效率。

四、Apache Hudi Roadmap

下面是一個 MySql 到 Hudi 千表入湖的演示。

首先資料來源這裡我們準備了兩個庫,benchmark1 和 benchmark2,benchmark1 下面有 100 張表,benchmark2 下面有 1000 張表。因為千表入湖強依賴於 catalog,所以我們首先要建立 catalog,對於資料來源我們要建立 MySql catalog,對於目標我們要建立 Hudi catalog。MySql catalog 用於獲取所有源表相關的資訊,包括表結構、表的資料等。Hudi catalog 用於建立目標。

img

執行兩條 sql 語句以後,兩條 catalog 就建立成功了。

img

接下來到作業開發頁面建立一個千表入湖的作業。只需要簡單的 9 行 SQL,第一種語法是 create database as database,它的作用是把 MySql benchmark1 庫下所有的表結構和表資料一鍵同步到 Hudi CDS demo 庫,表的關係是一對一對映。第二條語法是 create table as table,它的作用是把 MySql benchmark2 庫下所有匹配 sbtest. 正規表示式的表同步到 Hudi 的 DB1 下的 ctas_dema 表裡面,是多對一的對映關係,會做分庫分表的合併。

接著我們執行並上線,然後到作業運維的頁面去啟動作業,可以看到配置資訊已經更新了,說明已經重新上線過。接著點選啟動按鈕,啟動作業。然後就可以到作業總覽頁面檢視作業相關的狀態資訊。

img

上圖是作業的拓撲,非常複雜,有 1100 張源表和 101 張目標表。這裡我們做了一些優化 —— source merge,把所有的表合併到一個節點裡,可以在增量 binlog 拉取階段只拉取一次,減輕對 MySql 的壓力。

img

接下來重新整理 oss 頁面,可以看到已經多了一個 cdas_demo 路徑,進入 subtest1 路徑,可以看到已經有後設資料在寫入,表明資料其實在寫入過程中。

img

再到作業開發頁面寫一個簡單的 SQL 查詢某張表,來驗證一下資料是否真的在寫入。執行上圖 SQL 語句,可以看到資料已經可以查詢到,這些資料與插入的資料是一致的。

我們利用 catalog 提供的後設資料能力,結合 CDS 和 CTS 語法,通過幾行簡單的 SQL,就能輕鬆實現幾千張表的資料入湖,極大簡化了資料入湖的流程,降低了開發運維的工作量。


FFA 2021 直播回放 & 演講 PDF 下載

更多 Flink 相關技術問題,可掃碼加入社群釘釘交流群
第一時間獲取最新技術文章和社群動態,請關注公眾號~

image.png

相關文章