天天看點

提升 Apache Hudi Upsert 性能的三個建議

作者:Lakehouse

Apache Hudi 社群一直在快速發展,各公司正在尋找方法來利用其強大的功能來有效地攝取和管理大規模資料集。每周社群都會收到一些常見問題,最常見的問題與 Hudi 如何執行更新插入有關,以確定以低延遲通路最新資料。

選擇合适的存儲表類型

快速更新插入的主要考慮因素之一是選擇正确的存儲表類型。Hudi 支援兩種不同的存儲表類型——Copy-On-Write (COW) 和 Merge-On-Read (MOR)。由于處理資料更新的方法不同,每種表類型都會對 upsert 性能産生不同的影響。

COW表

與 MOR 表相比,COW 表的操作更簡單,因為所有更新都寫入 Apache Parquet 格式的基礎檔案。不需要運作像壓縮這樣的單獨服務來管理任何日志檔案以提高讀取或存儲效率。COW通過完全重寫檔案以生成新版本的基本檔案來處理更新。是以 COW 表表現出更高的寫放大,因為建立新的基本檔案版本會進行同步合并。然而 COW 表的一個關鍵優勢是它們的零讀取放大,因為所有資料都在基礎檔案中可用,随時可以讀取。查詢所需的磁盤讀取很少,因為它們不需要讀取多個位置或合并資料。

MOR表

與 COW 表相比,MOR 表具有更高的操作複雜性。MOR 不會重寫整個檔案,而是将更新寫入單獨的日志檔案,然後這些日志檔案稍後與基本檔案合并為一個新的檔案版本,這是通過壓縮服務完成的。需要壓縮來限制日志檔案的增長,這樣查詢性能就不會下降并優化存儲。

直接寫入日志檔案避免了多次重寫整個基本檔案,進而降低了寫入放大——如果正在處理流資料,這種差異就會變得很明顯,也就是說 MOR 表進行了寫入優化。但是由于需要讀取基本檔案和日志檔案并動态合并資料,MOR 表在壓縮之間對快照查詢有更高的讀取放大。

COW 和 MOR 表的注意事項

如果更新插入比率很高并且對攝取延遲很敏感,那麼更适合使用 MOR 表。如流資料源——通常會希望更快地根據洞察采取行動,以便為使用者提供相關和及時的資訊。但是如果工作負載更多地基于插入,并且可以容忍合理的攝取延遲,那麼更适合使用 COW 表。

根據記錄鍵選擇正确的索引類型

通過利用索引,Hudi 在更新插入期間查找記錄時避免全表掃描,這比較耗費時間和資源。Hudi 的索引層将記錄鍵映射到相應的檔案位置,索引層是可插拔的,有多種索引類型可供選擇。需要考慮的是索引延遲取決于多種因素,例如正在攝取多少資料、表中有多少資料、是否有分區表或非分區表、選擇的索引類型、工作負載的更新程度和記錄鍵的時間特性。根據所需的性能和唯一性保證,Hudi 提供了不同的開箱即用的索引政策,可以分為全局或非全局索引[1]。

全局與非全局索引

  • • 非全局索引:Hudi 確定一對分區路徑和記錄鍵在整個表中是唯一的。索引查找性能與正在攝取的傳入記錄之間的比對分區的大小成正比。‍
  • • 全局索引:該索引政策在表的所有分區中強制執行鍵的唯一性,即保證對于給定的記錄鍵,表中恰好存在一條記錄。全局索引提供了更強的保證,但是更新/删除成本随着表的大小而增長。

由于唯一性保證的差異,全局與非全局之間的主要考慮因素之一與索引查找延遲有關:

非全局索引僅查找比對的分區:例如如果有 100 個分區并且傳入的批處理僅包含最後 2 個分區的記錄,則隻會查找屬于這 2 個分區的檔案組。對于大規模的更新插入工作負載可能需要考慮非全局索引,例如非全局布隆、非全局簡單索引和桶索引。

全局索引檢視所有分區中的所有檔案組:例如如果有 100 個分區并且傳入的記錄批次中有最後 2 個分區的記錄,則将查找所有 100 個分區中的所有檔案組(因為 Hudi 必須保證整個表中隻有一個版本的記錄鍵)。這會增加大規模更新插入工作負載的延遲。

