網易基於 Iceberg 的實時湖倉一體系統構建經驗

帶你聊技術 發表於 2022-12-08

導讀:本文將介紹網易基於 Apache iceberg 構建的實時湖倉一體系統——Arctic。

主要包括以下幾大部分:

  • 當前業務的挑戰:Lambda 架構下流與批割裂帶來的問題

  • Arctic功能特性:網易 Arctic 基於 iceberg 構建的湖倉一體系統

  • 業務實踐:Arctic 在網易內外的實踐

  • 未來規劃

分享嘉賓|張永翔 網易數帆 資深大資料平臺開發

編輯整理|呂劍 SUTPC

出品社群|DataFun



01

當前業務的挑戰

1. Lambda 架構下流與批割裂帶來的問題

網易基於 Iceberg 的實時湖倉一體系統構建經驗

我們目前的業務基本上都是流批一體的,採用 Lambda 架構。上圖展示的是一個純 T的業務的生產資料流。它對應的是 T+1 的離線資料生產。我們在離線的資料生產上,初步引入了一些實時化,引入了一個訊息佇列,並把 CDC  資料和一些 log  資料引入到了訊息佇列中。 

網易基於 Iceberg 的實時湖倉一體系統構建經驗

離線流程保持不變的情況下,用 Flink  去做一些清洗聚合,最後資料進入到 kudu當中。在資料集市中,對兩邊 hive  的資料和 kudu 中的資料進行合併。kudu 這邊流的資料,有分鐘級別的延遲。hive 這邊是一個小時級別或者天級別的資料流,最終在資料集市中做聚合。

網易基於 Iceberg 的實時湖倉一體系統構建經驗

後來我們引入了更復雜的邏輯。首先包括一些清洗打寬聚合的邏輯,一些 Flink 任務開始去往 Hbase 裡面生成維表資料,然後在流這邊先做一個聚合之後才落入 kudu 當中,最後在資料集市再把流和批去做整合。

網易基於 Iceberg 的實時湖倉一體系統構建經驗

這是一個很典型的 Lambda 架構,流與批的流程是割裂的。這種割裂的 Lambda 架構,最大的問題就是資料孤島的問題,因為有大量的流的資料存在 kudu 當中,kudu 是一個獨立部署的架構,無法利用 hadoop 的整套生態體系,離線的資料也沒辦法複用,其次是研發體系上流和批處理完全是割裂的,人效很低,並且無法統一指標和語義。
02
Arctic 功能特性
1. Arctic 的定位

網易基於 Iceberg 的實時湖倉一體系統構建經驗

基於以上現狀,我們研發了 Arctic  產品,它是基於 Iceberg 去構建湖倉一體的系統。其定位是在 hive 和 Iceberg  之上,在計算引擎之下的一個 TableService, 並提供表結構最佳化以及 Kafka 以及 redis,  Hbase 等 KV 儲存封裝的實時湖倉系統。

網易基於 Iceberg 的實時湖倉一體系統構建經驗

上圖是一個 T+1 的 hive 計算場景。一個批的資料,進行了 T+1 或者 T+H 的攝取,再進行批的計算,每次都進行了全量的攝取或計算,在此基礎上我們引入了 Iceberg 和 Deltalake。

網易基於 Iceberg 的實時湖倉一體系統構建經驗

網易基於 Iceberg 的實時湖倉一體系統構建經驗
Iceberg 透過快照的隔離實現了 MVCC  和 ACID, 在基於 MVCC 和 ACID 的情況下,可以支援資料實時攝取,也支援準實時的 incremental query。在 Iceberg 的基礎上,Arctic 進一步進行了區分,把 batch 資料和 stream 的資料寫入的檔案進行分割槽,分為 change store 和 base store。透過非同步的 optimizing 對 stream 寫入的檔案進行合併,並提供了小檔案治理、唯一鍵保證和 upsert 的能力,透過 ArcticTable 封裝的介面提供 merge on read,實現準實時的讀寫能力。

網易基於 Iceberg 的實時湖倉一體系統構建經驗

我們在架構上把 Arctic Table 劃分為多個 Table store,流寫入叫 Change store,批寫入叫 Base store,並透過 table optimize 實現 primary key 的唯一鍵約束。primary key  提供的是 upsert 語義,包括 CDC ingestion,Batch Insert 的時候可以實現基於元件的更新,並透過 merge on read 保證唯一性約束。每個 table store 的實現都是一個 iceberg 表,未來在提供 base store 之外,還會提供 SortKey、AggKey。

網易基於 Iceberg 的實時湖倉一體系統構建經驗

