布隆過濾器(BloomFilter)原理 | 億級數據過濾解決方案

讀前福利:幾百本互聯網技術書籍送給你們mp.weixin.qq.com/s/dFqVQ2qJx…html

1970 年,布隆先生提出了一種很優秀的過濾器算法,用來判斷一個元素是否在集合中算法

「布隆過濾器算法」數據庫

故事開始→_→數組

先看本故事結構緩存

就當前互聯網環境來講,頭部的互聯網生態愈來愈往高併發、分佈式的形態發展。舉例來講,各大網頁的黑名單系統,爬蟲的重複率判斷。這些場景愈來愈多。服務器

舉例來講,實時狀態下可能會對超過百億級別的 URL 須要進行判斷是否符合規範或者存在於系統中,可否正常使用。markdown

一般狀況下,每一個 URL 的大小爲 64B(字節),那麼就按照100億的 URL 數量來看,大概須要640GB的內存容量【 64 × 100 / 102 4 3 64 \times 100\text{億}/1024^3 】,對於當前線上服務器來講,... 這個值依然仍是很大的!但若是利用布隆過濾器的優點,在沒有失誤率的狀況下只須要100億個比特,即:1.2GB,即便爲了下降失誤率,也不會超過幾十GB的空間【失誤率後面會談到】數據結構

那麼在這種狀況下,利用布隆過濾器來解決的確是很優秀,優秀到維基百科這樣說「它的優勢是空間效率和查詢時間都遠遠超過通常的算法」,空間複雜度和時間複雜度都遠超通常的算法,布隆過濾器存儲空間和插入/查詢時間都是常數O(1)!注意:遠超!!併發

看着來自各方面這麼牛B 的吹噓,我們把布隆過濾器安排到方方面面,來具體看看它的原理是怎麼樣的...app

維基百科的概念:布隆過濾器其實是一個很長的二進制向量和一系列隨機映射函數。布隆過濾器能夠用於檢索一個元素是否在一個集合中。它的優勢是空間效率和查詢時間都遠遠超過通常的算法,缺點是有必定的誤識別率和刪除困難。基於這個解釋,下面從

等方面系統性的說道說道...

【1】

主要做用:判斷一個元素是否在集合中

這樣的場景會有不少。會去判斷,要查詢的元素是否存在於集合當中。【該網站是否容許該用戶登陸、該網站共是否接受這樣的 url 請求】

一般在查詢的時候,通常會先從 cache 中進行查詢,若是沒有的話會直接到磁盤或者數據庫查詢,這樣的方式看起來很合理,可是若是在中間再加一層布隆顧慮器,這樣就會更加合理了!爲何?

假設要查詢的一個元素,而該元素不存在

a. 若是沒有 BloomFilter,從cache中查詢完就會直接到數據庫作查詢了,這樣帶來的現象是「慢」,畢竟從庫中查耗時是比較長的,很大程度上對服務的性能產生影響。

b. 中間存在 BloomFilter,從cache中查詢完就會首先查詢 BloomFilter,就會發現該元素不存在,就能夠不日後面進行查詢了,而 BloomFilter 的性能是極其優越的。這樣,對於機器或者說服務性能避免了很大沒必要要的消耗。

就上圖所示,假設元素不存在,若是沒有布隆過濾器,就直接會查詢【磁盤/數據庫】,這樣會帶來很大沒必要要的性能消耗!

既然效率這麼高,那究竟是什麼緣由呢?

【2】

數據結構:二進制數組+hash算法

二進制數組

這個是最關鍵的一個數據結構,會將每個元素通過 hash 算法映射到每個二進制數組中去。

開篇講到在時間複雜度和空間複雜度方面來講,都是在常數級別,這也歸功於一個數據結構就是由比特位構成的數組,因此在空間這塊是頗有優點的。就拿一個URL 64B來講,對應比特位就是1,大概是 64*8:1 這個空間比例。

hash 算法

對於hash算法來講,應該是比較熟悉了,數據元素通過 hash 函數會映射到不一樣的數組中,若是發生衝突可使用一些方法進行解決,好比說是拉鍊法解決。

hash 函數應用在 BloomFilter 中的時候,與通常的 hash 函數處理有區別的地方是:

a. BloomFilter 將值不會映射到一個地址,而是映射到對應的二進制數組位,而後將該數組爲置爲 1

b. BloomFilter 不會採用 hash 函數中經常使用的解決地址衝突的方法,而是會將同一個元素,通過幾個 hash function 後,將對應二進制數組位置置爲 1,後期若是進行查詢的時候,只有通過hash函數後,幾個位置同時爲1,才能夠判斷該元素存在

看下圖:

按照圖例,每一個數據元素會通過 3 個不一樣的 hash function,而後對應到不一樣的二進制數組位,而且置爲 1,這樣一方面減少了衝突的機率,另一方面會減少偏差率。

當一個客戶端查詢過來,對於 URL一、URL2 和 URL3 在BloomFilter 中都是存在的,因此對這三個元素進行查詢的時候,必定是能夠查到的。

