布隆過濾器(BloomFilter)

目錄

 

布隆過濾器的用途

布隆過濾器是什麼

比特數組的大小及如何創建

Hash 函數如何設置

布隆過濾器的實際誤差計算


布隆過濾器的用途

讓我們先來看一個業務場景.

假設某搜索公司的URL黑名單有100億條記錄.業務上需要時刻監控如果用戶訪問的URL是黑名單中的就駁回訪問請求.如何實現這樣的黑名單監控功能呢?

最容易的辦法當然是將數據存入hash表中,用戶訪問網頁時,查看黑名單hash表中是否包含其正訪問的網址以做出正確的迴應.僞代碼可以簡單的寫成

if(userUrl == blackList.get(userUrl)){
    reject();
}

雖然hash表加快了URL是否合法的判斷時間,但100億條的URL都存入hash表中,即便每條URL只佔用64字節,總共下來最少也要640G內存.一般的服務器是沒有這麼大內存的.而用一個多臺機器的集羣來完成這樣一件事又得不償失.

布隆過濾器解決的就是這種場景的問題.它可以讓我們只用有限的內存就可以解決原來需要640G內存才能完成的事情,而爲此只需要付出微小錯誤率的代價.布隆過濾器的錯誤是指將不在黑名單中的URL誤判爲非法URL而不是將非法的URL判爲合法,這在工程上往往是可以接受的.

 

布隆過濾器是什麼

擁有如此強大功能的布隆過濾器,其本質其實是hash函數和字節類型的數組.字節數組中的值只有0和1兩種,我們剛好以此來標記URL是否在黑名單中.

假設我們有一個足夠長的字節類型的數組來標記100億個黑名單URL,只需要將黑名單中的URL逐一傳入hash函數中,hash函數返回一個int類型的數作爲該URL在數組中的index,將該index上的值置爲1表示該URL是黑名單中的一個.當用戶訪問一個URL時,只需要將訪問的URL傳入hash函數,判斷其對應index值是否爲1即可.

比特數組的大小及如何創建

基本思路是清楚了,那我們來看一下細節問題.首先,一個足夠長的數組是多長呢?我們知道數組的長度就是URL通過hash函數之後的映射域,數組太小會導致hash碰撞的概率增加,合法URL同黑名單URL碰撞就造成了誤判,所以數組的長度定義應該根據提前想好的允許誤差來進行規劃.下面是數組長度的計算公式:

m = -\frac{n * ln p}{(ln 2)^{2}}

公式中,m代表最終數組的大小,n代表樣本數,p代表允許誤差.計算得到的m如果是小數則向上取整即可.如果100億URL的允許誤差率爲0.0001,那麼經過計算,最終需要的空間大概是22G多.從最少要640G到只要22G+,這個空間節省是特別可觀的.而且現在20多G的內存對於服務器來講,已經不是高成本的代價了.

如何創建一個長度爲m的字節類型數組呢,爲了方便計算,假設最終得到的數組大小爲100w.我們知道在Java中,一個int類型的數據會佔用四個字節,而每個字節又有8個比特,所以一個int數字就是32比特,我們可以創建一個100w/32 = 3125長度的int數組來完成布隆過濾器數據的存儲.假設hash函數返回值爲903,因爲一個int數組代表了32位,我們首先要計算903對應了int數組的第幾個index,也就是

903 / 32 -1 = 27

找到了index後需要計算該int值的第幾位使我們要標記的.也就是

903 % 27 = 12

創建和使用方法如下:

int [] bloomFileData = new int [31250];
//找到對應index
int index = 903 / 27;
//找到對應位數
int location = 903 % 27;

//標記爲1
bloomFilterData[index] = bloomFilterData[index] | (1 << location);

Hash 函數如何設置

設置好m之後,爲了能減小hash碰撞對準確率產生的影響,人們又想出了一個辦法就是,既然只要是使用了hash函數進行映射就一定存在hash碰撞的情況,那我使用多個hash函數,當一個值過來我多做幾次判斷,每次都命中才算對應上,這樣發生hash碰撞的概率總要小的多了吧.

辦法是好辦法,可以hash函數的個數也不是越多越好的,極端一點,如果設置100億個hash函數,那數組中幾乎所有的位置都被標記爲1了,誤判率範圍是100%.所以使用多少hash函數也有對應公式計算:

k = \frac{m}{n}ln 2

公式中,m爲數組大小,n爲樣本量.計算出的k如果是小數也向上取整.此處需要說明,k個hash函數必須是獨立的,每兩個hash函數之間不可以存在相關性.

 

布隆過濾器的實際誤差計算

到此,我們清楚了在明確樣本量和期望誤判率的情況下,如何來根據這兩個已知條件計算數組空間和hash函數個數,但是在根據上邊公式計算這兩者時,因爲數組空間和函數個數不可能有小數,我們都進行了向上取整.此時的誤判率不會嚴格符合我們的期望誤判率,如何計算實際誤判率呢?公式如下:

(1 - e^{-\frac{kn}{m}})^{k}

k,n,m還是分別代表hash函數的個數,樣本量以及數組空間大小.

 

到此,布隆過濾器就講完啦.感謝耐心閱讀.

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章