Apache Hudi的索引類型及應用場景


Apache Hudi使用索引來定位更刪操作所在的文件組。對於Copy-On-Write表,索引能加快更刪的操作,因為避免了通過連接整個數據集來決定哪些文件需要重寫。對於Merge-On-Read表,這個設計,對於任意給定的基文件,能限定要與其合並的記錄數量。具體地,一個給定的基文件只需要和其所包含的記錄的更新合並。相比之下,沒有索引的設計(比如Apache Hive ACID),可能會導致需要把所有基文件與所有更刪操作合並。

從高角度看,索引把一個記錄的鍵加一個可選的分區路徑映射到存儲上的文件組ID(更多細節參考這里)。在寫入過程中,我們查找這個映射然后把更刪操作導向基文件附帶的日志文件(MOR表),或者導向最新的需要被合並的基文件(COW表)。索引也使得Hudi可以根據記錄鍵來規定一些唯一性的限制。

記錄更新(黃色塊)和基文件(白色塊)合並消耗的對比

目前Hudi已經支持了幾種不同的索引機制,並且在工具庫中不斷地完善和增加更多機制,在本文余下的篇幅中將根據我們的經驗介紹幾種不同作業場景下索引機制的選擇。我們也會穿插一些對已有限制、即將開展的工作、以及優化和權衡方面的評論。

Hudi的索引類型

目前Hudi支持以下幾種索引類型。

  • 布隆索引(默認):使用以記錄的鍵生成的布隆過濾器,也可以用記錄鍵對可能對應的文件進行剪枝操作。
  • 簡單索引:對更刪的記錄和存儲上的表里提取的鍵進行輕量級的連接。
  • HBase索引:使用外部的Apache HBase表來管理索引映射。

寫入器可以通過hoodie.index.type來設置以上的類型。此外,自定義的索引實現可以通過hoodie.index.class來配置。對Apache Spark寫入器需提供SparkHoodieIndex的子類。

另一個需要了解的關鍵點是區分全局索引和非全局索引。布隆索引簡單索引都有一個全局選項,分別是hoodie.index.type=GLOBAL_BLOOMhoodie.index.type=GLOBAL_SIMPLEHBase索引本質上就是全局索引。

  • 全局索引:全局索引在全表的所有分區范圍下強制要求鍵的唯一性,也就是確保對給定的鍵有且只有一個對應的記錄。全局索引提供了更強的保證,而更刪的消耗隨着表的大小增加而增加(O(表的大小)),但仍可能對小表適用。
  • 非全局索引:這個默認的索引實現只在一個分區里強制要求了這樣的限制。由此可見,非全局索引依靠寫入器為同一個記錄的更刪提供一致的分區路徑,但由此同時大幅提高了效率,因為索引查詢復雜度成了O(更刪的記錄數量)且可以很好地應對寫入量的擴展。

由於數據可能有着不同的體量、速度和讀取規律,不同索引會適用於不同的作業場景。接下來讓我們分析幾個不同的場景來討論如何選擇適合的Hudi索引。

作業場景:對事實表的延遲更新

許多公司會在NoSQL數據存儲中存放大量的交易數據。例如共享出行的行程表、股票買賣記錄的表、和電商的訂單表。這些表通常一直在增長,且大部分的更新隨機發生在較新的記錄上,而對舊記錄有着長尾分布型的更新。這通常是源於交易關閉或者數據更正的延遲性。換句話說,大部分更新會發生在最新的幾個分區上而小部分會在舊的分區。

典型的事實表的更新樣式。

對於這樣的作業模式,布隆索引就能表現地很好,因為查詢索引可以靠設置得當的布隆過濾器來剪枝很多數據文件。另外,如果生成的鍵可以以某種順序排列,參與比較的文件數會進一步通過范圍剪枝而減少。Hudi用所有文件的鍵域來構造區間樹,這樣能來高效地依據輸入的更刪記錄的鍵域來排除不匹配的文件。