下面我們試着用 URL一、URL4 和 URL5 進行舉例說明:查找成功、查找不存在以及查找失誤這三種真實存在的狀況。【下圖👇】

在 BloomFilter 的二進制數組右邊是指的進行客戶端查詢 URL一、URL4 和 URL5時候的狀況,能夠看到:

  • URL1:徹底能夠和存儲 URL1 時候的數組位可以對應上而且同時都爲1,即位置:一、五、9。查詢成功!
  • URL4:在數組位 2 處爲0,其他爲 1,致使三個位置不全爲1,查找不存在,返回空!
  • URL5:URL5 的三個二進制數組位都爲 1,按原理說結果是URL5元素存在於BloomFilter中,可是能夠清楚看到,查詢有錯誤!這個就是查詢產生失誤的狀況。

這也就可以說明,BloomFIlter在空間和時間方面是極其優秀的,都達到了 O(1) 的級別,可是缺點是存在誤識別率。

在這裏注意,誤識別率僅僅是存在查找成功的狀況下,查詢不存在是沒有誤識別率的。即:

  • 查詢存在:存在誤識別率,就上面圖示所示
  • 查詢不存在:確定不會存在都是 1 的狀況,因此這方面不會存在誤識別率

【3】

優點:省空間+高性能

省空間

這個在URL長度的案例說到,正是採用了二進制數組,一個元素通過 hash 函數對應着一個數組位(比特位),若是是真實存儲一個URL(64B)的話,這就空間比例大約是64*8:1,即512:1

比特數組促使 BloomFilter 會省很大的空間,就空間效率來講,是極其高效的。

高性能

BloomFilter 不存在像鏈表查詢同樣,須要一個一個去遍歷。反而會像數組同樣,相似於直接取下標就能夠找到所須要的結果。

不一樣的是,BloomFilter 須要過幾個hash function,去查找下標。因此,綜合來看,性能是很高的!

綜合時間和空間效率,在有很低的誤識別率狀況下,各方面都是遠超其餘算法的。

【4】

不足:有誤判+不能刪除元素

有誤判的狀況 - hash存在衝突

在前面舉例 URL5 的時候,這種狀況就會使得查詢出現了失誤,這也是傳統hash衝突的直接表現,同時這也是使用了幾個 hash 函數的緣由。而這種錯誤識別,後期可使用白名單的形式進行標記以補全這方面的不足。

可是在真正工業界的使用來看,這種衝突或者說查詢失誤也是保持在 0.01% 如下,一方面會考量 hash 函數的使用,另一方面會增大 BloomFilter 二進制數組的位數來避免這種衝突。

那麼,就上面兩個考慮的方面,來具體看看:到底須要多大的二進制數組的長度以及多少個hash 函數 纔可使得上述案例的失誤率保持在 0.01% 如下?

二進制bit數組長度的選取

記: n = 100 n=100 億, p = 0.01 p=0.01% , BloomFilter 的二進制bit數組長度 m 使用如下公式決定的:

m = n × l n p ( l n 2 ) 2 m=\frac{n\times ln p}{(ln 2)^2}

能夠求得:m=19.19n,即二進制bit數組長度大概是元素個數 n 的20倍,須要200億個bit位,至關於大約25GB的空間,對於咱們普通工業界的服務器來講,是足夠容納這個數組的

最好須要多少個 hash 函數

業界通常用這個公式來計算須要 k 個 hash 函數:

k = l n 2 × m n = 0.7 × m n = 14 k=ln2 \times \frac {m}{n} = 0.7 \times \frac {m}{n} = 14

即:須要 14 個 hash 函數來進行構造

根據上述的計算,能夠獲得咱們要使用 BloomFilter 的最佳方案

不能刪除元素

這個就很好理解了,不一樣的元素經過 hash 函數使得相應位置都置爲了 1,是絕對不能刪除該元素,將它對應的位置置爲 0 的。

下圖中的 URL1 和 URL2,通過 hash 函數後,其中一個hash結果都指向了二進制數組中的3位置【紅色箭頭】,若是如今想要刪除URL1,那麼,按理說應該將 URL1 hash後指向的二進制數組對應位置都置爲 0 纔對,顯然,這樣作是不行的,會影響到 URL2 的查詢。

【5】

業界應用

工業界會有不少這種場景會使用到,例如:

  • 博客系統黑名單限制

  • 爬蟲重複率的斷定

  • 比特幣的應用

  • 垃圾郵件過濾

    等等...

不過大體都是很通用的一些解決方案,好比下圖所示:

在信息寫入的時候,會同時寫入到緩存、數據庫和布隆過濾器。須要查詢的時候,先進行對緩存進行查詢,若是找不到的話,就會先使用 BloomFilter 進行判斷是否存在,若是存在就會繼續向數據庫查詢;若是不存在,就直接返回空了。

這樣在很大程度上提高了應用服務的效率!

相關文章
相關標籤/搜索