詳解布隆過濾器的原理 使用場景和注意事項

2021-09-25 07:51:52 字數 2629 閱讀 1760

今天碰到個業務,他的 redis 集群有個大 value 用途是作為布隆過濾器,但溝通的時候被小懟了一下,意思大概是「布隆過濾器原理都不懂,還要我優化?」。技術菜被人懟認了、怪不得別人,自己之前確實只是聽說過這個,但是沒深入了解過,趁這個機會補充一下知識。

在進入正文之前,之前看到的有句話我覺得說得很好:

大意是不同的資料結構有不同的適用場景和優缺點,你需要仔細權衡自己的需求之後妥善適用它們,布隆過濾器就是踐行這句話的代表。

本質上布隆過濾器是一種資料結構,比較巧妙的概率型資料結構(probabilistic data structure),特點是高效地插入和查詢,可以用來告訴你 「某樣東西一定不存在或者可能存在」。

相比於傳統的 list、set、map 等資料結構,它更高效、占用空間更少,但是缺點是其返回的結果是概率性的,而不是確切的。

講述布隆過濾器的原理之前,我們先思考一下,通常你判斷某個元素是否存在用的是什麼?應該蠻多人回答 hashmap 吧,確實可以將值對映到 hashmap 的 key,然後可以在 o(1) 的時間複雜度內返回結果,效率奇高。但是 hashmap 的實現也有缺點,例如儲存容量佔比高,考慮到負載因子的存在,通常空間是不能被用滿的,而一旦你的值很多例如上億的時候,那 hashmap 佔據的記憶體大小就變得很可觀了。

還比如說你的資料集儲存在遠端伺服器上,本地服務接受輸入,而資料集非常大不可能一次性讀進記憶體構建 hashmap 的時候,也會存在問題。

布隆過濾器是乙個 bit 向量或者說 bit 陣列,長這樣:

如果我們要對映乙個值到布隆過濾器中,我們需要使用多個不同的雜湊函式生成多個雜湊值,並對每個生成的雜湊值指向的 bit 位置 1,例如針對值 「baidu」 和三個不同的雜湊函式分別生成了雜湊值 1、4、7,則上圖轉變為:

ok,我們現在再存乙個值 「tencent」,如果雜湊函式返回 3、4、8 的話,圖繼續變為:

值得注意的是,4 這個 bit 位由於兩個值的雜湊函式都返回了這個 bit 位,因此它被覆蓋了。現在我們如果想查詢 「dianping」 這個值是否存在,雜湊函式返回了 1、5、8三個值,結果我們發現 5 這個 bit 位上的值為 0,說明沒有任何乙個值對映到這個 bit 位上,因此我們可以很確定地說 「dianping」 這個值不存在。而當我們需要查詢 「baidu」 這個值是否存在的話,那麼雜湊函式必然會返回 1、4、7,然後我們檢查發現這三個 bit 位上的值均為 1,那麼我們可以說 「baidu」 存在了麼?答案是不可以,只能是 「baidu」 這個值可能存在。

這是為什麼呢?答案跟簡單,因為隨著增加的值越來越多,被置為 1 的 bit 位也會越來越多,這樣某個值 「taobao」 即使沒有被儲存過,但是萬一雜湊函式返回的三個 bit 位都被其他值置位了 1 ,那麼程式還是會判斷 「taobao」 這個值存在。

目前我們知道布隆過濾器可以支援 add 和 i***ist 操作,那麼 delete 操作可以麼,答案是不可以,例如上圖中的 bit 位 4 被兩個值共同覆蓋的話,一旦你刪除其中乙個值例如 「tencent」 而將其置位 0,那麼下次判斷另乙個值例如 「baidu」 是否存在的話,會直接返回 false,而實際上你並沒有刪除它。

如何解決這個問題,答案是計數刪除。但是計數刪除需要儲存乙個數值,而不是原先的 bit 位,會增大占用的記憶體大小。這樣的話,增加乙個值就是將對應索引槽上儲存的值加一,刪除則是減一,判斷是否存在則是看值是否大於0。

很顯然,過小的布隆過濾器很快所有的 bit 位均為 1,那麼查詢任何值都會返回「可能存在」,起不到過濾的目的了。布隆過濾器的長度會直接影響誤報率,布隆過濾器越長其誤報率越小。

另外,雜湊函式的個數也需要權衡,個數越多則布隆過濾器 bit 位置位 1 的速度越快,且布隆過濾器的效率越低;但是如果太少的話,那我們的誤報率會變高。

k 為雜湊函式個數,m 為布隆過濾器長度,n 為插入的元素個數,p 為誤報率。

至於如何推導這個公式,我在知乎發布的文章有涉及,感興趣可以看看,不感興趣的話記住上面這個公式就行了。

常見的適用常見有,利用布隆過濾器減少磁碟 io 或者網路請求,因為一旦乙個值必定不存在的話,我們可以不用進行後續昂貴的查詢請求。

另外,既然你使用布隆過濾器來加速查詢和判斷是否存在,那麼效能很低的雜湊函式不是個好選擇,推薦 murmurhash、fnv 這些。

redis 因其支援 setbit 和 getbit 操作,且純記憶體效能高等特點,因此天然就可以作為布隆過濾器來使用。但是布隆過濾器的不當使用極易產生大 value,增加 redis 阻塞風險,因此生成環境中建議對體積龐大的布隆過濾器進行拆分。

拆分的形式方法多種多樣,但是本質是不要將 hash(key) 之後的請求分散在多個節點的多個小 bitmap 上,而是應該拆分成多個小 bitmap 之後,對乙個 key 的所有雜湊函式都落在這乙個小 bitmap 上。

詳解布隆過濾器的原理 使用場景和注意事項

原文 在進入正文之前,之前看到的有句話我覺得說得很好 大意是不同的資料結構有不同的適用場景和優缺點,你需要仔細權衡自己的需求之後妥善適用它們,布隆過濾器就是踐行這句話的代表。本質上布隆過濾器是一種資料結構,比較巧妙的概率型資料結構 probabilistic data structure 特點是高效...

布隆過濾器的原理 使用場景和注意事項

今天碰到個業務,他的 redis 集群有個大 value 用途是作為布隆過濾器,但溝通的時候被小懟了一下,意思大概是 布隆過濾器原理都不懂,還要我優化?技術菜被人懟認了 怪不得別人,自己之前確實只是聽說過這個,但是沒深入了解過,趁這個機會補充一下知識。在進入正文之前,之前看到的有句話我覺得說得很好 ...

布隆過濾器的原理,使用場景和注意事項

本質上布隆過濾器是一種資料結構,比較巧妙的概率型資料結構 probabilistic data structure 特點是高效地插入和查詢,可以用來告訴你 某樣東西一定不存在或者可能存在 相比於傳統的 list set map 等資料結構,它更高效 占用空間更少,但是缺點是其返回的結果是概率性的,而...