在深入研究Hudi機制之前,讓我們首先了解Hudi正在解決的問題。
客戶在使用資料湖時通常會問一個問題:當源記錄被更新時,如何更新資料湖?這是一個很難解決的問題,因為一旦你寫了CSV或Parquet檔案,唯一的選擇就是重寫它們,沒有一種簡單的機制可以開啟這些檔案,找到一條記錄並用原始碼中的最新值更新該記錄,當資料湖中有多層資料集時,問題變得更加嚴重,資料集的輸出將作為下次資料集計算的輸入。
在資料庫中使用者只需發出一個更新記錄命令就可以完成任務了,所以從資料庫的思維模式來看很難理解上述限制,為什麼不能在資料湖中完成?首先讓我們來看看資料庫是如何應用記錄級更新的,這對於理解Hudi是如何工作的很有價值。
RDBMS的更新原理
RDBMS將資料儲存在B-Tree儲存模型中,資料儲存在資料頁中,資料頁可以通過在表的列上建立的索引來找到。因此當發出更新命令時,RDBMS引擎會找到包含該記錄的確切頁面,並在該資料頁面中適當地更新資料,這是一個簡化的描述,在大多數現代RDBMS引擎中,在多版本併發控制等方面存在額外的複雜性,但基本思想保持不變。
下圖說明了如何通過B樹索引找到帶有值13的資料頁,底層(第三層)是表示資料頁的葉節點,頂層(第一層)和中間層(第二層)上的節點是索引值。
以下是一些非SQL資料庫(如Cassandra)中的更新工作方式:
許多非SQL資料庫將資料儲存在LSM樹的儲存模型中,這是一個基於日誌的儲存模型,新資料(插入/更新/刪除)被新增到append-only的日誌中,然後定期將日誌合併回資料檔案,使資料檔案與所有更改的資料保持最新,這種合併過程稱為壓縮,因此當更新一條記錄時,只是將其寫入到append-only日誌中,根據資料庫引擎的優化規則,將組合append-only日誌和資料檔案來為讀取查詢提供服務,這也是一個簡化的描述,但基本思想相同。
下圖說明了如何將新的和更新的資料新增到append-only日誌(級別0)中,並最終合併到更大的檔案中(級別1和級別2)。
現在我們已經基本瞭解了資料庫如何處理記錄級別的更新,接著看看Hudi如何工作,在Hudi(和類似的框架,如DeltaLake)出現之前,對datalake應用更新的唯一途徑是重新計算並重寫整個csv/parquet檔案,如前所述,沒有簡單的機制來開啟檔案並更新其中的單個記錄,造成這種限制有很多原因,其中一些主要原因是不知道哪個檔案包含要更新的記錄,也沒有有效的方法來掃描一個檔案來找到想要更新的記錄,另外Parquet這樣的列檔案格式不能就地更新,只能重新建立。在資料湖中,通常還有多個被轉換的資料層,其中一組檔案被輸入到下一組檔案的計算中,因此在單記錄更新期間編寫邏輯來管理這種依賴關係幾乎是不可能的。
HUDI
HUDI框架的基本思想是採用資料庫更新機制的概念,並將其應用於datalake,這就是Hudi實現的目標,Hudi有兩種“更新”機制:
- 寫時拷貝(COW)-這類似於RDBMS B-Tree更新
- 讀時合併(MOR)-這類似於No-SQL LSM-Tree更新
此外,HUDI還維護以下內容:
- 將資料記錄對映到檔案(類似於資料庫索引)
- 跟蹤到資料湖中的每個邏輯表的最近提交
- 能夠基於“record_key”在檔案中識別單個記錄,這在所有Hudi資料集中是必需的,類似於資料庫表中的主鍵
Hudi使用上述機制以及“precombine_key”機制來保證不會存在重複的記錄。
- 標準資料檔案大小(儘可能)
Copy on Write
在該模型中,當記錄更新時,Hudi會找到包含更新資料的檔案,然後使用更新值重寫這些檔案,包含其他記錄的所有其他檔案保持不變,因此更新的處理是快速有效的,讀取查詢通過讀取最新的資料檔案來檢視最新的更新,此模型適用於讀效能更為重要的讀重負載,這種模型的缺點是突然的寫操作會導致大量的檔案被重寫,從而導致大量的處理。
Merge on Read
在該模型中,當記錄更新時,Hudi會將它附加到資料湖表的日誌中,隨著更多的寫入操作進入,它們都會被附加到日誌中,通過從日誌和資料檔案中讀取資料並將結果合併在一起,或者根據使用者定義的引數只從資料檔案中讀取資料來服務讀取查詢,如果使用者希望實時檢視資料,則從日誌中讀取資料;否則,如果指定為read optimized表,則從資料檔案中讀取資料,但資料可能已過時,Hudi會定期將日誌合併到資料檔案中,以使它們保持最新狀態,這是配置為根據用例需求定期執行的壓縮過程。
如果你的資料湖中有多層資料集,每一層都將其輸出作為下一個計算的輸入,那麼只要所有這些資料集都是Hudi資料集,記錄級更新可以很好地、自動地在多個處理層中傳播,而不必重新編寫整個資料集。
以上所有這些都是從記錄更新的角度出發的,同樣的Hudi概念也適用於插入和刪除,對於刪除有軟刪除和硬刪除兩個選項,使用軟刪除,Hudi保留記錄鍵並刪除記錄資料,使用硬刪除,Hudi會為整個記錄寫空白值,丟棄記錄鍵和記錄資料。