大白話布隆過濾器,又能和麪試官扯皮了~

前言

  • 文章首發於微信公衆號大白話布隆過濾器,又能和麪試官扯皮了~
  • 近期在作推薦系統中已讀內容去重的相關內容,恰好用到了布隆過濾器,因而寫了一篇文章記錄分享一下。
  • 文章的篇幅不是很長,主要講了布隆過濾器的核心思想,目錄以下:

什麼是布隆過濾器?

  • 布隆過濾器是由一個長度爲 m比特的位 數組k哈希函數組成的數據結構。比特數組均初始化爲 0,全部哈希函數均可以分別把輸入數據儘可能均勻地散列。
  • 插入一個元素時,將其數據經過 k個哈希函數轉換成 k個哈希值,這 k個哈希值將做爲比特數組的 下標,並將數組中的對應下標的值置爲 1
  • 查詢一個元素時,一樣會將其數據經過 k個哈希函數轉換成 k個哈希值(數組下標),查詢數組中對應下標的值,若是有一個下標的值爲 0代表該元素必定不在集合中,若是所有下標的值都爲 1,代表該元素有 可能在集合中。 至於爲何有可能在集合中? 由於有可能某個或者多個下標的值爲 1 是受到其餘元素的影響,這就是所謂的 假陽性,下文會詳細講述。
  • 沒法刪除一個元素,爲何呢?由於你刪除的元素的哈希值可能和集合中的某個元素的哈希值有相同的,一旦刪除了這個元素會致使其餘的元素也被刪除。
  • 下圖示出一個 m=18, k=3的布隆過濾器示例。集合中的 x、y、z 三個元素經過 3 個不一樣的哈希函數散列到位數組中。當查詢元素 w 時,由於有一個比特爲 0,所以 w 不在該集合中。

假陽性機率的計算

  • 假陽性是布隆過濾器的一個痛點,所以須要不擇一切手段來使假陽性的機率下降,此時就須要計算一下假陽性的機率了。
  • 假設咱們的哈希函數選擇位數組中的比特時,都是等機率的。固然在設計哈希函數時,也應該儘可能知足均勻分佈。
  • 在位數組長度 m的布隆過濾器中插入一個元素,它的其中一個哈希函數會將某個特定的比特置爲 1。所以,在插入元素後,該比特仍然爲 0 的機率是:
  •  
  • 現有 k個哈希函數,並插入 n個元素,天然就能夠獲得該比特仍然爲 0 的機率是:
  • 反過來說,它已經被置爲 1的機率就是:
  •  
  • 也就是說,若是在插入 n個元素後,咱們用一個不在集合中的元素來檢測,那麼被誤報爲存在於集合中的機率(也就是全部哈希函數對應的比特都爲 1的機率)爲:
  • n比較大時,根據極限公式,能夠近似得出假陽性率:
  •  
  • 因此,在哈希函數個數 k必定的狀況下有以下結論:
    1. 位數組長度 m 越大,假陽性率越低。
    2. 已插入元素的個數 n 越大,假陽性率越高。

優勢

  • 用比特數組表示,不用存儲數據自己,對空間的節省相比於傳統方式佔據絕對的優點。
  • 時間效率很高,不管是插入仍是查詢,只須要簡單的通過哈希函數轉換,時間複雜度均爲 O(k)

缺點

  • 存在 假陽性的機率,準確率要求高的場景不太適用。
  • 只能插入和查詢,不能刪除了元素。

應用場景

  • 布隆過濾器的用途不少,可是主要的做用就是去重,這裏列舉幾個使用場景。

爬蟲重複 URL 檢測

  • 試想一下,百度是一個爬蟲,它會定時蒐集各大網站的信息,文章,那麼它是如何保證爬取到文章信息不重複,它會將 URL 存放到布隆過濾器中,每次爬取以前先從布隆過濾器中判斷這個 URL 是否存在,這樣就避免了重複爬取。固然這種存在假陽性的可能,可是隻要你的比特數組足夠大, 假陽性的機率會很低,另外一方面,你認爲百度會在乎這種的偏差嗎,你的一篇文章可能由於假陽性機率沒有收錄到,對百度有影響嗎?

