數據湖框架選型很糾結?一文了解Apache Hudi核心優點

英文原文:https://hudi.apache.org/blog/hudi-indexing-mechanisms/html

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

從高角度看,索引把一個記錄的鍵加一個可選的分區路徑映射到存儲上的文件組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索引。code

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

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

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

對於這樣的做業模式,布隆索引就能表現地很好,由於查詢索引能夠靠設置得當的布隆過濾器來剪枝不少數據文件。另外,若是生成的鍵能夠以某種順序排列,參與比較的文件數會進一步經過範圍剪枝而減小。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表做爲二級索引。

在接下來的開發中,項目組會對這個領域保持積極的投入。咱們始終期待更多貢獻者的加入以及推動路線圖中的項目。若是有意參與,歡迎與咱們的社區聯繫

相關文章
相關標籤/搜索