Apache Hudi 0.13.0版本重磅釋出!

2023-03-06 06:01:01

Apache Hudi 0.13.0 版本引入了許多新功能,包括 Metaserver變更資料捕獲新的 Record Merge APIDeltastreamer支援新資料來源等。 雖然此版本不需要表版本升級,但希望使用者在使用 0.13.0 版本之前按照下面的遷移指南採取相關重大變更行為變更的操作。

遷移指南:概述

此版本與 0.12.0 版本保持相同的表版本 (5),如果從 0.12.0 升級,則無需升級表版本。 如下所述,存在一些重大變更和行為變更,使用者在使用 0.13.0 版本之前需要採取相應的措施。

注意:如果從舊版本(0.12.0 之前)遷移,請按順序檢查每個舊版本的升級說明。

遷移指南:重大變更

Bundle包更新

Spark Bundle支援

從現在開始,hudi-spark3.2-bundle 可與 Apache Spark 3.2.1 和 Spark 3.2.x 的更新版本一起使用。 由於Spark 版本 3.2.0 和 3.2.1 之間 HiveClientImplgetHive方法實現不相容,因此hudi-spark3.2-bundle放棄了對Spark 3.2.0 的支援。

實用程式Bundle包更改

AWS 和 GCP bundle jar 與 hudi-utilities-bundle 分開。意味著使用者在使用雲服務時需要使用 hudi-aws-bundlehudi-gcp-bundle 以及 hudi-utilities-bundle

Hudi 現在通過新的 hudi-flink1.16-bundle 支援 Flink 1.16.x 。

Spark Lazy檔案索引

Hudi 在 Spark 中的檔案索引預設切換為Lazy載入:這意味著它只會列出查詢請求的分割區(即在分割區修剪之後),而不是在此版本之前總是列出整個表。這會為大型錶帶來相當大的效能提升。

如果使用者想要更改列表行為,則會新增一個新的設定屬性:hoodie.datasource.read.file.index.listing.mode(現在預設為Lazy)。 可以設定兩個可能的值:

  • eager:這會在初始化期間列出所有分割區路徑和其中相應的檔案切片。這是 0.13.0 之前的預設行為。如果一個Hudi表有1000個分割區,eager模式在構建檔案索引時會列出所有分割區下的檔案。
  • lazy:其中的分割區和檔案切片將被延遲載入,允許分割區修剪謂詞被適當地向下推,因此只列出已經被修剪的分割區。初始化檔案索引時,檔案未列在分割區下。 在查詢中使用謂詞(例如,datestr=2023-02-19)進行分割區修剪後,檔案僅列在目標分割區下。

要保留 0.13.0 之前的行為需要設定 hoodie.datasource.read.file.index.listing.mode=eager。

只有當表同時具有以下兩種情況時才會發生重大更改:多個分割區列和分割區值包含未進行 URL 編碼的斜槓。
例如假設我們要從分割區路徑 2022/01/03 解析兩個分割區列 - 月 (2022/01)日 (03)。 由於分割區列的數量(此處為 2 - 分別為)與分割區路徑中由 /分隔的數量(在本例中為 3 - )不匹配,因此會導致歧義。 在這種情況下不能恢復每個分割區列對應的分割區值。
有兩種方法可以避免重大變更:

  • 第一個選項是更改分割區值的構造方式。 使用者可以切換月份列的分割區值,避免任何分割區列值出現斜槓,比如202201,那麼解析分割區路徑(202201/03)就沒有問題。
  • 第二個選項是將列表模式切換為 eager。 檔案索引將假定表未分割區並僅犧牲分割區修剪,但將能夠像表未分割區一樣處理查詢(因此可能導致效能損失),而不是失敗查詢。

Spark Structured Streaming 的檢查點管理

