布隆過濾器:高效、大概的判斷數據是否存在

1      什麼是布隆過濾器

本質上布隆過濾器是一種數據結構,比較巧妙的機率型數據結構(probabilistic data structure),特色是高效地插入和查詢,能夠用來告訴你 「某樣東西必定不存在或者可能存在」,或者說「判斷一個元素是否存在一個集合中」,好比:html

  • 字處理軟件中,須要檢查一個英語單詞是否拼寫正確
  • 在 FBI,一個嫌疑人的名字是否已經在嫌疑名單上
  • 在網絡爬蟲裏,一個網址是否被訪問過
  • yahoo, gmail等郵箱垃圾郵件過濾功能

 

 

相比於傳統的 List、Set、Map 等數據結構,它更高效、佔用空間更少,可是缺點是其返回的結果是機率性的,而不是確切的。算法

2      使用場景

網頁爬蟲對URL的去重,避免爬取相同的URL地址;網頁爬蟲

反垃圾郵件,從數十億個垃圾郵件列表中判斷某郵箱是否垃圾郵箱(同理,垃圾短信);數組

緩存擊穿,將已存在的緩存放到布隆中,當黑客訪問不存在的緩存時迅速返回避免緩存及DB掛掉。緩存

 

 

3      實現原理

3.1      插入數據

布隆過濾器是一個 bit 向量或者說 bit 數組,長這樣:服務器

 

 

若是咱們要映射一個值到布隆過濾器中,咱們須要使用多個不一樣的哈希函數生成多個哈希值,並對每一個生成的哈希值指向的 bit 位置 1,例如針對值 「baidu」 和三個不一樣的哈希函數分別生成了哈希值 一、四、7,則上圖轉變爲:網絡

 

 

Ok,咱們如今再存一個值 「tencent」,若是哈希函數返回 三、四、8 的話,圖繼續變爲:數據結構

 

 

值得注意的是,4 這個 bit 位因爲兩個值的哈希函數都返回了這個 bit 位,所以它被覆蓋了。函數

3.2      判斷數據是否存在

如今咱們若是想查詢 「dianping」 這個值是否存在,哈希函數返回了 一、五、8三個值,結果咱們發現 5 這個 bit 位上的值爲 0,說明沒有任何一個值映射到這個 bit 位上,所以咱們能夠很肯定地說 「dianping」 這個值不存在。而當咱們須要查詢 「baidu」 這個值是否存在的話,那麼哈希函數必然會返回 一、四、7,而後咱們檢查發現這三個 bit 位上的值均爲 1,那麼咱們能夠說 「baidu」 存在了麼?答案是不能夠,只能是 「baidu」 這個值可能存在。性能

這是爲何呢?答案跟簡單,由於隨着增長的值愈來愈多,被置爲 1 的 bit 位也會愈來愈多,這樣某個值 「taobao」 即便沒有被存儲過,可是萬一哈希函數返回的三個 bit 位都被其餘值置位了 1 ,那麼程序仍是會判斷 「taobao」 這個值存在。

 

4      優勢

4.1      佔內存少

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

 

還好比說你的數據集存儲在遠程服務器上,本地服務接受輸入,而數據集很是大不可能一次性讀進內存構建 HashMap 的時候,也會存在問題。

 

布隆過濾器就不用爲每一個數都分配空間了,而是直接把全部的數經過算法映射到同一個數組,帶來的問題就是衝突上升,只要機率在能夠接受的範圍,用時間換空間,在不少時候是好方案。布隆過濾器須要的空間僅爲HashMap的1/8-1/4之間,並且它不會漏掉任何一個在黑名單的可疑對象,問題只是會誤傷一些非黑名單對象。

 

4.2      插入查詢O(1)

見查詢原理

5      缺點

5.1      錯誤率問題

見查詢原理

5.2      刪除問題

目前咱們知道布隆過濾器能夠支持 add 和 isExist 操做,那麼 delete 操做能夠麼,答案是不能夠,例如上圖中的 bit 位 4 被兩個值共同覆蓋的話,一旦你刪除其中一個值例如 「tencent」 而將其置位 0,那麼下次判斷另外一個值例如 「baidu」 是否存在的話,會直接返回 false,而實際上你並無刪除它。

 

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

 

6      錯誤率估計及如何肯定數組長度

顯然,數組越長,錯誤率越低,但佔用空間越大,如何作權衡,見下文:

http://www.javashuo.com/article/p-cbngsayn-kv.html

 

7      最佳實踐

7.1      高效的Hash函數

既然你使用布隆過濾器來加速查找和判斷是否存在,那麼性能很低的哈希函數不是個好選擇,推薦 MurmurHash、Fnv 這些。

7.2      大Value拆分

Redis 因其支持 setbit 和 getbit 操做,且純內存性能高等特色,所以自然就能夠做爲布隆過濾器來使用。可是布隆過濾器的不當使用極易產生大 Value,增長 Redis 阻塞風險,所以生成環境中建議對體積龐大的布隆過濾器進行拆分。

 

拆分的形式方法多種多樣,可是本質是不要將 Hash(Key) 以後的請求分散在多個節點的多個小 bitmap 上,而是應該拆分紅多個小 bitmap 以後,對一個 Key 的全部哈希函數都落在這一個小 bitmap 上。

8      參考

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

https://zhuanlan.zhihu.com/p/43263751

布隆過濾器(Bloom Filter)的原理和實現

http://www.javashuo.com/article/p-aenfkllo-mo.html

使用BloomFilter布隆過濾器解決緩存擊穿、垃圾郵件識別、集合判重

http://www.javashuo.com/article/p-cbngsayn-kv.html

相關文章
相關標籤/搜索