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

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 提供了不同的開箱即用的索引策略,可以分爲全局或非全局索引

全局與非全局索引

  • 非全局索引:Hudi 確保一對分區路徑和記錄鍵在整個表中是唯一的。 索引查找性能與正在攝取的傳入記錄之間的匹配分區的大小成正比。 ‍

  • 全局索引:該索引策略在表的所有分區中強制執行鍵的唯一性,即保證對於給定的記錄鍵,表中恰好存在一條記錄。 全局索引提供了更強的保證,但是更新/刪除成本隨着表的大小而增長。

由於唯一性保證的差異,全局與非全局之間的主要考慮因素之一與索引查找延遲有關:
非全局索引僅查找匹配的分區:例如如果有 100 個分區並且傳入的批處理僅包含最後 2 個分區的記錄,則只會查找屬於這 2 個分區的文件組。 對於大規模的更新插入工作負載可能需要考慮非全局索引,例如非全局布隆、非全局簡單索引和桶索引。
全局索引查看所有分區中的所有文件組:例如如果有 100 個分區並且傳入的記錄批次中有最後 2 個分區的記錄,則將查找所有 100 個分區中的所有文件組(因爲 Hudi 必須保證整個表中只有一個版本的記錄鍵)。 這會增加大規模更新插入工作負載的延遲。

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

  • 布隆索引:這是一種索引策略,可以有效地管理文件組中的更新插入和記錄查找。 該索引利用布隆過濾器,這是一種概率數據結構,有助於確定給定記錄鍵是否存在於特定文件組中。適用於全局和非全局索引。

  • 簡單索引:這是一種索引策略,它提供了一種將記錄鍵映射到其相應文件組的直接方法。它針對從存儲表中提取的鍵執行傳入更新/刪除記錄的連接。適用於全局和非全局索引。

  • HBase索引:該索引策略使用HBase存儲索引來映射記錄鍵及其在文件組中對應的文件位置。 適用於全局索引。

  • 桶索引:這是一種索引策略,它使用散列將記錄路由到靜態分配的文件組。 適用於非全局索引。

  • 一致性哈希桶索引:這是一種索引策略,是桶索引的高級版本。 雖然桶索引需要爲每個分區預先分配文件組,但使用一致的哈希索引可以根據負載動態地增加或收縮每個分區的文件組。 適用於非全局索引。

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

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

分區路徑粒度

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

一個常見的誤區是將分區設置得過於精細,例如按 <city>/<day>/<hour> 劃分分區。 根據工作負載每小時粒度的數據可能不足,從而導致許多隻有幾千字節的小文件。 如果小文件越多,磁盤尋道成本就越高,查詢性能就會下降。 其次在攝取方面,小文件也會影響索引查找,因爲修剪不相關文件需要更長的時間。 根據正在使用的索引策略,這可能會影響寫入性能。因此建議用戶始終從較粗糙的分區方案開始,如 / 以避免小文件的問題,如果仍然覺得需要粒度分區,可以根據查詢模式重新評估分區方案和/或可以潛在地利用Clustering服務來平衡攝取和查詢性能。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章