Apache Hudi核心之檔案標記機制深入解析

leesf發表於2021-09-12

1. 摘要

Hudi 支援在寫入時自動清理未成功提交的資料。Apache Hudi 在寫入時引入標記機制來有效跟蹤寫入儲存的資料檔案。 在本部落格中,我們將深入探討現有直接標記檔案機制的設計,並解釋了其在雲端儲存(如 AWS S3、Aliyun OSS)上針對非常大批量寫入的效能問題。 並且演示如何通過引入基於時間軸伺服器的標記來提高寫入效能。

2. 為何引入Markers機制

Hudi中的marker是一個表示儲存中存在對應的資料檔案的標籤,Hudi使用它在故障和回滾場景中自動清理未提交的資料。

每個標記條目由三部分組成

  • 資料檔名
  • 標記副檔名 (.marker)
  • 建立檔案的 I/O 操作(CREATE - 插入、MERGE - 更新/刪除或 APPEND - 兩者之一)。

例如標記91245ce3-bb82-4f9f-969e-343364159174-0_140-579-0_20210820173605.parquet.marker.CREATE指示相應的資料檔案是91245ce3-bb82-4f9f-969e-343364159174-0_140-579-0_20210820173605.parquet 並且 I/O 型別是 CREATE。

在寫入每個資料檔案之前,Hudi 寫入客戶端首先在儲存中建立一個標記,該標記會被持久化,在提交成功後會被寫入客戶端顯式刪除。

標記對於寫客戶端有效地執行不同的操作很有用,標記主要有如下兩個作用

  • 刪除重複/部分資料檔案:通過 Spark 寫入 Hudi 時會有多個 Executor 進行併發寫入。一個 Executor 可能失敗,留下部分資料檔案寫入,在這種情況下 Spark 會重試 Task ,當啟用speculative execution時,可以有多次attempts成功將相同的資料寫入不同的檔案,但最終只有一次attempt會交給 Spark Driver程式程式進行提交。標記有助於有效識別寫入的部分資料檔案,其中包含與後來成功寫入的資料檔案相比的重複資料,並在寫入和提交完成之前清理這些重複的資料檔案。
  • 回滾失敗的提交:寫入時可能在中間失敗,留下部分寫入的資料檔案。在這種情況下,標記條目會在提交失敗時保留在儲存中。在接下來的寫操作中,寫客戶端首先回滾失敗的提交,通過標記識別這些提交中寫入的資料檔案並刪除它們。

接下來我們將深入研究現有的標記機制,闡述其效能問題,並演示新的基於時間軸伺服器的標記機制來解決該問題。

3. 現有的直接標記機制及其侷限性

現有的標記機制簡單地建立與每個資料檔案相對應的新標記檔案,標記檔名如前面所述。 每個 marker 檔案被寫入在相同的目錄層次結構中,即提交即時分割槽路徑,在Hudi表的基本路徑下的臨時資料夾.hoodie/.temp下。 例如,下圖顯示了向 Hudi 表寫入資料時建立的標記檔案和相應資料檔案的示例。 在獲取或刪除所有marker檔案路徑時,該機制首先列出臨時資料夾.hoodie/.temp/<commit_instant>下的所有路徑,然後進行操作。

雖然掃描整個表以查詢未提交的資料檔案效率更高,但隨著要寫入的資料檔案數量的增加,要建立的標記檔案的數量也會增加。 這可能會為 AWS S3 等雲端儲存帶來效能瓶頸。 在 AWS S3 中,每個檔案建立和刪除呼叫都會觸發一個 HTTP 請求,並且對儲存桶中每個字首每秒可以處理的請求數有速率限制。 當併發寫入的資料檔案數量和 marker 檔案數量巨大時,marker 檔案的操作會成為寫入效能的顯著效能瓶頸。而在像 HDFS 這樣的儲存上,使用者可能幾乎不會注意到這一點,其中檔案系統後設資料被有效地快取在記憶體中。

4. 基於時間線伺服器的標記機制提高寫入效能

