若是想判斷一個元素是否是在一個集合裏,通常想到的是將集合中全部元素保存起來,而後經過比較肯定。鏈表、樹、散列表(又叫哈希表,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)能夠用於檢索一個元素是否在一個集合中。它的優勢是空間效率和查詢時間都遠遠超過通常的算法,缺點是有必定的誤識別率和刪除元素困難。緩存
通常的布隆過濾器會提供兩個方法:Test 和 Add服務器
Test用來確認某個元素是否在集合內。若是它返回:網絡
假正率就是實際數據爲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 ,那麼程序仍是會判斷這個值存在。
同時hash函數的個數也須要權衡,個數越多則布隆過濾器 bit 位置位 1 的速度越快,且布隆過濾器的效率越低;可是若是太少的話,那咱們的誤報率會變高。
關於如何選擇合適的hash函數個數k和布隆過濾器長度m,有人推導出了公式以下
詳情可在 Probabilistic Data structures: Bloom filter中瞭解,這裏不贅述。瞭解了布隆過濾器的原理後咱們就知道在布隆過濾器中刪除元素幾乎是不可能的了,不過其實有一種叫作counting bloom filters的數據結構
比較場景的例子是用布隆過濾器減小磁盤IO或者網絡請求(都是代價很高的操做)查找不存在的key的時候。
布隆過濾器返回false那麼這個值確定不在
由於一旦一個值一定不存在的話,咱們能夠不用進行後續昂貴的查詢請求。若是它在,那麼咱們就去作查找,因爲誤判率不會過高因此這種代價通常也能承受。