題目描述面試
一個網站有100億url存在一個黑名單中,每條url平均64字節。這個黑名單要怎麼存?若此時隨便輸入一個url,你如何快速判斷該 url 是否在這個黑名單中?算法
題目解析數據庫
這是一道常常在面試中出現的算法題。憑藉着題目極其容易描述,電面的時候也出現過。網頁爬蟲
不考慮細節的話,此題就是一個簡單的查找問題。對於查找問題而言,使用散列表來處理每每是一種效率比較高的方案。數組
可是,若是你在面試中回答使用散列表,接下來面試官確定會問你:而後呢?若是你不能回答個因此然,面試官就會面無表情的通知你:今天的面試到此結束,咱們會在一週內給你答覆。緩存
爲何不能用散列表服務器
100億是一個很大的數量級,這裏每條url平均64字節,所有存儲的話須要640G的內存空間。又由於使用了散列表這種數據結構,而散列表是會出現散列衝突的。爲了讓散列表維持較小的裝載因子,避免出現過多的散列衝突,須要使用鏈表法來處理,這裏就要存儲鏈表指針。所以最後的內存空間可能超過1000G了。數據結構
只是存儲個url就須要1000G的空間,老闆確定不能忍!函數
位圖(BitMap)性能
這個時候就須要拓展一下思路。首先,先來考慮一個相似但更簡單的問題:如今有一個很是龐大的數據,好比有1千萬個整數,而且整數的範圍在1到1億之間。那麼如何快速查找某個整數是否在這1千萬個整數中呢?
須要判斷該數是否存在,也就是說這個數存在兩種狀態:存在(True)或者不存在(False)。
所以這裏可使用一個存儲了狀態的數組來處理。這個數組特色是大小爲1億,而且數據類型爲布爾類型(True或者False)。而後將這1千萬個整數做爲數組下標,將對應的數組值設置成True,好比,整數233對應下標爲233的數組值設置爲True,也就是array[233]=True。
這種操做就是位圖法:就是用每一位來存放某種狀態,適用於大規模數據,但數據狀態又不是不少的狀況。
另外,位圖法有一個優點就是空間不隨集合內元素個數的增長而增長。它的存儲空間計算方式是找到全部元素裏面最大的元素(假設爲N),所以所佔空間爲:
S=N/8 byte
所以,當N爲1億的時候須要12MB的存儲空間。當N爲10億的時候須要120MB的存儲空間了。當N的數量大到必定量級的時候,好比N爲2^64這個海量級別的時候,須要消耗2048PB的存儲空間,這個量級的BitMap,目前硬件上是支持不了的。
也就是說:位圖法的所佔空間隨集合內最大元素的增大而增大。這就會帶來一個問題,若是查找的元素數量少但其中某個元素的值很大,好比數字範圍是1到1000億,那消耗的空間不容樂觀。
這個就是位圖的一個不容忽視的缺點:空間複雜度隨集合內最大元素增大而線性增大。對於開頭的題目而言,使用位圖進行處理,實際上內存消耗也是很多的。
所以,出於性能和內存佔用的考慮,在這裏使用布隆過濾器纔是最好的解決方案:布隆過濾器是對位圖的一種改進。
布隆過濾器
布隆過濾器(英語:BloomFilter)是1970年由Burton Bloom提出的。
它其實是一個很長的二進制矢量和一系列隨機映射函數。
它能夠用來判斷一個元素是否在一個集合中。它的優點是隻須要佔用很小的內存空間以及有着高效的查詢效率。
對於布隆過濾器而言,它的本質是一個位數組:位數組就是數組的每一個元素都只佔用1bit,而且每一個元素只能是0或者1。
一開始,布隆過濾器的位數組全部位都初始化爲0。好比,數組長度爲m,那麼將長度爲m個位數組的全部的位都初始化爲0。
0 0 0 0 0 0 0 0 0 0 0 0 1 。 。 。 。 。 m-2 m-1
在數組中的每一位都是二進制位。
布隆過濾器除了一個位數組,還有K個哈希函數。當一個元素加入布隆過濾器中的時候,會進行以下操做:
•使用K個哈希函數對元素值進行K次計算,獲得K個哈希值。•根據獲得的哈希值,在位數組中把對應下標的值置爲1。
舉個例子,假設布隆過濾器有3個哈希函數:f1,f2,f3和一個位數組arr。如今要把2333插入布隆過濾器中:
•對值進行三次哈希計算,獲得三個值n1,n2,n3。•把位數組中三個元素arr[n1],arr[n2],arr[3]都置爲1。
當要判斷一個值是否在布隆過濾器中,對元素進行三次哈希計算,獲得值以後判斷位數組中的每一個元素是否都爲1,若是值都爲1,那麼說明這個值在布隆過濾器中,若是存在一個值不爲1,說明該元素不在布隆過濾器中。
很明顯,數組的容量即便再大,也是有限的。那麼隨着元素的增長,插入的元素就會越多,位數組中被置爲1的位置所以也越多,這就會形成一種狀況:當一個不在布隆過濾器中的元素,通過一樣規則的哈希計算以後,獲得的值在位數組中查詢,有可能這些位置由於以前其它元素的操做先被置爲 1 了。
如圖1所示,假設某個元素經過映射對應下標爲4,5,6這3個點。雖然這3個點都爲1,可是很明顯這3個點是不一樣元素通過哈希獲得的位置,所以這種狀況說明這個元素雖然不在集合中,也可能對應的都是1,這是誤判率存在的緣由。
因此,有可能一個不存在布隆過濾器中的會被誤判成在布隆過濾器中。
這就是布隆過濾器的一個缺陷:存在誤判。
可是,若是布隆過濾器判斷某個元素不在布隆過濾器中,那麼這個值就必定不在布隆過濾器中。總結就是:
•布隆過濾器說某個元素在,可能會被誤判•布隆過濾器說某個元素不在,那麼必定不在
用英文說就是:Falseisalwaysfalse.Trueismaybetrue。
誤判率
布隆過濾器能夠插入元素,但不能夠刪除已有元素。其中的元素越多,falsepositiverate(誤報率)越大,可是falsenegative(漏報)是不可能的。
因爲公衆號內對於數學公式的排版不太友好,小吳就不在這貼出來了,具體的計算公式能夠在網上查找到。
補救方法
布隆過濾器存在必定的誤識別率。常見的補救辦法是在創建白名單,存儲那些可能被誤判的元素。好比你苦等的offer可能被系統丟在郵件垃圾箱(白名單)了。
使用場景
布隆過濾器的最大的用處就是,可以迅速判斷一個元素是否在一個集合中。所以它有以下三個使用場景:
•網頁爬蟲對 URL 的去重,避免爬取相同的 URL 地址•進行垃圾郵件過濾:反垃圾郵件,從數十億個垃圾郵件列表中判斷某郵箱是否垃圾郵箱(同理,垃圾短信)•有的黑客爲了讓服務宕機,他們會構建大量不存在於緩存中的key向服務器發起請求,在數據量足夠大的狀況下,頻繁的數據庫查詢可能致使DB掛掉。布隆過濾器很好的解決了緩存擊穿的問題。
回到問題
回到一開始的問題,若是面試官問你如何在海量數據中快速判斷該url是否在黑名單中時,你應該回答使用布隆過濾器進行處理,而後說明一下爲何不使用hash和bitmap,以及布隆過濾器的基本原理,最後你再談談它的使用場景那就更好了。