Redis之布隆過濾器(BloomFilter)

在實際的開發中通常會遇到數據去重的問題,比如判斷用戶是不是新用戶,哪些用戶登陸過系統等等這些類似的問題,比較容易想到的解決方案如下:

  • 使用set,但是數據量比較大的時候會非常浪費內存,並且性能也不高
  • 使用之前提到的BitMap,但是在數據稀疏的場景下,反而還不如set

那麼這種場景應該如何解決呢?業界有另外一個更優秀的數據結構來解決這類問題,那就是——布隆過濾器(BloomFilter)

什麼是布隆過濾器

布隆過濾器與BitMap類似,底層也是一個位數組。1表示有,0表示無。但布隆過濾器比BitMap需要更少的內存,它是怎麼辦到的呢?答案是多個hash。

hash算法就是把任意長度的輸入,通過算法,變換成固定長度的輸出,該輸出就是hash值。

比如我們有一個10位的數組,使用某個hash算法及其數組上的表示:

hash("布隆") = 3

hash(“布隆過濾器”) = 5

0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0

這樣,我們使用這個hash算法就能快速的判斷一個字符串是不是存在一個集合裏面了。但衆所周知,hash算法是有可能發生hash衝突的。比如可能有兩個不同的字符串映射到同一個數:

hash("布隆") = 3

hash("布隆過濾器") = 3

這種情況下,就不能準確得判斷出某個字符串是不是存在於集合之中呢。

那怎麼解決這個問題呢?答案是使用多個不同的hash算法。比如:

h1("布隆") = 3、h2("布隆") = 5、h3("布隆") = 7

h1("過濾器") = 5、h2("過濾器") = 6、h3("過濾器") = 7

h1("布隆過濾器") = 3、h2("布隆過濾器") = 6、h3("布隆過濾器") = 9

最開始,集合裏沒有元素,所有位都是0:

0, 0, 0, 0, 0, 0, 0, 0, 0, 0

然後,插入"布隆",利用多次hash,把每次hash的結果下標3,5,7都插入到相應的地方:

0, 0, 0, 1, 0, 1, 0, 1, 0, 0

然後,插入"過濾器",利用多次hash,把每次hash的結果下標5,6,7都插入到相應的地方,已經是1的下標不變

0, 0, 0, 1, 0, 1, 1, 1, 0, 0

然後,插入"布隆過濾器",利用多次hash,把每次hash的結果下表3,6,9插入到相應的地方,同樣已經是1的下標不變

0, 0, 0, 1, 0, 1, 1, 1, 0, 1

這個時候,如果想要判斷"布隆"是否在集合中,只需要使用同樣的3個hash算法,來計算出下標是3, 5, 7,發現這3個下標都爲1,那麼就認爲"布隆"這個字符串在集合中。而"布隆過濾器"計算出來的下標是3, 6, 9。發現這三個下標有不是1的地方,比如下標爲9的地方是0,那就說明"布隆過濾器"這個字符串還不在集合中。

關於布隆過濾器的誤差

從布隆過濾器的原理來看,是存在一定誤差的,尤其是當hash函數比較少的情況下。

布隆過濾器是根據多次hash計算下標後,數組的這些下標是否都爲1來判斷這個元素是否存在的。所以是存在一定的機率,要檢查的元素實際上沒有插入,但被其它元素插入影響,導致所有下標都爲1。

所以布隆過濾器不能刪除,因爲一旦刪除(即將相應的位置爲0),就很大可能會影響其他元素。

如果使用布隆過濾器判斷一個函數是否存在於一個集合,如果它返回true,則代表可能存在。如果它返回false,則代表一定不存在

針對布隆過濾器存在一定誤差的原因,常見的應用場景是對準確率要求不那麼高的場景:

  • 統計有多少用戶登陸過系統
  • 判斷用戶是否看過某篇文章

當然,對準確性要求嚴格的場景就不適合使用布隆過濾器:

  • 用戶是否購買過課程
  • 用戶是否收藏過我的文章

當然針對緩存穿透問題,布隆過濾器也是一個很好的解決方案。

redis中的布隆過濾器

Redis的布隆過濾器主要有兩個命令:

bf.add 添加元素到布隆過濾器中:bf.add strs xy
bf.exists 判斷某個元素是否在過濾器中:bf.exists strs xy

Redis中有一個命令可以來設置布隆過濾器的準確率:

bf.reserve strs 0.01 100

三個參數的含義:

  • 第一個值是過濾器的名字。
  • 第二個值爲error_rate的值:允許布隆過濾器的錯誤率。
  • 第三個值爲initial_size的值:初始化位數組的大小。
拓展學習
  • Java實現的布隆過濾器

如果你的項目沒有使用Redis,那可以使用一些開源庫,基於代碼實現,直接存放在內存。比如Google的guava包中提供了BloomFilter類,有興趣的讀者可以去了解一下,研究研究源碼和使用。

  • 布穀鳥過濾器

RedisBloom模塊還實現了布穀鳥過濾器,它算是對布隆過濾器的增強版。解決了布隆過濾器的一些比較明顯的缺點,比如:不能刪除元素,不能計數等。除此之外,布穀鳥過濾器不用使用多個hash函數,所以查詢性能更高。除此之外,在相同的誤判率下,布穀鳥過濾器的空間利用率要明顯高於布隆,空間上大概能節省40%多。

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