Bloom過濾器

提出一個問題

在我們細述Bloom過濾器之前,我們先拋出一個問題:給你一個巨大的數據集(百萬級、億級…),怎麼判斷一個元素是否在此數據集中?或者怎麼判斷一個元素不在此數據集中?

思考這個問題的時候,最先想到的可能是哈希表,在數據集規模較小的時候,這個方法是可行的,當然,數據集巨大的時候也可以採用分佈式哈希表的方式。當數據集規模較大時,尤其是應用中只需要判斷一個元素不在此數據集中的情況時,我們可以借鑑哈希表的思路,使用Bloom過濾器解決這個問題。既然我們只關心元素在不在,不關心元素值是什麼,只要把元素映射爲一個布爾值表示在不在就足夠了。下面細述Bloom過濾器數據結構的設計。

Bloom過濾器數據結構

Bloom filter(布魯姆過濾器)是用於測試元素成員資格的空間高效概率數據結構。數據結構以犧牲規定的假陽性率爲代價實現了巨大的數據壓縮。一個Bloom過濾器作爲一個m位的數組全部設置爲0。選擇一組k個隨機散列函數,在Bloom過濾器中添加元素時,元素將分別進行哈希散列,而對於每個k個輸出,該索引處的相應的Bloom過濾器位將被設置爲1。通過使用與之前相同的散列函數來完成Bloom過濾器的查詢。如果在bloom過濾器中訪問的所有k個比特被設置爲1,則這很可能表明該元素位於該集合中。刪除元素只能通過廢除Bloom過濾器並從頭重新創建來完成,這個問題後面會討論到。

Bloom過濾器是由底層數組和哈希函數組合在一起工作的,根據對誤報率要求的不同,可以選擇一個哈希函數,也可以選2個、3個,一般情況下選3個。與哈希表不同,爲節省空間,Bloom過濾器的底層數組的每一位是一個比特,1表示有映射,0表示無映射。數組的長度與問題規模、哈希函數、誤報率等因素有關,根據數據集規模的不同,可選用適當的哈希函數與適合的數組大小。因爲具體問題的不同,很難說那種實現是最好的。下面舉例說明Bloom過濾器的工作過程。

image

數據集{x,y,z},底層數組長度m=18,初始值全部爲0,哈希函數個數k=3,分別爲H1H_1H2H_2H3H_3

首先把數據集的每個元素分別通過3個不同的哈希函數映射到底層數組中,將數組中對應位置的值置爲1。可以看到有哈希碰撞發生,這裏不用解決哈希碰撞。

當要判斷一個元素是否在數據集中時,例如w,則依次計算3個哈希值,找數組中的映射值,如果映射值有一個爲0則,元素不存在數據集中,如果3個對應映射值全部爲1,則元素很大概率在數據集中,不能完全確定(因爲哈希碰撞的存在)。圖中,元素w的其中一個哈希映射爲0,所以w一定不在數據集中。

可以看到,Bloom過濾器的工作過程是非常簡單的。

Bloom過濾器的相關問題

爲什麼Bloom過濾器不能100%確定元素在數據集中呢?

很明顯,Bloom過濾器是可以100%確定一個元素不在數據集中的,但是判斷一個元素在數據集中只能說很大概率在。

因爲哈希碰撞的原因,底層數組對應映射值爲1,有可能是其他元素與要查找的元素髮生碰撞,實際上,該元素並不存在在數據集中。所以Bloom過濾器存在誤報率。

Bloom過濾器能否有刪除元素的操作

可以看到,Bloom過濾器,只有插入、查找操作,沒有刪除操作,爲什麼呢?

因爲哈希碰撞的原因,有可能2個元素髮生哈希碰撞,此時刪掉其中一個元素,對應底層數組的值置爲0,則等於把另一個元素也刪掉了,所以是沒有刪除操作的。那單從數據集中刪掉一個元素,對應的底層數組的值不變,這樣可以嗎?邏輯上,是可以的,並不會引發錯誤,但實際上,這樣做會大幅增加誤報率,如果不斷的刪除插入,最後會發現,整個底層數組都快被填滿了,失去了Bloom過濾器快速判斷元素是否在數據集中的意義了。當然,如果刪除操作很少的話,這樣解決也是可以的,但是要在誤報率允許範圍內,定期重建Bloom過濾器(當數據集非常大時,不斷重建的過程代價是很大的)。

當然,現實需求中,很可能是有刪除元素的操作需求的,哪怎麼辦呢?可能的一個思路是在Bloom過濾器的基礎上,做改進,類似引用計數的思路,底層數組中的值不再是布爾值,而是一個整型,每當發生一次碰撞,對應值遞增一次,當刪除一個元素時,遞減一次。當然僅僅做到這樣,還是不夠的,可能還會有其他的問題,具體怎麼實現,還有待各位大神去解決,這裏不再做更深的思考。

還有大神提出了Cuckoo Filter,參考論文Cuckoo Filter: Practically Better Than Bloom

誤報率的計算

誤報率與多個因素相關,數組的長度,元素的個數,哈希函數本身,哈希函數的個數等等。對於誤報率如何計算,可參考Bloom filter,這裏不再細述。

參考文檔:布隆過濾器

除了理論上的誤報率計算,程序也可以實際感受到誤報率的變化,需要的話可以在程序中對每一次誤報做統計,誤報的次數/總的次數

Bloom過濾器的應用

經過上面的學習,我們可以回答文章最開始的問題了,只需要將原始數據集全部映射到Bloom過濾器底層數組中,所需要的空間開銷要比哈希表等其他方式小的多,因爲它不存儲原始數據集數據,只存儲象徵數據是否存在的一個布爾值。判斷一個元素時,計算哈希,查找對應映射值即可判斷。

下圖說明的是在一個KV存儲系統中,使用Bloom提高查詢響應速度。

image

可以看到,針對key1這種查詢請求,就無需再訪問KV存儲系統了,可返回訪問結果:數據不存在;針對key2這種請求,則繼續訪問KV存儲系統返回結果;針對key3這種是屬於誤報,很少發生。如果實際的應用需求中,有大量的系統並未實際存儲的數據查詢請求,這種方式能夠顯著降低對KV存儲系統的訪問,提高響應效率。

歡迎關注微信公衆號,推送數據結構、分佈式、區塊鏈、後端開發等更多內容!

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