我們對 Arctic Table 做了兩種 Optimize,一種是短週期的 Minor Optimize,大約 5 到 10分鐘執行一次,主要提供小檔案的治理,把寫入 change store 中間的 equal delete  轉換 pos-delete。另外一種是長週期的 Major Optimize,大約 1 天執行一次,將 insert file 和 change file 合併到 base file,當合並的 major optimize 執行完成之後,就只有 base 表,base 檔案與 hive 格式完全相容。

網易基於 Iceberg 的實時湖倉一體系統構建經驗

為了增強流處理下的應用場景,進行了 hidden queue 封裝,在 Arctic table 內部封裝kafka,對實時性要求高的上游任務進行雙寫,即往 change store 寫同時也寫入 hidden queue,下游直接從 kafka 消費,以此達到秒級甚至毫秒級的 CDC。如果不開啟 hidden queue,上游只寫 change store,下游利用 iceberg incremental poll 實現分鐘級別訂閱,整個雙寫過程透過 Arctic-Flink-connector 進行封裝。
在雙寫情況下會遇到一致性問題,比如上游 Flink 任務在雙寫的時候,要保證毫秒級和秒級延遲,需要先寫入 kafka,再寫入 iceberg 檔案,可能會在寫入的時候,任務發生故障,導致 iceberg 檔案並沒有提交,而下游已經消費了未提交的資料,如果上游任務做了 failover 會導致部分資料重發,下游重複消費。

網易基於 Iceberg 的實時湖倉一體系統構建經驗

為解決這個問題,arctic 在雙寫的時候用回撤方式對訊息提供了最終一致性的保證,所有寫入 Kafka 的訊息均進行了一次封裝,每一個訊息帶上上游 writer 對應的 state 週期,即當前的 checkpoint 的 index。在下游任務發生 failover 之後,會根據 checkpoint 恢復,先發出 Flip 訊息,下游任務收到 Flip 訊息之後,會自動的從 Kafka 中掃描找到對應的需要回撤的訊息並進行 retract 操作,整個流程在 Arctic-flink-connector 封裝,遮蔽業務層面雙寫帶來的一致性問題,業務層面只需要將 Arctic 當成流表使用。
同時為了支援將 Arctic 當維表使用,實現了一個 hidden 的 index,內部封裝了 Hbase或 Redis,支援在上游寫入實現雙寫,資料寫入 redis 或 HBase,下游不需要關心實現細節,將 Arctic 當成維表使用,但這種情況下,目前沒有比較好的辦法解決一致性,未來會實現 flink1.12 中提出來的時態表 temporal join,無需依賴外部 KV。

網易基於 Iceberg 的實時湖倉一體系統構建經驗

在使用時態表(temprol table)的情況下,流表、維表都寫入 kafka,下游做 left join 的時候已經把資料快取在 flink 的 state,不需要引入額外的 KV 元件,也不存在一致性問題,且能支援 event time join,當前方案處於規劃中。
網易基於 Iceberg 的實時湖倉一體系統構建經驗

網易基於 Iceberg 的實時湖倉一體系統構建經驗

為了支援流、批的同時,寫一張 Arctic 表,在併發寫入時涉及一致性問題。比如在這個場景中,在某個時間點的記錄 ID =1,value=A,這個時候開始 spark 任務,將 value 更新為 C, flink 任務啟動之後一直在寫,在後面 commit 的時候把值 value 更新為 B,在任務結束之後該記錄值應該是多少?由於 Iceberg 在 commit 的時候做 ACID 的保證,且 Flink 任務是先 commit 的,Spark 任務是後 commit 的,在這個時間點,它的 value 應該是 C。但是我們按照傳統資料庫的視角來看,spark 任務在開啟時 value 已經確定,這時 Flink 任務進行更新,記錄已經被鎖住,當 spark 任務進行 commit 之後,記錄才能夠被更新,在資料庫的視角來看,在這個時間點該記錄 value 應該為 B。

網易基於 Iceberg 的實時湖倉一體系統構建經驗

有兩種比較常見的業務場景,一種是資料回補的場景,比如 CDC 任務發生故障,在重跑時發現 CDC 資料已經有一些丟失,這時需要批任務進行資料全量回補。還有一種場景,在有 GDPR/CPAA 方案時,要求支援使用者關閉推薦,刪除大資料系統內使用者的存量資料;這種流式在不停的寫入的情況下,需要進行級更新。我們的做法就是引入了一個 txId 的概念,對於每一個 Flink 任務,每個 check point 週期開始時,向 Arctic 申請 txID,在寫入的時把 txID 與批檔案、對應的 txId 一起寫入。對於 spark 任務,在 plan 時申請 txId ,最終在寫入時,把 txID 與檔案資訊一起寫入。當讀取任務進行 merge on read 時,根據具體的 txId 的前後順序,決定最終讀哪一個 record;包括 Minor/Major Optimize 時也是根據 txId 決定最終保留的 record。