如果您使用 Spark streaming 攝取到 Hudi,Hudi 會在內部自行管理檢查點。 我們現在正在新增對多使用者端寫入的支援,每個寫入使用者端都通過流式攝取攝取到同一個 Hudi 表中。 在舊版本的 hudi 中,您不能將多個流式攝取編寫器攝取到同一個 hudi 表中(一個具有並行 Spark 資料來源編寫器的流式攝取寫入與鎖提供程式一起工作;但是不支援兩個 Spark 流式攝取編寫器)。 在 0.13.0 中,我們新增了對同一個表進行多個流式攝取的支援。 如果是單個流攝取,使用者無需執行任何操作; 舊管道無需任何額外更改即可工作。 但是如果有多個流式寫入使用者端寫入到同一個 Hudi 表,則每個表都必須為設定 hoodie.datasource.write.streaming.checkpoint.identifier 設定一個唯一的值。 此外使用者應該設定通常的多寫入器設定。更多詳情參考

Spark ORC 支援

此版本中刪除了 Spark 2.x 中支援的 ORC ,因為為了與Spark 3相容,Hudi 中對 orc-core:nohive 的依賴現在被 orc-core 取代,Spark 3.x 支援 ORC,但以前的版本不支援了。

強制設定記錄鍵欄位

設定記錄鍵欄位的設定hoodie.datasource.write.recordkey.field現在需要顯示設定,沒有預設值。 以前預設值為 uuid。

遷移指南:行為變更

寫路徑中的模式Schema處理

許多使用者想使用 Hudi CDC 用例,他們希望在新模式中刪除現有列的模式進行自動演化。 從 0.13.0 版本開始,Hudi 現在具有此功能,可以允許模式自動演化,可以在新模式中刪除現有列。

由於根據源Schema在目標表中刪除列是很大的行為更改,因此預設情況下禁用此功能並由以下設定保護:hoodie.datasource.write.schema.allow.auto.evolution.column.drop。要啟用自動刪除列以及傳入批次的新演化模式,請將其設定為 true

此設定不需要通過使用例如 ALTER TABLE … Spark 中的 DROP COLUMN 手動演變模式。

刪除預設Shuffle並行性

此版本更改了 Hudi 寫入操作的Shuffle並行度的方式,包括 INSERT、BULK_INSERT、UPSERT 和 DELETE (hoodie.insert|bulkinsert|upsert|delete.shuffle.parallelism),這最終會影響寫入效能。

之前如果使用者不設定,Hudi 會使用 200 作為預設的 shuffle 並行度。 從 0.13.0 開始,預設情況下Hudi 通過使用由 Spark 確定的輸出 RDD 分割區數(如果可用)或使用 spark.default.parallelism 值自動推導suffle並行度。 如果上述Hudi shuffle並行度是使用者明確設定的,那麼使用者設定的並行度仍然用於定義實際的並行度。 對於具有合理大小輸入的工作負載,此類行為更改可將開箱即用的效能提高 20%。