抖音推薦功能

  • 讀者朋友們應該沒人沒刷過抖音吧,每次刷的時候抖音給你的視頻有重複的嗎?他是如何保證推薦的內容不重複的呢?
  • 最容易想到的就是抖音會記錄用戶的歷史觀看記錄,而後從歷史記錄中排除。這是一種解決辦法,可是性能呢?不用多說了,有點常識的都知道這不可能。
  • 解決這種重複的問題,布隆過濾器有着絕對的優點,可以很輕鬆的解決。

防止緩存穿透

  • 緩存穿透是指查詢一條數據庫和緩存都沒有的一條數據,就會一直查詢數據庫,對數據庫的訪問壓力會一直增大。
  • 布隆過濾器在解決緩存穿透的問題效果也是很好,這裏再也不細說,後續文章會寫。

如何實現布隆過濾器?

  • 瞭解布隆過濾器的設計思想以後,想要實現一個布隆過濾器其實很簡單,陳某這裏就再也不搬門弄斧了,介紹一下現成的實現方式吧。

Redis 實現

  • Redis4.0 以後推出了插件的功能,下面用 docker 安裝:
docker pull redislabs/rebloom
docker run -p6379:6379 redislabs/rebloom
複製代碼
  • 安裝完成後鏈接 redis 便可,運行命令:
redis-cli
複製代碼
  • 至於具體的使用這裏再也不演示了,直接看官方文檔和教程,使用起來仍是很簡單的。

Guava 實現

  • guava 對應布隆過濾器的實現作出了支持,使用 guava 能夠很輕鬆的實現一個布隆過濾器。

1. 建立布隆過濾器java

  • 建立布隆過濾器,以下:
BloomFilter<Integer> filter = BloomFilter.create(
                    Funnels.integerFunnel(),
                    5000,
                    0.01);
//插入
IntStream.range(0, 100_000).forEach(filter::put);
//判斷是否存在
boolean b = filter.mightContain(1);
複製代碼
  • arg1:用於將任意類型 T 的輸入數據轉化爲 Java 基本類型的數據,這裏轉換爲 byte
  • arg2:byte 字節數組的基數
  • arg3:指望的假陽性機率

2.估計最優 m 值和 k 值面試

  • guava 在底層對 byte 數組的基數(m)和哈希函數的個數 k 作了本身的算法,源碼以下:
//m值的計算
  static long optimalNumOfBits(long n, double p) {
    if (p == 0) {
      p = Double.MIN_VALUE;
    }
    return (long) (-n * Math.log(p) / (Math.log(2) * Math.log(2)));
  }

  //k值的計算
  static int optimalNumOfHashFunctions(long n, long m) {
    // (m / n) * log(2), but avoid truncation due to division!
    return Math.max(1, (int) Math.round((double) m / n * Math.log(2)));
  }
複製代碼
  • 想要理解 guava 的計算原理,還要從的上面推導的過程繼續。
  • 由假陽性率的近似計算方法可知,若是要使假陽性率儘可能小,在 m 和 n 給定的狀況下, k值應爲:
  •  
  • 將 k 代入上一節的式子並化簡,咱們能夠整理出指望假陽性率 p 與 m、n 的關係:
  • 換算而得:
  •  
  • 根據以上分析得出如下的結論:
    1. 若是指按期望假陽性率 p,那麼最優的 m 值與指望元素數 n 呈線性關係。
    2. 最優的 k 值實際上只與 p 有關,與 m 和 n 都無關,即:
    3. 綜上兩個結論,在建立布隆過濾器的時候,肯定 p值和 m值很重要。

總結

  • 至此,布隆過濾器的知識介紹到這裏,若是以爲陳某寫得不錯的,轉發在看點一波,讀者的一份支持將會是我莫大的鼓勵。
  • 另外想和陳某私聊或者想要加交流羣的朋友,公衆號回覆關鍵詞加羣加陳某微信,陳某會第一時間拉你進羣。


巨人的肩膀

  • https://blog.csdn.net/u012422440/article/details/94088166
  • https://blog.csdn.net/Revivedsun/article/details/94992323
相關文章
相關標籤/搜索