網易基於 Iceberg 的實時湖倉一體系統構建經驗

推廣的過程中,在內部首先推廣 iceberg,但是我們在推廣流批一體的過程中,發現已經有很完善的 hive 生態;包括基於 hive 構建的一整套大資料的方法論,和圍繞 hive 的中臺元件,我們發現幾乎不太可能離開 hive 。我們後來就對基座重新做了規劃,在支援 iceberg 當作基座的情況下,又支援直接將 hive 作為基座。在這種情況下,支援直接將 hive 原地升級為 arctic 表,原地升級的意思是會保留 hive 表,並且保留原有 hive 表上所有任務。對原有的 hive 表的讀寫任務是沒有任何侵入的。支援將 hive 表升級成 arctic 表之後,支援將 arctic base store 作為 hive 表進行讀寫,支援透過 Flink 任務往 arctic 表上寫,透過我們的 optimize 往 hive 表同步。使得在離線場景中不用做任何改動的情況下,增加離線表的實時能力。

網易基於 Iceberg 的實時湖倉一體系統構建經驗

Arctic meta service,簡稱AMS,它的定位是未來的 HMS。負責 Arctic Table Metadata 的管理,分配事物 ID,提供面向資料引擎的後設資料服務。在 AMS 內部可以進行多個 HMS 管理,AMS 還負責觸發表結構最佳化任務,最佳化任務包含基於時間的觸發,基於檔案大小,小檔案碎片的評估的自動觸發;以及最佳化任務的排程與資源管理,同時提供運維友好的 Dashboard。
網易基於 Iceberg 的實時湖倉一體系統構建經驗

網易基於 Iceberg 的實時湖倉一體系統構建經驗

上圖為 dashboard 展示的某張表在某一個 transaction 中,所提交的所有檔案資訊,還有在 optimize 的執行資訊,我們可以看到它在某一次 optimize 執行之後,將 84 個檔案合併成 4 個。

網易基於 Iceberg 的實時湖倉一體系統構建經驗

我們在立項的時候,對目前的資料湖產品,以及資料倉產品進行對比;其中與 Arctic 定位最相似的是 Hudi。Arctic 與 Hudi 的區別在於,Hudi目前對於多寫的場景支援的不是很好。它的 CDC 目前還是分鐘級的,並不是秒級;而且以它當維表的情況下,用起來不是太友好。另外,Arctic 和 Hudi 的底層不一樣,arctic選用更面向未來的,在未來可能會替代 hive 的 iceberg 作為底層。Kudu 的主要痛點是存算不分離,無法利用 HDFS  的資源,存在資料孤島,無法實現不同層資料串聯,以及存在效能問題。
Arctic 的優勢首先是基於 iceberg,相容 iceberg 所有功能,同時對 hive 相容性好,在短時間業務升級阻力更低;支援動態排程自動觸發合併任務,提供分鐘級別延遲數倉的 merge on read;在開啟 hidden on queue 的情況下,提供流批一體的功能包括秒級實時訂閱和實時 join;還提供方便管理的運維平臺,方便業務更快的上手。
03

實踐案例

網易基於 Iceberg 的實時湖倉一體系統構建經驗

網易基於 Iceberg 的實時湖倉一體系統構建經驗

這是在網易雲音樂的應用案例,它是一個推送系統分析,有兩張主站埋點的 log 日誌,及演算法埋點的 log 日誌。裝置庫是一個 mysql 維表同步。分析師想要知道推送的效果,需要透過報表進行查詢。它是透過一個 IP  的查詢來回的 join 這些表,走的批的流程,跟原來的批計算流程完全一致。如果分析師在報表分析之後,去做演算法上的最佳化。可以在架構不發生變化的情況下,立刻推送到面向流的應用,同樣是個 left join,但是它走的流程全部是這種流的生產路徑,資料全部經過 kafka 做來回的校驗,最終推送到歸因表中,最後由資料應用去使用流計算出來的結果資料,整個生產鏈路包括資料儲存,指標也不存在二義性,只有一份的資料儲存,任務等實現百分百複用。
04
未來規劃

網易基於 Iceberg 的實時湖倉一體系統構建經驗

未來的規劃主要包括:

  • 會更加專注於流批一體的場景,提供 doris 一樣的 RollUp 聚合檢視, 提供 sort key  的支援,支援簡單的 sort key 排序或者 z-order 的排序,以及部分列的 stream upsert,支援 temperol join。
  • 在中臺的整合方面,會繼續去追蹤到任務提交資訊,後續會持續追蹤任務的血緣和資料血緣,並且在 dashboard 上提供簡單的自助查詢。
  • 在安全體系方面,未來會支援更加開放的許可權外掛,支援 ranger 對接。
  • 未來會支援更多的底層資料湖儲存,如 S3、OSS 等。


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