布隆過濾器 - 如何在100個億URL中快速判斷某URL是否存在?


題目描述

一個網站有 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 ),因此所占空間為:

因此,當 N 為 1 億的時候需要 12MB 的存儲空間。當 N 為 10 億的時候需要 120MB 的存儲空間了。當 N 的數量大到一定量級的時候,比如 N 為 2^64 這個海量級別的時候,需要消耗 2048PB 的存儲空間,這個量級的BitMap,目前硬件上是支持不了的。

也就是說:位圖法的所占空間隨集合內最大元素的增大而增大。這就會帶來一個問題,如果查找的元素數量少但其中某個元素的值很大,比如數字范圍是 1 到 1000 億,那消耗的空間不容樂觀。

這個就是位圖的一個不容忽視的缺點空間復雜度隨集合內最大元素增大而線性增大。對於開頭的題目而言,使用位圖進行處理,實際上內存消耗也是不少的。

因此,出於性能和內存占用的考慮,在這里使用布隆過濾器才是最好的解決方案:布隆過濾器是對位圖的一種改進。

布隆過濾器

布隆過濾器(英語:Bloom Filter)是 1970 年由 Burton Bloom 提出的。

它實際上是一個很長的二進制矢量和一系列隨機映射函數

可以用來判斷一個元素是否在一個集合中。它的優勢是只需要占用很小的內存空間以及有着高效的查詢效率。

對於布隆過濾器而言,它的本質是一個位數組:位數組就是數組的每個元素都只占用 1 bit ,並且每個元素只能是 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,這是誤判率存在的原因。

所以,有可能一個不存在布隆過濾器中的會被誤判成在布隆過濾器中。

這就是布隆過濾器的一個缺陷:存在誤判。

但是,如果布隆過濾器判斷某個元素不在布隆過濾器中,那么這個值就一定不在布隆過濾器中。總結就是:

  • 布隆過濾器說某個元素在,可能會被誤判
  • 布隆過濾器說某個元素不在,那么一定不在

用英文說就是:False is always false. True is maybe true。

誤判率

布隆過濾器可以插入元素,但不可以刪除已有元素。其中的元素越多,false positive rate(誤報率)越大,但是false negative (漏報)是不可能的。

補救方法

布隆過濾器存在一定的誤識別率。常見的補救辦法是在建立白名單,存儲那些可能被誤判的元素。 比如你苦等的offer 可能被系統丟在郵件垃圾箱(白名單)了。

使用場景

布隆過濾器的最大的用處就是,能夠迅速判斷一個元素是否在一個集合中。因此它有如下三個使用場景:

  • 網頁爬蟲對 URL 的去重,避免爬取相同的 URL 地址
  • 進行垃圾郵件過濾:反垃圾郵件,從數十億個垃圾郵件列表中判斷某郵箱是否垃圾郵箱(同理,垃圾短信)
  • 有的黑客為了讓服務宕機,他們會構建大量不存在於緩存中的 key 向服務器發起請求,在數據量足夠大的情況下,頻繁的數據庫查詢可能導致 DB 掛掉。布隆過濾器很好的解決了緩存擊穿的問題。

回到問題

回到一開始的問題,如果面試官問你如何在海量數據中快速判斷該 url 是否在黑名單中時,你應該回答使用布隆過濾器進行處理,然后說明一下為什么不使用 hash 和 bitmap,以及布隆過濾器的基本原理,最后你再談談它的使用場景那就更好了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM