初探布隆過濾器

前言

若是想判斷一個元素是否是在一個集合裏,通常想到的是將集合中全部元素保存起來,而後經過比較肯定。鏈表、樹、散列表(又叫哈希表,Hash table)等等數據結構都是這種思路,通常來說,計算機中的集合是用哈希表(Hash table)來存儲的。html

可是隨着集合中元素的增長,咱們須要的存儲空間愈來愈大。同時檢索速度也愈來愈慢,上述三種結構的檢索時間複雜度分別爲 O(n),O(logn),O(1)。其中時間複雜度最低的hash table的空間複雜度是O(N),在面對數以億記的數據時,這種實現方式的存儲成本會比較大。面試

好比說,一個像 Yahoo,Hotmail 和 Gmai 那樣的公衆電子郵件(email)提供商,老是須要過濾來自發送垃圾郵件的人(spamer)的垃圾郵件。一個辦法就是記錄下那些發垃圾郵件的 email 地址。因爲那些發送者不停地在註冊新的地址,全世界少說也有幾十億個發垃圾郵件的地址,將他們都存起來則須要大量的網絡服務器。若是用哈希表,每存儲一億 個 email 地址, 就須要 1.6GB 的內存(用哈希表實現的具體辦法是將每個 email 地址對應成一個八字節的信息指紋(詳見:china.googleblog.com/2006/08/blo… ), 而後將這些信息指紋存入哈希表,因爲哈希表的存儲效率通常只有 50%,所以一個 email 地址須要佔用十六個字節。一億個地址大約要 1.6GB, 即十六億字節的內存)。所以存貯幾十億個郵件地址可能須要上百 GB 的內存。除非是超級計算機,通常服務器是沒法存儲的 -- Google黑板報 china.googleblog.com/2007/07/blo…算法

那麼有沒有其餘的方式來實現一樣的事呢?本文介紹一下布隆過濾器及其原理和應用。數據庫

若是面試官問你,一個網站有 100 億 url 存在一個黑名單中,每條 url 平均 64 字節。問這個黑名單要怎麼存?若此時隨便輸入一個 url,如何判斷該 url 是否在這個黑名單中?數組

正文

布隆過濾器(bloom filter)能夠用於檢索一個元素是否在一個集合中。它的優勢是空間效率和查詢時間都遠遠超過通常的算法,缺點是有必定的誤識別率和刪除元素困難緩存

通常的布隆過濾器會提供兩個方法:TestAdd服務器

Test用來確認某個元素是否在集合內。若是它返回:網絡

  1. false,那麼這個元素必定不在集合內。
  2. true,那麼這個元素只是有可能在集合裏面,部分不在集合內的元素會被誤判在集合內,布隆過濾器的假正率(False positive rate)用來描述這一律率,其隨着數據的增大而增大,同時也和所使用的hash函數有關

假正率就是實際數據爲false,預測數據(布隆過濾器的判斷)爲true,的機率。 數據結構

Add用來添加元素到集合內。併發

這裏沒有刪除,在講到原理部分時會比較清楚的講解爲何布隆過濾器刪除元素困難。

布隆過濾器原理

布隆過濾器的原理是,當一個元素被加入集合時,經過K個hash函數將這個元素映射成一個位數組中的K個點,把它們置爲1。檢索時,咱們只要看看這些點是否是都是1就(大約)知道集合中有沒有它了:若是這些點有任何一個0,則被檢元素必定不在;若是都是1,則被檢元素極可能在。這就是布隆過濾器的基本思想。

數據結構

布隆過濾器是一個 bit 向量,長這樣:

往布隆過濾器中添加一個元素時,咱們將這個值傳入 k 個hash函數,而後將結果位置bit置爲1,在圖中例子向量或者說數組的長度爲50,使用3個hash函數。

當咱們要判斷一個元素是否在布隆過濾器中時,咱們把這個值傳入k個hash函數中得到映射的k個點。這一次咱們確認下是否全部的點都被置爲1了,若是有某一位沒有置爲1則這個元素確定不在集合中。若是都在那這個元素就有可能在集合中

看完原理後咱們就知道了爲何布隆過濾器中有可能誤判以及元素越多假正率(誤判概率)越大,由於隨着增長的值愈來愈多,被置爲 1 的 bit 位也會愈來愈多,這樣某個值即便沒有被存儲過,可是有可能元素的hash函數返回的k個 bit 位都被其餘值置位了 1 ,那麼程序仍是會判斷這個值存在。

The false positive probability p as a function of number of elements n in the filter and the filter size m.

同時hash函數的個數也須要權衡,個數越多則布隆過濾器 bit 位置位 1 的速度越快,且布隆過濾器的效率越低;可是若是太少的話,那咱們的誤報率會變高。

關於如何選擇合適的hash函數個數k和布隆過濾器長度m,有人推導出了公式以下

詳情可在 Probabilistic Data structures: Bloom filter中瞭解,這裏不贅述。

刪除困難

瞭解了布隆過濾器的原理後咱們就知道在布隆過濾器中刪除元素幾乎是不可能的了,不過其實有一種叫作counting bloom filters的數據結構

使用場景

利用布隆過濾器減小磁盤IO或者網絡請求

比較場景的例子是用布隆過濾器減小磁盤IO或者網絡請求(都是代價很高的操做)查找不存在的key的時候。

布隆過濾器返回false那麼這個值確定不在

由於一旦一個值一定不存在的話,咱們能夠不用進行後續昂貴的查詢請求。若是它在,那麼咱們就去作查找,因爲誤判率不會過高因此這種代價通常也能承受。

  • Google 著名的分佈式數據庫 Bigtable 使用了布隆過濾器來查找不存在的行或列,以減小磁盤查找的IO次數
  • 在不少Key-Value系統中也使用了布隆過濾器來加快查詢過程,如 Hbase,Accumulo,Leveldb,通常而言,Value 保存在磁盤中,訪問磁盤須要花費大量時間,然而使用布隆過濾器能夠快速判斷某個Key對應的Value是否存在,所以能夠避免不少沒必要要的磁盤IO操做,
  • 好比高併發秒殺系統中搶紅包系統中判斷用戶是否今天已領取紅包
  • 在爬蟲系統中,咱們須要對URL進行去重,已經爬過的網頁就能夠不用爬了。可是URL太多了,幾千萬幾個億,若是用一個集合裝下這些URL地址那是很是浪費空間的。這時候就能夠考慮使用布隆過濾器。它能夠大幅下降去重存儲消耗,只不過也會使得爬蟲系統錯過少許的頁面。
  • 郵箱系統的垃圾郵件過濾功能也廣泛用到了布隆過濾器,由於用了這個過濾器,因此平時也會遇到某些正常的郵件被放進了垃圾郵件目錄中,這個就是誤判所致,機率很低。
  • Redis防雪崩(緩存穿透)

後記

參考資料

www.sigma.me/2011/09/13/…

hackernoon.com/probabilist…

www.jasondavies.com/bloomfilter…

相關文章
相關標籤/搜索