1. 程式人生 > >【淺析】|白話布隆過濾器BloomFilter

【淺析】|白話布隆過濾器BloomFilter

通過本文將瞭解到以下內容:

  • 查詢問題的一般思路
  • 布隆過濾器的基本原理
  • 布隆過濾器的典型應用
  • 布隆過濾器的工程實現

場景說明:

本文闡述的場景均為普通單機伺服器、並非分散式大資料平臺,因為在大資料平臺下問題就是另外一種考慮方式了,因此本文只描述貧窮落後一窮二白的場景,儼然有種60年代先輩們在戈壁攻克原子彈的感覺。

1.查詢問題的一般思路

查詢問題是出現頻率極高的問題,來看一道面試題:

給你A,B兩個檔案,各存放50億條URL,每條URL佔用64位元組,記憶體限制是4G,讓你找出A,B檔案所有共同的URL。

一般思路:面對一般的問題,根據不同的資料規模,轉換為計算機問題之後就落地到實際的資料結構:

  • 線性結構:陣列、連結串列、
  • 容器結構:集合、Map、HashTable
  • 樹形結構:AVL、RBTree、BTree

但是上述的結構都是將待查詢資料直接儲存,如果是大資料量,這樣雖然保證了準確性但是空間消耗會非常大,實際是不可行的。

苛求條件下的思路:

  • 把待查資料進行資訊無缺失地壓縮

這句話的意思就是:對於公民可以使用身份證來獨立唯一表示此個體,而無需太多諸如性別、出生日期、出生地、履歷等描述,這種轉換就相當於在資訊無缺失的情況下,使用更少的特徵來表示。還有一個例子就是:文言文往往篇幅很短,翻譯為白話文可能很長,所以文言文就可以認為是白話文的資訊無缺失壓縮。

這種做法可以實現使用更少的資訊量表達更多的含義,對儲存很有利。

  • 藉助於儲存容器來儲存輔助資訊

這句話是說:比如高考之後的每個省份的一分一檔表,每個分數段代表一個儲存空間,假如要知道0-750分中每個分數段有多少人,那麼只需要計數即可,因為在對應分數段的考生就是對應的分數,換句話說600分段的儲存的值都是600分,無需單獨記錄絕對值,借用下標即可,也就是需要設計儲存容器來自動帶有相關資訊。

2.布隆過濾器的基本原理

前面那道問題就可以使用布隆過濾器解決。

  • 布隆過濾器的定義
布隆過濾器Bloom Filter是1970年由布隆提出的。它實際上是一個很長的二進位制向量和一系列隨機對映函式。布隆過濾器可以用於檢索一個元素是否在一個集合中。它的優點是空間效率和查詢時間都遠遠超過一般的演算法,缺點是有一定的誤識別率和刪除困難。
  • 布隆過濾器橫向對比
如果想判斷一個元素是不是在一個集合裡,一般是將集合中所有元素儲存起來,然後通過比較確定。連結串列、樹、雜湊表等資料結構都是這種思路。但是集合元素的增加需要的儲存空間越大,檢索速度也越慢。維基百科
  • 布隆過濾器的基本原理

布隆過濾器本質上是bitmap位陣列的擴充套件,位陣列的應用也非常多,比如在IO複用工具Select中就使用三個FD_SET來儲存Socket檔案描述符的值,簡單說一下位陣列的原理:假如現在有0-31範圍的20個不重複數字,如何判斷16是否在其中呢?

一種做法是建立個數組就算是int8 單位元組的陣列,也需要8*32=256位空間,之後去遍歷這個陣列檢視是否有16.

另外一種做法使用一個int32的變數,這個變數佔4個位元組32bit,可以用bit0表示數字0,bit1表示數字1,.....bit30表示30,因此我們實現了使用4位元組空間完成了之前32位元組的工作,空間使用是之前的1/8,非常可觀。

一圖勝千言 靈魂畫手上線了:

如圖所示在bit陣列中如果對於bit是1表示存在對應的值,為0則不存在。

  • 布隆過濾器的兩大元件
  1. 一定大小的BitAarry位陣列(具體大小和儲存規模有關)
  2. N個優秀的雜湊函式(N的個數和儲存規模和容忍誤判率有關)

兩個元件的功能也非常明確,位陣列就是儲存對應位的值是0/1的二進位制向量,雜湊函式的作用是將原始輸入經過數學運算轉換為一個數字值,雜湊函式是加密和安全的基礎,也是數學的偉大體現,感興趣可以進一步學習其基本原理。

對應最開始提到的資訊無失真壓縮其實就是雜湊函式的作用,就像我們每個人可以用一個學號、工號、身份證號來代替一樣,雜湊函式也實現了原始輸入的數字化。

  • 布隆過濾器和雜湊衝突

