基於 Apache Hudi 構建增量和無限回放事件流的 OLAP 平臺

leesf發表於2022-04-11

1. 摘要

在本部落格中,我們將討論在構建流資料平臺時如何利用 Hudi 的兩個最令人難以置信的能力。

  • 增量消費--每 30 分鐘處理一次資料,並在我們的組織內構建每小時級別的OLAP平臺
  • 事件流的無限回放--利用 Hudi 的提交時間線在超級便宜的雲物件儲存(如 AWS S3)中儲存 10 天的事件流(想象一個具有 10 天保留期的 kafka 主題)
  • 具有部分記錄更新的自定義 Hudi Payload 類

2. 當前狀態

2.1 問題說明

對於大多數業務需要手動干預以通過檢視 KPI 和資料趨勢來決定下一組操作用例以及其他不太實時的用例,我們需要具有成本效益和高效能的近實時系統。
但是我們在資料湖中獲得的資料通常以 D -1 的每日批處理粒度出現,即使我們每天不止一次地執行這些日常批處理資料處理系統以獲取當前 D 的最新資料,這些批處理系統的固有侷限性也無助於我們解決近實時業務用例。

2.2 挑戰

在將批處理資料攝取到我們的資料湖時,我們支援 S3 的資料集在每日更新日期分割槽上進行分割槽。即使我們每天多次執行這些批處理系統,我們從上游 Kafka 或 RDBMS 應用程式資料庫中提取的最新批處理也會附加到 S3 資料集中當前日期的分割槽中。
當下遊系統想要從我們的 S3 資料集中獲取這些最新記錄時,它需要重新處理當天的所有記錄,因為下游程式無法在不掃描整個資料分割槽的情況下從增量記錄中找出已處理的記錄。
此外如果我們按小時(而不是每日分割槽)對 S3 資料集進行分割槽,那麼這會將分割槽粒度設定為每小時間隔。任何試圖以低於一小時(例如最後 x 分鐘)的粒度獲取最新更新的下游作業都必須在每次執行時再次重新處理每小時資料分割槽,即這些批處理源將錯過解決近實時用例所需的關鍵增量資料消費。

2.3 無限播放事件流

現在回到幫助我們解決這些挑戰的 Apache Hudi 的特性,讓我們首先嚐試瞭解commit(提交)和commit timeline(提交時間線)如何影響增量消費和事件流保留/回放。
Hudi 維護了在不同時刻在表上執行的所有操作的時間表,這些commit(提交)包含有關作為 upsert 的一部分插入或重寫的部分檔案的資訊,我們稱之為 Hudi 的提交時間線。
對於每個 Hudi 表,我們可以選擇指定要保留多少歷史提交,要保留的預設提交是 10 次,即在 10 次提交之後,第 11 次提交將另外執行一個清理服務,該服務將清除第一次提交歷史記錄。
清理commit(提交)時,清理程式會清理與該提交對應的部分檔案的過時版本,相關資料被保留,因為過時的檔案中的所有資料無論如何都存在於新版本的檔案中,這裡重要的是我們可以觸發快照查詢來獲取資料的最新狀態,但我們將無法對已清理的提交執行增量查詢來獲取增量資料。
簡而言之,如果清除了commit(提交),我們就失去了從該commit(提交)回放事件流的能力,但是我們仍然可以從任何尚未清理的commit(提交)中回放事件流。
在我們的例子中,我們將 Hudi 表配置為保留 10K 提交,從而為我們提供 10 天的增量讀取能力(類似於保留 10 天的 kafka 主題)
我們保留的歷史提交數量越多,我們就越有能力及時返回並重放事件流。

3. 每小時 OLAP

讓我快速展示一下我們的端到端訊息 OLAP 計算管道與 10 天事件流的架構

在 kafka 層,我們的 kafka 輸入源每個都有 1 天的主題保留期。
在攝取層,我們有 Spark 結構化流作業,從 kafka 源讀取資料並將微批處理寫入 S3 支援的 Hudi 表。 這是我們配置為保持 10k 提交以啟用 10 天事件流播放的地方。

.option("hoodie.cleaner.commits.retained", 10000)
.option("hoodie.keep.max.commits", 10002)
.option("hoodie.keep.min.commits", 10001)

計算層由我們當前每 30 分鐘執行一次的批處理 Spark 作業組成,並重新處理我們在過去 60 分鐘內攝取到 Hudi 表中的所有事件。 每小時 OLAP 作業讀取兩個跨國表和可選的 N 維表,並將它們全部連線起來以準備我們的 OLAP 增量DataFrame。
我們每 30 分鐘處理一次 60 分鐘的資料,以增強表連線的一致性。
有趣的是生產系統中通常不建議保留 1 天的 kafka 保留期,但是我們能夠進行這種權衡以節省一些 SSD 和 Kafka 代理成本,因為無論如何我們都可以通過 S3 支援的 Hudi 表實現 10 天的事件流播放能力。

4. 部分記錄更新

上面的管道顯示了我們如何通過讀取和合並兩個增量上游資料來源來建立每小時增量 OLAP。
然而這些增量資料處理有其自身的挑戰。 可能會發生在兩個上游表中,對於主鍵,我們在其中一個資料來源中獲得更新,但在另一個資料來源中沒有,我們稱之為不匹配的交易問題。
下面的插圖試圖幫助我們理解這一挑戰,並看看我們實施的解決方案。

在這裡,表A和B都有一些對應的匹配事務和一些不匹配的事務。使用內部連線將簡單地忽略不匹配的事務,這些事務可能永遠不會流入我們的基礎 OLAP。相反使用外連線會將不匹配的事務合併到我們的每小時增量資料載入中。但是使用外連線會將缺失的列值新增為 null,現在這些空值將需要單獨處理。
在使用預設有效負載類將此每小時增量資料更新到基礎 Hudi OLAP 時,它將簡單地用我們準備的每小時增量資料中的新記錄覆蓋基礎 Hudi OLAP 中的記錄。但是通過這種方式,當我們用傳入記錄中的空列值覆蓋現有記錄時,我們將丟失現有記錄中可能已經存在的資訊。因此為了解決這個問題,我們提供了我們的自定義部分行更新有效負載類,同時將外部連線的每小時增量資料插入到基礎 Hudi OLAP。有效負載類定義了控制我們在更新記錄時如何合併新舊記錄的函式。
我們的自定義有效負載類比較儲存和傳入記錄的所有列,並通過將一條記錄中的空列與另一條記錄中的非空列重疊來返回一條新記錄。
因此即使只有一個上游表得到了更新,我們的自定義有效負載類也會使用這個部分可用的新資訊,它會返回包含部分更新資訊的完全最新記錄。
由於儲存和部分行更新記錄的主鍵和分割槽鍵相同,因此 Hudi upsert 操作會自動更新舊記錄,從而為我們提供基本 OLAP 的去重和一致檢視。
有關如何編寫自己的有效負載類的更多技術細節

5. 結語

結合這三個概念,即增量消費、增量每小時 OLAP 處理和自定義部分行更新有效負載類,我們為我們的獨角獸初創公司構建了一個強大的流處理平臺,以使其一直擴充套件成為一個百角獸組織。

相關文章