Hudi 提供開箱即用的索引類型

  • • 布隆索引:這是一種索引政策,可以有效地管理檔案組中的更新插入和記錄查找。該索引利用布隆過濾器,這是一種機率資料結構,有助于确定給定記錄鍵是否存在于特定檔案組中。适用于全局和非全局索引。
  • • 簡單索引:這是一種索引政策,它提供了一種将記錄鍵映射到其相應檔案組的直接方法。它針對從存儲表中提取的鍵執行傳入更新/删除記錄的連接配接。适用于全局和非全局索引。
  • • HBase索引:該索引政策使用HBase存儲索引來映射記錄鍵及其在檔案組中對應的檔案位置。适用于全局索引。
  • • 桶索引:這是一種索引政策,它使用散列将記錄路由到靜态配置設定的檔案組。适用于非全局索引。
  • • 一緻性哈希桶索引:這是一種索引政策,是桶索引的進階版本。雖然桶索引需要為每個分區預先配置設定檔案組,但使用一緻的哈希索引可以根據負載動态地增加或收縮每個分區的檔案組。适用于非全局索引。

更新密集型工作負載要考慮的索引類型

  • • Bloom 索引:如果記錄鍵按某些标準(例如基于時間戳)排序并且更新與最近的資料集相關,那麼這對于更新繁重的工作負載是一個很好的索引政策。例如如果記錄鍵是根據時間戳排序的,并且我們在最近幾天更新資料。
    • • Bloom 索引用例:假設每 10 分鐘就會攝取一批新資料。我們假設新批次包含最近 3 天内的資料更新。Hudi 根據布隆索引,識别出檔案組中的候選更新記錄,并從基礎檔案頁腳中擷取布隆過濾器,進一步裁剪檔案組中每個檔案中要查找的記錄。如果沒有找到記錄則被視為插入。‍
  • • 簡單索引:如果偶爾更新整個表範圍内的檔案并且記錄鍵是随機的,即不基于時間戳,那麼這對于更新繁重的工作負載是一個很好的索引政策。
    • • 簡單索引用例:如果有一個次元表,其中記錄鍵是旅行 ID(随機 UUID)并且分區是按城市 ID。如果我們要更新分布在一系列城市的 10000 條行程,Hudi 首先根據傳入的城市 ID 識别相關分區。Hudi 通過執行連接配接有效地找到包含記錄的檔案。
  • • 桶索引:如果每個分區存儲的資料總量在所有分區中都相似,這是一個很好的索引政策。每個分區的桶(或檔案組)數量必須預先為給定的表定義。更多細節參考如下文檔[2]。
    • • 桶索引用例:當定義桶數量後,Hudi會對記錄鍵應用一個哈希函數來将記錄均勻地分布在桶中。哈希函數将每個記錄 ID 配置設定給一個桶号,當更新時 Hudi 将哈希函數應用于記錄 ID 并确定相應的桶,然後 Hudi 将寫入委托給相應的桶(檔案組)。

分區路徑粒度

分區是一種技術,用于根據資料集中的某些屬性或列将大型資料集拆分為較小的、易于管理的部分。這可以大大提高查詢性能,因為在查詢期間隻需要掃描資料的一個子集。然而分區的有效性在很大程度上取決于分區的粒度。

一個常見的誤區是将分區設定得過于精細,例如按

<city>/<day>/<hour>

劃分分區。根據工作負載每小時粒度的資料可能不足,進而導緻許多隻有幾千位元組的小檔案。如果小檔案越多,磁盤尋道成本就越高,查詢性能就會下降。其次在攝取方面,小檔案也會影響索引查找,因為修剪不相關檔案需要更長的時間。根據正在使用的索引政策,這可能會影響寫入性能。是以建議使用者始終從較粗糙的分區方案開始,如 / 以避免小檔案的問題,如果仍然覺得需要粒度分區,可以根據查詢模式重新評估分區方案和/或可以潛在地利用Clustering服務來平衡攝取和查詢性能。

引用連結

[1]

全局或非全局索引: [https://medium.com/@simpsons/global-vs-non-global-index-in-apache-hudi-ac880b031cbc](https://medium.com/@simpsons/global-vs-non-global-index-in-apache-hudi-ac880b031cbc)

[2]

文檔: [https://medium.com/@simpsons/speed-up-your-write-latencies-using-bucket-index-in-apache-hudi-2f7c297493dc](https://medium.com/@simpsons/speed-up-your-write-latencies-using-bucket-index-in-apache-hudi-2f7c297493dc)