Apache Hudi數據跳過技術加速查詢高達50倍
- 2022 年 7 月 18 日
- 筆記
介紹
在 Hudi 0.10 中,我們引入了對高級數據布局優化技術的支持,例如 Z-order和希爾伯特空間填充曲線(作為新的聚類算法),即使在經常使用過濾器查詢大表的複雜場景中,也可以在多個列而非單個列上進行數據跳過。
但實際上什麼是Data Skipping數據跳過?
隨着存儲在數據湖中的數據規模越來越大,數據跳過作為一種技術越來越受歡迎。 數據跳過本質上是各種類型索引的通用術語,使查詢引擎能夠有效地跳過數據,這與它當前執行的查詢無關,以減少掃描和處理的數據量,節省掃描的數據量以及( 潛在地)顯着提高執行時間。 讓我們以一個簡單的非分區parquet表「sales」為例,它存儲具有如下模式的記錄:
此表的每個 parquet 文件自然會在每個相應列中存儲一系列值,這些值與存儲在此特定文件中的記錄集相對應,並且對於每個列 parquet 將遵循自然順序(例如,字符串、日期、整數等) 或推導一個(例如,複合數據類型 parquet 按字典順序對它們進行排序,這也匹配其二進制表示的排序)。
但是如果有一個排序和一個範圍……還有最小值和最大值!現在意味着每個 Parquet 文件的每一列都有明確定義的最小值和最大值(也可以為 null)。最小值/最大值是所謂的列統計信息的示例 – 表徵存儲在列文件格式(如 Parquet)的單個列中的值範圍的指標,比如
- 值的總數
- 空值的數量(連同總數,可以產生列的非空值的數量)
- 列中所有值的總大小(以位元組為單位)(取決於使用的編碼、壓縮等)
配備了表徵存儲在每個文件的每個單獨列中的一系列值的列統計信息,現在讓我們整理下表:每一行將對應於一對文件名和列,並且對於每個這樣的對,我們將寫出相應的統計數據:最小值,最大值,計數,空計數:
這本質上是一個列統計索引!
為方便起見我們對上表進行轉置,使每一行對應一個文件,而每個統計列將分叉為每個數據列的自己的副本:
這種轉置表示為數據跳過提供了一個非常明確的案例:
對於由列統計索引索引的列 C1、C2、… 上的謂詞 P1、P2、… 的查詢 Q,我們可以根據存儲在索引中的列統計信息評估這些謂詞 P1、P2 等對於表的每個對應文件,以了解特定文件「file01」、「file02」等是否可能包含與謂詞匹配的值。這種方法正是 Spark/Hive 和其他引擎所做的,例如,當他們從 Parquet 文件中讀取數據時——每個單獨的 Parquet 文件都存儲自己的列統計信息(對於每一列),並且謂詞過濾器被推送到 Parquet Reader 它能夠評估所討論的查詢是否符合存儲在列中(在文件中)的數據條件,從而避免在文件不包含任何與查詢謂詞匹配的數據的情況下對數據進行不必要的提取、解壓縮和解碼。
但是如果 Parquet 已經存儲了列統計信息,那麼創建附加索引有什麼意義呢?
每個 Parquet 文件僅單獨存儲我們上面組合的索引中的一行。這種方法的明顯缺點是,要了解哪些文件可能包含查詢正在尋找的數據,查詢引擎必須讀取表中影響查詢性能的每個 Parquet 文件的 Parquet 頁腳(甚至可能導致來自雲的限制)存儲)與以更緊湊格式表示的專用索引相比。
Hudi 0.11 中的列統計索引和數據跳過
在 Hudi 0.10 中,我們引入了非常簡單的列統計索引(存儲為簡單的 Parquet 表)的權宜之計實現,以支持 Hudi 中數據跳過實現的第一個版本,以展示 Z-order 和 Hilbert 的強大功能空間填充曲線作為高級布局優化技術。
在 Hudi 0.11 中,我們在元數據表中引入了多模索引,例如布隆過濾器索引和列統計索引,這兩者都實現為元數據表中的專用分區(分別為「column_stats」和「bloom_filters」)。雖然這些新索引仍處於試驗階段,但將列統計索引移動到元數據表中意味着更多:
- 強大的支持:列統計索引 (CSI) 現在還享有元數據表的一致性保證
- 高效實現:元數據表使用 HFile 作為基礎文件和日誌文件格式,促進基於鍵的快速查找(排序鍵值存儲)。實際上意味着對於具有大量列的大型表,我們不需要讀取整個列統計索引,並且可以通過查找查詢中引用的列來簡單地投影其部分。
設計
在這裡,我們將介紹新列統計索引設計的一些關鍵方面。如果您對更多詳細信息感興趣,請查看 RFC-27 了解更多詳細信息。
列統計索引作為獨立分區保留在元數據表中(指定為「column_stats」)。為了能夠在保持靈活性的同時跟上最大表的規模,可以將索引配置為分片到多個文件組中,並根據其鍵值將單個記錄散列到其中的任何一個中。要配置文件組的數量,請使用以下配置(默認值為 2):
如前所述,元數據表使用 HFile 作為其存儲文件格式(這是一種非常有效的排序二進制鍵值格式),以便能夠
- 有效地查找基於它們的鍵的記錄以及
- 根據鍵的前綴有效地掃描記錄範圍
為了解釋如何在列統計索引中使用它,讓我們看一下它的記錄鍵的組成:
用列前綴索引記錄的鍵不是隨機的,而是由以下觀察引起的
- 通過 HFile 存儲所有排序的鍵值對,這樣的鍵組合提供了與特定列 C 相關的所有記錄的局部性的良好屬性
- 對原始表的任何給定查詢通常只過濾少數列,這意味着我們可以通過避免讀取完整索引來尋求效率,而是簡單地將其連續切片投影到列 C1、C2 等查詢過濾上
為了更好地舉例說明,讓我們看一下 C2 列上的查詢 Q 過濾:
我們可以簡單地讀取一個連續的記錄塊,而無需 a) 讀取整個索引(可能很大),也不需要 b) 隨機尋找我們感興趣的記錄。這使我們能夠在非常大的表上獲得可觀的性能改進。
基準測試
為了全面演示列統計索引和數據跳過功能,我們將使用眾所周知的 Amazon 評論數據集(僅佔用 50Gb 存儲空間),以便任何人都可以輕鬆複製我們的結果,但是使用稍微不常見的攝取配置來展示列統計索引和數據跳過帶來的效率如何隨着數據集中的文件數量而變化。
攝取
為了將 Amazon 評論數據集提取到 Hudi 表中,我們使用了這個gist。
請注意,您必須指定以下配置屬性以確保在攝取期間同步構建列統計索引:
但是,如果您想在當前沒有列統計索引的現有表上運行實驗,您可以利用異步索引器功能回填現有表的索引。
查詢
請注意要查看數據跳過操作,需要執行以下操作:
- 確保在讀取路徑上啟用了元數據表
- 數據跳過功能已啟用
為此必須將以下 2 個屬性指定為 Spark 或 Hudi 選項:
默認情況下元數據表僅在寫入端啟用,如果讀者願意在讀取路徑上利用元數據表,他們仍然必須明確指定相應的配置
請查看此gist以了解如何查詢先前攝取的數據集。
EMR 配置
所有測試都在具有以下配置的小型 EMR 集群上執行,如果您選擇這樣做可以輕鬆地重現相同的結果。
節點:m5.xlarge(1 個 master / 3 個 executor)
Spark:OSS 3.2.1(Hadoop 3.2)
運行非分區 COW 表
請注意我們故意壓縮文件大小以生成大量有意義的文件,因為數據集只有 50Gb。
- 數據集:亞馬遜評論(約 50Gb 未壓縮)
- 記錄:161M(~160 位元組)
- 表類型:COW(非分區)
- 文件大小:1Mb
- 文件數:~39k(總大小~47Gb,壓縮,zstd)
- 列統計:21 列(~847k 記錄,~63 Mb)
- 預熱:否(冷緩存,每次都重新啟動 shell 以刷新任何緩存)
從上表中可以很容易地看出,由 Hudi 0.11 中的新列統計索引提供支持的數據跳過顯着提高了查詢的執行性能(與其修剪潛力成正比),減少了執行運行時間並節省了關鍵的計算資源 直接轉化為基於 Hudi 的基於雲的 Lakes 和 Lakehouses 的成本節約。
儘管現在 Hudi 用戶已經可以使用列統計索引和數據跳過的功能,但目前還有更多工作要做:
- 支持 Merge-On-Read 表中的數據跳過
- 為列統計索引查詢添加緩存
- 進一步分析和優化列統計索引性能
如果您想關注當前正在進行的工作,請查看 HUDI-1822 並留下您的評論。