如果輸入資料檔案很小,例如小於 10MB,我們建議顯式設定 Hudi shuffle 並行度(hoodie.insert|bulkinsert|upsert|delete.shuffle.parallelism),這樣並行度至少為 total_input_data_size/500MB,以 避免潛在的效能下降(有關更多資訊請參考調優指南

預設的簡單寫執行器SimpleExecutor

對於插入/更新插入操作的執行,Hudi 過去使用執行器的概念,依靠記憶體中的佇列將攝取操作(以前通常由 I/O 操作獲取shuffle資料)與寫入操作分離。到現在 Spark 架構有了很大的發展,使得這種架構變得多餘。為了發展這種寫入模式並利用 Spark 優勢,在 0.13.0 中引入了一個新的簡化版本的執行程式,並命名為 SimpleExecutor並將其設定為開箱即用的預設值。

SimpleExecutor 沒有任何內部緩衝(即不在記憶體中儲存記錄),它在內部實現對提供的迭代器的簡單迭代(類似於預設的 Spark 行為)。 它在 Spark 3.x版本上提供了約 10% 的開箱即用效能改進,與 Spark 原生 SparkRecordMerger一起使用時效果更好。

批次插入使用NONE排序以匹配 Parquet 寫入

此版本調整了 BULK_INSERT 寫入操作的 NONE 排序模式(預設排序模式)的並行度。 從現在開始預設情況下,使用輸入並行性而不是隨機shuffle並行性 (hoodie.bulkinsert.shuffle.parallelism) 來寫入資料,以匹配預設的 parquet 寫入行為。這不會更改使用 NONE 排序模式的聚類行為。

BULK_INSERT 寫入操作行為的變更提高了開箱即用的寫入效能。

如果在預設的NONE排序方式下還是發現小檔案問題,我們建議在寫入Hudi表之前,先根據分割區路徑和記錄鍵對輸入資料進行排序。還可以使用 GLOBAL_SORT 來確保最佳檔案大小。

Deltstreamer 元同步失敗處理

在早期版本中,我們使用了一種快速失敗的方法,如果任何目錄同步失敗,則不會嘗試同步剩餘的目錄。 在 0.13.0 中,在任何目錄同步失敗的操作失敗之前嘗試同步到所有設定的目錄。 在一個目錄同步失敗的情況下,其他目錄的同步仍然可以成功,所以使用者現在只需要重試失敗的目錄即可。

不覆蓋內部後設資料表Metadata Table設定

由於錯誤設定可能導致資料完整性問題,在 0.13.0 中我們努力使使用者的後設資料表設定更加簡單。 在內部 Hudi 確定這些設定的最佳選擇,以實現系統的最佳效能和穩定性。

以下與後設資料表相關的設定是內部設定,使用者不能再顯式設定這些設定:

hoodie.metadata.clean.async
hoodie.metadata.cleaner.commits.retained
hoodie.metadata.enable.full.scan.log.files
hoodie.metadata.populate.meta.fields

Spark SQL CTAS 效能修復

以前由於設定錯誤,CTAS 寫入操作被錯誤地設定為使用 UPSERT。 在 0.13.0 版本中我們修復了這個問題,以確保 CTAS 使用 BULK_INSERT操作來提高初始化寫入 Hudi 表的效能。

在 0.13.0 之前,我們通過清理所有訊息來初始化 ckp 後設資料(檢查點相關後設資料)。但一些極端情況沒有得到正確處理。 例如:

  • 重新啟動作業時,寫任務無法正確獲取pending的instant。
  • 如果檢查點成功並且作業突然崩潰,則instant沒有時間提交。 資料丟失,因為最後一個pending的instant被回滾; 然而 Flink 引擎仍然認為檢查點/即時是成功的。

Q:為什麼要在 0.13.0 版本之前清理訊息?
A:為了防止時間線和訊息不一致
Q:為什麼我們要保留 0.13.0 版本中的訊息?
A:不一致有兩種情況:

  1. 時間線即時完成但 ckp 訊息正在傳輸(用於提交instant)。
  2. 時間軸時刻處於pending狀態,而 ckp 訊息未啟動(用於啟動新instant)。

對於case 1,不需要re-commit instant,如果write task在恢復的時候沒有得到pending instant就好了

對於case 2,instant基本上是pending的。instant將被回滾(如預期的那樣)。 因此保持 ckp 訊息實際上可以保持正確性。

重大特性

元伺服器Metaserver

在 0.13.0 中我們引入了後設資料集中管理服務 Metaserver。 這是我們在未來引入的平臺服務元件之一。 Metaserver 幫助使用者輕鬆管理資料湖平臺中的大量表。

注意:這是一項實驗性功能。

如需要在環境中設定元伺服器,請使用 hudi-metaserver-server-bundle 並將其作為 java 伺服器應用程式執行,例如 java -jar hudi-metaserver-server-bundle-<HUDI_VERSION>.jar。 在使用者端新增以下選項以與元伺服器整合:

hoodie.metaserver.enabled=true
hoodie.metaserver.uris=thrift://<server url>:9090

Metaserver 儲存 Hudi 表的後設資料,如表名、資料庫、所有者; 以及時間線的後設資料,如提交instant、action、state等。此外Metaserver 通過 Hudi Spark 包支援 Spark 寫入和讀取。

更改資料捕獲CDC

在 Hudi 表用作流源的情況下,我們希望瞭解屬於單個提交的記錄的所有變更。 例如我們想知道哪些記錄被插入、刪除和更新。 對於更新的記錄,後續管道可能希望獲取更新前的舊值和更新後的新值。 0.13.0之前,增量查詢不包含硬刪除記錄,使用者需要使用軟刪除流刪除,可能不符合GDPR要求。

Change-Data-Capture (CDC) 功能使 Hudi 能夠通過生成變更來顯示記錄是如何變更的,從而處理 CDC 查詢用例。

注意:CDC 是一項實驗性功能,當前僅支援COW 表。CDC 查詢尚不支援 MOR 表。

要使用 CDC,使用者需要先在寫入表時啟用它以記錄額外的資料,這些資料由 CDC 增量查詢返回。
對於寫入,設定 hoodie.table.cdc.enabled=true 並通過 hoodie.datasource.query.incremental.format指定 CDC 紀錄檔記錄模式,以控制記錄的資料。 有3種模式可供選擇:

  • data_before_after:這記錄了變更記錄的操作以及變更前後的整個記錄。 這種模式在儲存上產生最多的 CDC 資料,並且查詢 CDC 結果的計算量最少。
  • data_before:這記錄了變更記錄的操作和更改前的整個記錄。
  • op_key_only:這隻記錄變更記錄的操作和key。這種模式在儲存上產生最少的 CDC 資料,但查詢 CDC 結果需要最多的計算工作。

預設值為 data_before_after
CDC讀取設定如下設定:

hoodie.datasource.query.type=incremental
hoodie.datasource.query.incremental.format=cdc

和其他通常的增量查詢選項,如開始和結束即時時間,並返回 CDC 結果。

請注意,hoodie.table.cdc.enabled 是表設定。 一旦啟用就不允許為關閉它。 同樣不能更改 hoodie.table.cdc.supplemental.logging.mode,一旦它被儲存為表設定。

優化記錄負載處理

此版本引入了期待已久的支援,可將記錄作為其引擎原生表示進行處理,從而避免將它們轉換為中間形式 (Avro) 的需要。

注意:此功能處於實驗模式,目前僅支援 Spark。

通過引入新的 HoodieRecordMerger 抽象。 HoodieRecordMerger 是未來在 Hudi 中實現任何合併語意的核心和事實來源。在這種能力下,它取代了以前用於實現自定義合併語意的 HoodieRecordPayload 層次結構。 通過依賴 HoodieRecordMerger 形式的統一元件,我們可以在寫入操作的整個生命週期內以統一的方式處理記錄。 這大大減少了延遲,因為記錄現在是引擎原生表示,避免了不必要的複製、反序列化和轉換為中間表示 (Avro)。 在基準測試中,與 0.13.0 預設狀態相比,upsert 效能提高了 10%,與 0.12.2 相比提高了 20%。

如要啟用,需要為 Hudi 表指定不同的設定:

  • 對於 COW,指定 hoodie.datasource.write.record.merger.impls=org.apache.hudi.HoodieSparkRecordMerger
  • 對於 MOR,指定 hoodie.datasource.write.record.merger.impls=org.apache.hudi.HoodieSparkRecordMerger 和 hoodie.logfile.data.block.format=parquet

請注意,當前的 HoodieSparkRecordMerger 實現僅支援與 OverwriteWithLatestAvroPayload 類等效的合併語意,這是當前用於合併記錄的預設 HoodieRecordPayload 實現(設定為「hoodie.compaction.payload.class」)。 因此如果您正在使用任何其他 HoodieRecordPayload 實現,則需要等到它被相應的 HoodieRecordMerger 實現替換。

Deltastreamer 中的新源支援

Deltastreamer 是一個完全託管的增量 ETL 實用程式,支援各種資料來源。 在此版本中我們新增了三個新資料來源。

Proto Kafka源

Deltastreamer 已經支援從 Kafka 中一次性攝取 JSON 和 Avro 格式新事件。 ProtoKafkaSource 支援基於 Protobuf 類的模式。只需一個額外的設定,就可以輕鬆設定此源。 檢視檔案以獲取更多詳細資訊。

GCS 增量源

沿著 S3 事件源的路線,我們現在有一種可靠且快速的方法來通過 GcsEventsHoodieIncrSource 從 Google Cloud Storage (GCS) 中的物件中攝取。

Pulsar源

Apache Pulsar 是一個為雲構建的開源分散式訊息傳遞和流資料平臺。 PulsarSource 支援通過 Deltastreamer 從 Apache Pulsar 攝取。

支援部分負載更新

部分更新是社群中的一個常見用例,它需要能夠僅更新某些欄位而不是替換整個記錄。 以前我們建議使用者通過引入他們自己的自定義記錄負載實現來滿足此用例。 隨著該需求變得越來越普遍,在 0.13.0 版本中,我們新增了一個新的記錄有效負載實現 PartialUpdateAvroPayload以支援這種開箱即用的功能,因此使用者可以使用該實現而不必編寫自己的自定義實現。

一致性雜湊索引

我們引入了 Consistent Hashing Index 作為使用 Hudi 寫入的另一種索引選項。 這是對 0.11.0 版本中新增的 Bucket Index 的增強。 使用桶索引,每個分割區的桶/檔案組是靜態分配的,而使用一致性雜湊索引,桶可以動態增長,因此使用者無需擔心資料傾斜。 桶將根據每個分割區的負載因子擴充套件和收縮。 更多細節可參考此功能設計的 RFC

如需使用,設定如下

hoodie.index.type=bucket
hoodie.index.bucket.engine=CONSISTENT_HASHING
hoodie.bucket.index.max.num.buckets=128
hoodie.bucket.index.min.num.buckets=32
hoodie.bucket.index.num.buckets=4
## do split if the bucket size reach 1.5 * max_file_size
hoodie.bucket.index.split.threshold=1.5
## do merge if the bucket size smaller than 0.2 * max_file_size
hoodie.bucket.index.merge.threshold=0.1 

要強制縮小或擴大儲存桶,需要使用以下設定啟用Clustering

## check resize for every 4 commit
hoodie.clustering.inline=true
hoodie.clustering.inline.max.commits=4
hoodie.clustering.plan.strategy.class=org.apache.hudi.client.clustering.plan.strategy.SparkConsistentBucketClusteringPlanStrategy
hoodie.clustering.execution.strategy.class=org.apache.hudi.client.clustering.run.strategy.SparkConsistentBucketClusteringExecutionStrategy
## for supporting concurrent write & resizing
hoodie.clustering.updates.strategy=org.apache.hudi.client.clustering.update.strategy.SparkConsistentBucketDuplicateUpdateStrategy

Consistent Hashing Index 仍然是一個不斷髮展的特性,目前從 0.13.0 開始使用它有一些限制:

  • 只有使用 MOR 表的 Spark 引擎才支援此索引。
  • 它不適用於啟用後設資料表。
  • 要擴大或縮小桶,使用者必須使用上述設定(以某種節奏)手動觸發Clustering,但他們不能同時執行壓縮。
  • 因此如果常規寫入管道啟用了壓縮,請遵循以下建議:可以選擇每 12 小時觸發一次擴容/縮容。 在這種情況下,每 12 小時一次,可能需要禁用壓縮、停止寫入管道並啟用Clustering。 應該格外小心不要同時執行兩者,因為這可能會導致衝突和管道失敗。 Clustering完成後可以恢復常規寫入管道,這將啟用壓縮。

我們正在努力實現這些自動化,並使使用者更容易利用 Consistent Hashing Index。 可以在此處關注 Consistent Hashing Index 正在進行的工作。

多使用者端寫入的早期衝突檢測

Hudi提供樂觀並行控制(OCC),允許多個寫入使用者端在沒有重疊資料檔案寫入的情況下,並行寫入並原子提交到Hudi表,保證資料的一致性、完整性和正確性。 在0.13.0版本之前,這種重疊資料檔案的衝突檢測是在提交後設資料之前和資料寫入完成之後進行的。 如果在最後階段檢測到任何衝突,則可能會浪費計算資源,因為資料寫入已經完成。

為了提高並行控制,0.13.0版本引入了OCC早期衝突檢測的新特性,利用Hudi的標記機制,在資料寫入階段檢測到衝突,一旦檢測到衝突就提前中止寫入。 Hudi 現在可以更早地停止衝突寫入器,因為它可以及早檢測衝突並釋放叢集所需的計算資源,從而提高資源利用率。

注意:OCC 中的早期衝突檢測在 0.13.0 版本中是實驗性的。

預設情況下,此功能處於關閉狀態。 要嘗試這一點,使用者需要在使用 OCC 進行並行控制時將 hoodie.write.concurrency.early.conflict.detection.enable設定為 true(有關更多詳細資訊請參閱並行控制)。

寫入資料中的無鎖訊息佇列

在以前的版本中,Hudi 使用生產者-消費者模型通過有界記憶體佇列將傳入資料寫入表中。 在此版本中我們新增了一種新型佇列,利用 Disruptor,它是無鎖的。 當資料量很大時,這會增加寫入吞吐量。 將 1 億條記錄寫入雲端儲存上的 Hudi 表中的 1000 個分割區的基準顯示,與現有的有界記憶體佇列執行器型別相比,效能提高了 20%。

注意:DisruptorExecutor 作為實驗特性支援 Spark 插入和 Spark 批次插入操作

使用者可以設定 hoodie.write.executor.type=DISRUPTOR_EXECUTOR 來啟用該功能。 還有其他設定,如 hoodie.write.wait.strategyhoodie.write.buffer.size 可以進一步調整效能。

Hudi CLI 包

我們為 Spark 3.x 引入了一個新的 Hudi CLI Bundle,hudi-cli-bundle_2.12,使 Hudi CLI 更簡單易用。 使用者現在可以使用這個 bundle jar(已經發布到 Maven 倉庫)和 Hudi Spark bundle 來啟動指令碼來啟動帶有 Spark 的 Hudi-CLI shell,Hudi-CLI 可輕鬆部署,因為使用者不需要在本地編譯 Hudi CLI 模組、上傳 jar 和解決任何依賴衝突(如果有)。 可以在 Hudi CLI 頁面上找到詳細說明。

Flink 1.16.x 整合Hudi,在編譯原始碼時使用profile引數-Pflink1.16啟用版本。 或者使用 hudi-flink1.16-bundle。 Flink 1.15、Flink 1.14 和 Flink 1.13 也將繼續支援。

Json Schema轉換器

對於設定模式登入檔的 DeltaStreamer 使用者,新增了一個 JSON schema轉換器,以幫助將 JSON 模式轉換為目標 Hudi 表的 AVRO。 將 hoodie.deltastreamer.schemaprovider.registry.schemaconverter 設定為 org.apache.hudi.utilities.schema.converter.JsonToAvroSchemaConverter 以使用此功能。 使用者還可以實現此介面 org.apache.hudi.utilities.schema.SchemaRegistryProvider.SchemaConverter 以提供從原始模式到 AVRO 的自定義轉換。

通過 Spark SQL 設定 Hudi Config

使用者現在可以通過 Spark SQL conf 提供 Hudi 設定,例如,設定

spark.sql("set hoodie.sql.bulk.insert.enable = true")

確保 Hudi 在執行 INSERT INTO 語句時能夠使用 BULK_INSERT 操作

感謝

感謝參與0.13.0版本的所有貢獻者,歡迎廣巨量資料湖愛好者加入Apache Hudi社群,歡迎star & fork https://github.com/apache/hudi