布隆過濾器是一項非常有用的Data-skipping技術。該技術可以快速判斷表文件中是否包含要查詢的數據,如果不包含就及時跳過該文件,從而減少掃描的數據量,提升查詢性能。
詳細內容可參考Databricks官網文章:Bloom過濾索引
如果在表的某列上創建了布隆過濾器索引,并且使用where col = "something"
作為查詢條件,那么在掃描表中文件時,我們可以使用布隆過濾器索引得出兩種結論:文件中肯定不包含col = "something"
的行,或者文件有可能包含col = "something"
的行。
當得出文件中肯定不包含
col = "something"
的行的結論時,就可以跳過該文件,從而減少掃描的數據量,提升查詢性能。當得出文件中可能包含
col = "something"
的行的結論時,引擎才會去處理該文件。注意,這里僅僅是判斷該文件中可能包含目標數據。布隆過濾器定義了一個指標,用于描述出現判斷失誤的概率,即判斷文件中包含需要查詢的數據,而實際上該文件并不包含目標數據的概率,并稱之為FPP(False Positive Probability: 假陽性概率)。
Databricks支持文件級Bloom過濾器,如果在表的某些列創建了布隆過濾器索引,那么該表的每個表文件都會關聯一個 Bloom 篩選器索引文件,索引文件存儲在 _delta_log 同級目錄下的 _delta_index 目錄中。在讀取表文文件之前,Databricks會檢查索引文件,根據上面的步驟判斷表文件中是否包含需要查詢的數據,如果不包含則直接跳過,否則再進行處理。
布隆過濾器索引的使用
創建布隆過濾器索引
CREATE BLOOMFILTER INDEX
ON TABLE table_name
FOR COLUMNS(col_name OPTIONS (fpp=0.1, numItems=50000000))
fpp:假陽性概率,假陽性概率越低,過濾越準確,但索引文件越大(使用更多的位來判斷數據是否存在于文件中)
numItems:該列可能出現的值的種類
布隆過濾器支持對:byte、short、int、long、float、double、date、timestamp和string類型的列建立索引。需要注意的是:布隆過濾器不會對null值建立索引,因此如果你的查詢條件中使用 where x is null 類似的語句,則即使在x列建立了布隆過濾器索引,該索引也不會生效。此外,嵌套的列也不支持布隆過濾器索引。
刪除布隆過濾器索引
DROP BLOOMFILTER INDEX ON TABLE table_name FOR COLUMNS(col_name);
查看表上已建立的布隆過濾器索引
DESCRIBE EXTENDED table_name;
使用案例
創建表:創建一張測試表,該表包括ID以及ID % 50000000的sha加密。
CREATE OR REPLACE TABLE bloom_test (
id BIGINT NOT NULL,
str1 STRING NOT NULL,
sha STRING NOT NULL,
sha1 STRING NOT NULL,
sha2_256 STRING NOT NULL,
row_hash_too_big STRING NOT NULL,
row_hash STRING NOT NULL
)
USING DELTA
location "oss://databricks-delta-demo/bloom_filter"
在該測試表的列 sha 上創建布隆過濾器索引。在sha列有50,000,000種可能出現的值,假陽性的概率為0.1 (即判斷某數據存在于文件中,但實際并不在的概率)。
CREATE BLOOMFILTER INDEX
ON TABLE bloom_test
FOR COLUMNS(sha OPTIONS (fpp=0.1, numItems=50000000))
生成測試數據:
WITH sample (
SELECT
id,
'windows.exe' as str1,
monotonically_increasing_id() mono_id,
hash(id) hash,
sha (cast(id % 50000000 as string)) sha,
sha1(cast(id % 50000000 as string)) sha1,
sha2(cast(id as string), 256) sha2_256
from
RANGE(0, 1000000000, 1, 448) -- start, end, step, numPartitions
)
INSERT INTO bloom_test
SELECT id,
str1,
sha,
sha1,
sha2_256,
sha2(concat_ws('||',id, str1, mono_id, hash, sha, sha1, sha2_256),512) row_hash_too_big,
sha2(concat_ws('||',id, str1, mono_id, hash, sha, sha1, sha2_256),256) row_hash
FROM sample
查詢沒有創建布隆過濾器索引的列:耗時300s。
SELECT count(*) FROM bloom_test
WHERE sha1 = 'b2f9544427aed7b712b209fffc756c001712b7ca'
查詢創建了布隆過濾器索引的列:耗時90s。
SELECT count(*) FROM bloom_test
WHERE sha = 'b6589fc6ab0dc82cf12099d1c2d40ab994e8410c'
查詢根本不存在于表中的數據:耗時60s(不需要掃描任何數據,僅需要查詢布隆過濾器索引)。
SELECT count(*) FROM bloom_test
WHERE sha = 'b6589fc6ab0dc82cf12099d1c2d40ab994e8410c_'
從上面的結果可以看出,在150GB的數據上,創建布隆過濾器相比未創建布隆過濾器能帶來3倍以上的性能提升,當我們查詢的數據能跳過的文件越多,這種性能提升更加顯著。
禁用布隆過濾器索引
Databricks默認啟用布隆過濾器索引,如果需要禁用布隆過濾器索引,可以通過設置配置項spark.databricks.io.skipping.bloomFilter.enabled 為false實現。