在 Hudi 中可以根據業務場景爲 Hudi 表配置負載類Payload,它用於在更新期間合併同一記錄的兩個版本。本文將深入瞭解有效負載類的用途以及可以使用的所有不同方式。
配置:hoodie.datasource.write.payload.class
注意:對於新的記錄合併API ,這些可能會發生變化。 因此此有效負載類詳細信息適用於 Hudi 0.13.0 之前的所有版本。 未來的版本可能會棄用這一點。
Payload類
Hudi 有一個有效負載類接口,它將確定如何將同一記錄的兩個版本合併在一起。
核心方法如下:
/**
* This methods lets you write custom merging/combining logic to produce new values as a function of current value on storage and whats contained
* in this object. Implementations can leverage properties if required.
* <p>
* eg:
* 1) You are updating counters, you may want to add counts to currentValue and write back updated counts
* 2) You may be reading DB redo logs, and merge them with current image for a database row on storage
* </p>
*
* @param currentValue Current value in storage, to merge/combine this payload with
* @param schema Schema used for record
* @param properties Payload related properties. For example pass the ordering field(s) name to extract from value in storage.
* @return new combined/merged value to be written back to storage. EMPTY to skip writing this record.
*/
Option<IndexedRecord> combineAndGetUpdateValue(IndexedRecord currentValue, Schema schema, Properties properties) throws IOException;
Hudi 在內部將一條記錄表示爲 HoodieRecord,它由一對 HoodieKey 和 HoodieRecordPayload 組成。 正如我們在之前的博客中看到的,HoodieKey 代表一條記錄的主鍵(通常是分區路徑和記錄鍵)。 HoodieRecordPayload是用戶實際傳入的數據。
讓我們來看一個典型的例子。 在 commit1 中攝取了 2 條記錄,即 {HK1, payload1_1} 和 {HK2, payload2_1}。 在 commit2 中,假設攝取 {HK1, payload1_2} 和 {HK3, payload3_1}。
由於更新了 HK1,Hudi 將合併兩個有效載荷(payload1_1 和 payload1_2 以產生 HK1 的最終輸出。這就是上面顯示的 combineAndGetUpdateValue()
發揮作用的地方。
本質上,HK1.payload1_2.combineAndGetUpdateValue(HK1.payload1_1) 在 commit2 結束時推導出 HK1 的最終值。
在這種情況下,讓我們深入研究 Hudi 提供的一些有效負載實現。 默認負載類稱爲 OverwriteWithLatestAvroPayload。
OverwriteWithLatestAvroPayload
正如名稱所暗示的那樣,當使用此有效負載類時,我們只需使用新的傳入值覆蓋任何現有值。 因此,在上述示例中,一旦 commit2 完成,payload1_2 將成爲 HK1 的最終值。 這是 Hudi 提供的最簡單的有效負載,並且對社區中的大多數用戶來說效果很好。
DefaultHoodieRecordPayload
我們還有一個名爲 DefaultHoodieRecordPayload 的負載類。 與 Hudi 一開始就提供的 OverwriteWithLatestAvroPayload 相比,這個 DefaultHoodieRecordPayload 是在 1.5 年前引入的。 讓我們深入瞭解一下這個負載類的特殊之處。
一般來說,Hudi表可以配置preCombine
字段。 簡而言之 preCombine 字段用於解決同一批次中同一記錄的兩個版本之間的優勝者。 例如,如果在寫入 Hudi 時在同一批次中攝取 {HK1, payload1_1} 和 {HK1, payload1_2},Hudi 將在內部路由之前對傳入記錄進行去重。 因此在這種情況下,preCombine 字段值將決定多個版本中的獲勝者。
例如可以在表schema中選擇“updated_at”字段作爲 preCombine 字段。 因此,如果傳入批次中有超過 1 條具有相同 HoodieKey 的記錄,則具有較高 preCombine 值的記錄將優先。
儘管 OverwriteWithLatestAvroPayload 和 DefaultHoodieRecordPayload 可能看起來很相似,但有一個關鍵區別。 這是 combineAndGetUpdateValue() 的實現方式。 DefaultHoodieRecordPayload 在將傳入記錄與存儲中的記錄合併時也遵循 preCombine 值,而 OverwriteWithLatestAvroPayload 將盲目地選擇傳入而不是存儲中的任何內容。
讓我們添加帶有插入記錄(HK3,以及 HK1 的更新值)的 commit2。
OverwriteWithLatestAvroPayload 和 DefaultHoodieRecordPayload 都用 payload1_2 更新了 HK1。 OverwriteWithLatestAvroPayload 始終選擇較新的傳入,因此選擇了 payload1_2。 DefaultHoodieRecordPayload 根據 preCombine 字段值推導。 由於 payload1_2 的預組合字段值(20)高於 payload1_1 的預組合字段值(10),DefaultHoodieRecordPayload 也選擇 payload1_2 作爲 HK1 的最終快照。
現在讓我們使用 commit3,它使用較低的 preCombine 值更新 HK1 以模擬遲到的數據。
OverwriteWithLatestAvroPayload 選擇新的傳入有效負載而不考慮 preCombine 值,因此它選擇 payload1_3 作爲 HK1 的最終值。 但 DefaultHoodieRecordPayload 根據 preCombine 值選擇最終獲勝者,因此它選擇 payload1_2 作爲 HK1 的最終快照值。
社區有其他有效負載類供使用,如 OverwriteNonDefaultsWithLatestAvroPayload、AWSDmsAvroPayload、MySqlDebeziumAvroPayload、PostgresDebeziumAvroPayload 等。
還可以自定義合併兩個版本的記錄的負載類,爲 lakehouse 用戶提供了極大的靈活性。 如果不是 SparkSQL 寫入(MERGE INTO),沒有多少系統能給你這種靈活性,但 Hudi 用戶從一開始就享受它
結論
因爲不同用例的場景不同,Hudi 支持Payload方式提供靈活性,有效負載類就是這樣一種設計,可以根據自己的需求定義自己的 Payload 類,而不是侷限於 Hudi 提供的 Payload。 希望這篇博客有助於理解有效負載類的用途、常用的有效負載實現。