為了高效地把記錄鍵和布隆過濾器進行比對,即盡量減少過濾器的讀取和均衡執行器間的工作量,Hudi緩存了輸入記錄並使用了自定義分區器和統計規律來解決數據的偏斜。有時,如果布隆過濾器的偽正率過高,查詢會增加數據的打亂操作。Hudi支持動態布隆過濾器(設置hoodie.bloom.index.filter.type=DYNAMIC_V0)。它可以根據文件里存放的記錄數量來調整大小從而達到設定的偽正率。

在不久的將來,我們計划引入一個更快的布隆索引機制。該機制會在Hudi內部的元數據表中跟蹤記錄布隆過濾器和取值范圍從而進行快速的點查詢。這可以避免因從基文件中讀取布隆過濾器和取值范圍而導致的查詢的局限性。(總體設計請參考RFC-15

作業場景:對事件表的去重

事件流無處不在。從Apache Kafka或其他類似的消息總線發出的事件數通常是事實表大小的10-100倍。事件通常把時間(到達時間、處理時間)作為首類處理對象,比如物聯網的事件流、點擊流數據、廣告曝光數等等。由於這些大部分都是僅追加的數據,插入和更新只存在於最新的幾個分區中。由於重復事件可能發生在整個數據管道的任一節點,在存放到數據湖前去重是一個常見的需求。

上圖演示了事件表的更新分布情況。

總的來說,低消耗去重一個非常挑戰的工作。盡管甚至可以用一個鍵值存儲來實現去重(即HBase索引),但索引存儲的消耗會隨着事件數增長而線性增長以至於變得不可行。事實上,有范圍剪枝功能的布隆索引是最佳的解決方案。我們可以利用作為首類處理對象的時間來構造由事件時間戳和事件id(event_ts+event_id)組成的鍵,這樣插入的記錄就有了單調增長的鍵。這會在最新的幾個分區里大幅提高剪枝文件的效益。

作業場景:對維度表的隨機更刪

這種類型的表通常包含高緯度的數據和數據鏈接,比如用戶資料、商家信息等。這些都是高保真度的表。它們的更新量通常很小但所接觸的分區和數據文件會很多,范圍涉及從舊到新的整個數據集。有時因為沒有很好的分區條件,這些表也會不分區。

上圖演示了維度表的更新分布情況。

正如之前提到的,如果范圍比較不能剪枝許多文件的話,那么布隆索引並不能帶來很好的效益。在這樣一個隨機寫入的作業場景下,更新操作通常會觸及表里大多數文件從而導致布隆過濾器依據輸入的更新對所有文件標明真正(true positive)。最終會導致,即使采用了范圍比較,也還是檢查了所有文件。使用簡單索引對此場景更合適,因為它不采用提前的剪枝操作,而是直接和所有文件的所需字段連接。如果額外的運維成本可以接受的話,也可以采用HBase索引,其對這些表能提供更加優越的查詢效率。

當使用全局索引時,用戶也可以考慮通過設置hoodie.bloom.index.update.partition.path=true或者hoodie.simple.index.update.partition.path=true(Global Bloom)hoodie.hbase.index.update.partition.path=true或者hoodie.hbase.index.update.partition.path=true 或者來處理分區路徑需要更新的情況;例如對於以所在城市分區的用戶表,會有用戶遷至另一座城市的情況。這些表也非常適合采用Merge-On-Read表型。

將來我們計划在Hudi內實現記錄層的索引機制,以此提高索引查詢效率,同時也將省去維護額外系統(比如HBase)的開銷。

總結

若沒有索引功能,Hudi就不可能在超大擴展規模上實現更刪操作。希望這篇文章為目前的索引機制提供了足夠的背景知識和對不同權衡取舍的解釋。

以下是一些頗具意義的相關工作:

  • 基於Apache Flink並建立在RocksDB狀態存儲上的索引機制將帶來真正意義上的數據湖流式插入更新。
  • 全新的元數據索引將基於Hudi元數據全面翻新現有的布隆索引機制。
  • 記錄層的索引實現,用另一個Hudi表作為二級索引。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM