布隆過濾器是什麼鬼?有什麼用?

LLSS發表於2021-08-25

前言

應對快取穿透問題,我們可以使用布隆過濾器。我們先來回顧下快取穿透知識點哈:

一個常見的快取使用方式:讀請求來了,先查下快取,快取有值命中,就直接返回;快取沒命中,就去查資料庫,然後把資料庫的值更新到快取,再返回。

快取穿透:指查詢一個一定不存在的資料,由於快取是不命中時需要從資料庫查詢,查不到資料則不寫入快取,這將導致這個不存在的資料每次請求都要到資料庫去查詢,進而給資料庫帶來壓力。

假設我們需要查產品詳情,有查詢請求進來,我們先根據產品Id直接去快取中查一下,沒有的話,再去查下資料庫。如果現在有大量請求進來,而且都在請求一個不存在的產品Id,那麼這些請求都會懟到資料庫,資料庫壓力一上來,可能就掛了。我們可以在請求資料庫層前,加個中間層,去緩解資料庫壓力嘛,如果,不存在的話,就不去查資料庫啦。

這個中間層,是不是用HashMap就好了呢?聽起來不錯嘛,HashMap時間複雜度可以達到O(1),但是呢因為HashMap資料是在記憶體裡面的,如果大量的資料遠超出了伺服器的記憶體呢,那就無法使用HashMap啦,可以使用布隆過濾器來做這個緩衝的事情。

布隆過濾是什麼

布隆過濾器是一種佔用空間很小的資料結構,它由一個很長的二進位制向量和一組Hash對映函式組成,它用於檢索一個元素是否在一個集合中,空間效率和查詢時間都比一般的演算法要好的多,缺點是有一定的誤識別率和刪除困難。

布隆過濾器原理是? 假設我們有個集合A,A中有n個元素。利用k個雜湊雜湊函式,將A中的每個元素對映到一個長度為a位的陣列B中的不同位置上,這些位置上的二進位制數均設定為1。如果待檢查的元素,經過這k個雜湊雜湊函式的對映後,發現其k個位置上的二進位制數全部為1,這個元素很可能屬於集合A,反之,一定不屬於集合A

來看個簡單例子吧,假設集合A有3個元素,分別為{d1,d2,d3}。有1個雜湊函式,為Hash1。現在將A的每個元素對映到長度為16位陣列B。

布隆過濾器是什麼鬼?有什麼用?

我們現在把d1對映過來,假設Hash1(d1)= 2,我們就把陣列B中,下標為2的格子改成1,如下:

布隆過濾器是什麼鬼?有什麼用?

我們現在把d2也對映過來,假設Hash1(d2)= 5,我們把陣列B中,下標為5的格子也改成1,如下:

布隆過濾器是什麼鬼?有什麼用?

接著我們把d3也對映過來,假設Hash1(d3)也等於 2,它也是把下標為2的格子標1:

因此,我們要確認一個元素dn是否在集合A裡,我們只要算出Hash1(dn)得到的索引下標,只要是0,那就表示這個元素不在集合A,如果索引下標是1呢?那該元素可能是A中的某一個元素。因為你看,d1和d3得到的下標值,都可能是1,還可能是其他別的數對映的,布隆過濾器是存在這個缺點的:會存在hash碰撞導致的假陽性,判斷存在誤差。

如何減少這種誤差呢?

  • 搞多幾個雜湊函式對映,降低雜湊碰撞的概率
  • 同時增加B陣列的bit長度,可以增大hash函式生成的資料的範圍,也可以降低雜湊碰撞的概率

我們又增加一個Hash2雜湊對映函式,假設Hash2(d1)=6,Hash2(d3)=8,它倆不就不衝突了嘛,如下:

布隆過濾器是什麼鬼?有什麼用?

即使存在誤差,我們可以發現,布隆過濾器並沒有存放完整的資料,它只是運用一系列雜湊對映函式計算出位置,然後填充二進位制向量。如果數量很大的話,布隆過濾器通過極少的錯誤率,換取了儲存空間的極大節省,還是挺划算的。

目前布隆過濾器已經有相應實現的開源類庫啦,如Google的Guava類庫,Twitter的 Algebird 類庫,信手拈來即可,或者基於Redis自帶的Bitmaps自行實現設計也是可以的。

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章