為解決上述 AWS S3 速率限制導致的效能瓶頸,我們引入了一種利用時間線伺服器的新標記機制,該機制優化了儲存標記的相關延遲。 Hudi 中的時間線伺服器用作提供檔案系統和時間線檢視。 如下圖所示,新的基於時間線伺服器的標記機制將標記建立和其他標記相關操作從各個執行器委託給時間線伺服器進行集中處理。 時間線伺服器在記憶體中為相應的標記請求維護建立的標記,時間線伺服器通過定期將記憶體標記重新整理到儲存中有限數量的底層檔案來實現一致性。 通過這種方式,即使資料檔案數量龐大,也可以顯著減少與標記相關的實際檔案操作次數和延遲,從而提高寫入效能。

為了提高處理標記建立請求的效率,我們設計了在時間線伺服器上批量處理標記請求。 每個標記建立請求在 Javalin 時間線伺服器中非同步處理,並在處理前排隊。 對於每個批處理間隔,例如 20 毫秒,排程執行緒從佇列中拉出待處理的請求並將它們傳送到工作執行緒進行處理。 每個工作執行緒處理標記建立請求,並通過重寫儲存標記的底層檔案。有多個工作執行緒併發執行,考慮到檔案覆蓋的時間比批處理時間長,每個工作執行緒寫入一個不被其他執行緒觸及的獨佔檔案以保證一致性和正確性。 批處理間隔和工作執行緒數都可以通過寫入選項進行配置。

請注意工作執行緒始終通過將請求中的標記名稱與時間線伺服器上維護的所有標記的記憶體副本進行比較來檢查標記是否已經建立。 儲存標記的底層檔案僅在第一個標記請求(延遲載入)時讀取。 請求的響應只有在新標記重新整理到檔案後才會返回,以便在時間線伺服器故障的情況下,時間線伺服器可以恢復已經建立的標記。 這些確儲存儲和記憶體中副本之間的一致性,並提高處理標記請求的效能。

5. 標記相關的寫入選項

我們在 0.9.0 版本中引入了以下與標記相關的新寫入選項,以配置標記機制。

  • hoodie.write.markers.type,要使用的標記型別。支援兩種模式: direct,每個資料檔案對應的單獨標記檔案由編寫器直接建立; timeline_server_based,標記操作全部在時間線服務中處理作為代理。 為了提高效率新的標記條目被批處理並儲存在有限數量的基礎檔案中。預設值為direct
  • hoodie.markers.timeline_server_based.batch.num_threads,用於在時間軸伺服器上批處理標記建立請求的執行緒數。預設值為20。
  • hoodie.markers.timeline_server_based.batch.interval_ms,標記建立批處理的批處理間隔(以毫秒為單位)。預設值為50。

6. 效能

我們通過使用 Amazon EMR 和 Spark 和 S3 批量插入大規模資料集來評估directtimeline_server_based的標記機制的寫入效能。 輸入資料大約為 100GB。 我們通過設定最大 parquet 檔案大小為 1MB 和並行度為 240 來配置寫入操作以併發生成大量資料檔案。 正如我們之前提到的,而直接標記機制的延遲對於較小數量的增量寫入是可以接受的,對於產生更多資料檔案的大批量插入/寫入,開銷會急劇增加。

如下圖所示,由於是批處理,基於時間線伺服器的標記機制生成的儲存標記的檔案要少得多,從而導致標記相關的 I/O 操作的時間要少得多,因此與直接相比,寫入完成時間減少了 31%。 標記檔案機制。

7. 總結

我們發現由於 AWS S3 等雲端儲存上檔案建立和刪除呼叫的速率限制,現有的直接標記檔案機制會導致效能瓶頸。 為了解決這個問題我們引入了一種利用時間線伺服器的新標記機制,它將標記建立和其他與標記相關的操作從各個 Executor 委託給時間線伺服器,並使用批處理來提高效能。使用 Spark 和 S3 在 Amazon EMR 上進行的效能評估表明,與標記相關的 I/O 延遲和整體寫入時間有所減少。

相關文章