雜湊衝突雖然概率很低,但是在大規模資料場景下還是會出現的,而且衝突率和雜湊函式本身有很大關係,因此在設計布隆過濾器時要選擇效能優良的雜湊函式來降低雜湊衝突。舉個栗子:

對於雜湊函式fuc(唱歌都很棒),有四個輸入分別是大白、周杰倫、張學友、巖崎良美,雜湊函式計算結果分別是:Yes/Yes/Yes/Yes。

可以看到如果以yes/no作為結果的話,我和周杰倫他們仨產生了雜湊衝突,因此這個雜湊函式並不優秀。水平有限關於雜湊函式和碰撞的數學原理就不再展開了。多個雜湊:即使很優秀的雜湊函式仍然存在衝突,那麼如何降低衝突率呢?很明顯多用幾個雜湊函式!假如Hash1衝突率萬分之一,Hash2萬分之一,同時Hash1和Hash2衝突的概率就是億分之一了,但是實際中為了將資料都儲存在bitarray中,由於取模運算的存在,衝突率會比理論值高。

這種思想也並不新鮮,遠在春秋戰國時期,就有先例:

(圖片來自網路)

上圖是兵符,當兩個兵符合在一起才可以排程千軍萬馬,跟單個雜湊衝突率高就使用多個是一個道理。

  • 布隆過濾器的具體使用

假如現在有三個雜湊函式分別為h1,h2,h3,同時有三個輸入x,y,z。三個輸入分別通過h1-h3進行雜湊計算出對應整數之後,對bitarray的長度進行取模運算,獲取對應下標再進行置1,這樣運算三次就形成了如圖的bitmap結構:

圖片來自網路

布隆過濾器檢索時,使用相同的雜湊函式進行計算出對應的bit位置,只要看這些位置的值,如果這些位置有任何一個0,則被檢元素一定不在;如果都是1,則被檢元素可能存在。一句話概率就是全0一定不存在、全1不一定存在。

  • 布隆過濾器和誤判率

布隆過濾器的誤判是指多個輸入經過雜湊之後在相同的bit位置1了,這樣就無法判斷究竟是哪個輸入產生的,因此誤判的根源在於相同的bit位被多次對映且置1。

這種情況也造成了布隆過濾器的刪除問題,因為衝突的存在無法確定有多少輸入對映到這個bit位了,當然這是個優化方向。

布隆過濾器存在一定的誤判,主要因素包括:

  1. 雜湊函式本身的衝突率
  2. bitarray位陣列的大小

這個其實很明顯,如果位陣列很小,雜湊函式再優秀也會產生誤判,因此在工程設計中需要首先設定誤判率和資料規模再確定雜湊函式個數和位陣列大小。

上述過程再用一張圖來表示:

圖片來自網路

  • 布隆過濾器的優缺點
相比於其它的資料結構,布隆過濾器在空間和時間方面都有巨大的優勢。布隆過濾器儲存空間和插入/查詢時間都是常數。另外,雜湊函式相互之間沒有關係,方便由硬體並行實現。布隆過濾器不需要儲存元素本身,在某些對保密要求非常嚴格的場合有優勢。布隆過濾器可以表示全集,其它任何資料結構都不能;維基百科--布隆的優點
誤算率是其中之一,隨著存入的元素數量增加,誤算率隨之增加,但是如果元素數量太少,則使用散列表足夠。另外一般情況下不能從布隆過濾器中刪除元素。

3.布隆過濾器的典型應用

布隆在海量資料查詢中以優異的空間效率和低誤判率有非常廣泛的應用,其中包括但不限於:

  • 檢查單詞拼寫正確性
  • 檢測海量名單嫌疑人
  • 垃圾郵件過濾
  • 搜尋爬蟲URL去重
  • 快取穿透過濾

4.布隆過濾器的工程實現

  • 位陣列和雜湊函式個數的計算

理論要遷移到實際工程還是需要做一些調研的,從上面的理論部分可以知道,要設計一個誤判率低的布隆過濾器最關鍵的是要確定兩個因素:

  1. bitarray位陣列的大小m
  2. 雜湊函式的個數k

感興趣的可以查詢相關推導過程,這裡直接給出網上較為認可的工程值:

 

 

其中fpp是誤判率比如0.0001,n是預估要儲存的資料元素個數。

  • 多個雜湊函式的優化

使用多個雜湊函式確實可以降低衝突,但是多個雜湊函式也會造成計算時間的增加,因此雜湊函式的個數是個折中值,但是在2008年哈佛的一篇論文指出可以使用2個雜湊函式來模擬多個雜湊函式:

這篇論文涉及大量的數學推導,我很自覺地知難而退了,大神可以看看,作為一個結論可以在工程實踐中用一下。

  • 工程元件

Google的Guava類庫提供了簡潔的介面,只要設定誤判率和資料規模即可完成,Redis新版本中也有bitmap型別,也可以實現布隆,當然不借助於這些元件,也可以自己來實現,在此就不展開了,後續有機會可以聊一