天天看點

高并發系統一定要考慮的 Bloom Filter 布隆過濾器Bloom Filter 布隆過濾器原理布隆過濾器的優點、缺點動手玩一玩喜歡文章請關注我

開篇思考
  1. 你能想到哪些方式判斷一個元素是否存在集合中?
  2. 布隆過濾器并不存儲資料本身,那麼是怎麼做到過濾的?
  3. 布隆過濾器實作?參數配置?

一般我們用來判斷一個元素是否存在,會想到用 List,Map,Set 等,會将元素先儲存下來,然後進行篩選。

但是這樣的形式都有一個弊端就是一定要儲存資料才行,可是我們僅僅想知道是否存在資料,并不要求擷取實際資料,

這時候就會覺得這種方式實在是浪費空間。

什麼情況下我們隻需要判斷是否存在這個元素呢?

在系統設計的時候,我們會考慮大量并發的形式,但是很多請求可能是在通路不存在的資料,

那麼我們就沒有必要繼續這個請求,可以在 API 網關層就直接過濾掉。

Bloom Filter 布隆過濾器原理

Bloom filter 是由 Howard Bloom 在 1970 年提出的二進制向量資料結構,它具有很好的空間和時間效率,

被用來檢測一個元素是不是集合中的一個成員。

布隆過濾器實作是不儲存資料本身,而是通過 K 個 hash 函數來計算在 byte[] 數組中的存放位置,

并把這個位置的值設定為 1, 而這個 K 到底是多少個呢,要根據公式來算出,待會列出。

除了這個 K 值,我們還要計算 byte[] 數組的長度 m ,下面一并列出計算公式:

高并發系統一定要考慮的 Bloom Filter 布隆過濾器Bloom Filter 布隆過濾器原理布隆過濾器的優點、缺點動手玩一玩喜歡文章請關注我
  • fpp : 誤判率參數,(must be 0 < fpp < 1)
  • n :預估的需要過濾的總數量
  • ln :求對數,不會的把高中老師的名字寫下來
高并發系統一定要考慮的 Bloom Filter 布隆過濾器Bloom Filter 布隆過濾器原理布隆過濾器的優點、缺點動手玩一玩喜歡文章請關注我
  • m :數組長度
  • n :預估的需要過濾的總數量

下面我們以數字 11 為例來使用,有個網站可以測試布隆過濾器,

線上測試布隆
高并發系統一定要考慮的 Bloom Filter 布隆過濾器Bloom Filter 布隆過濾器原理布隆過濾器的優點、缺點動手玩一玩喜歡文章請關注我

布隆過濾器的優點、缺點

優點:

  • 節省空間,不用儲存所有資料,知識通過 hash 值來計算位置,并通過 byte[] 記錄下來。
  • 速度快,時間複雜度低 O(1);

缺點:

  • 精度低,假設:a 計算的位置 1 ,3 ;b 計算的位置 5,7;c 計算的位置 1,7,那麼 c 一定存在嗎?
  • 不能直接删除,因為想要删除就要把對應的位置置為 0 ,如果這樣做,可能會影響其他值的過濾。
高并發系統一定要考慮的 Bloom Filter 布隆過濾器Bloom Filter 布隆過濾器原理布隆過濾器的優點、缺點動手玩一玩喜歡文章請關注我

# 布隆過濾器實作

這個其實在 google guava 包中有現成的實作,不用我們自己去實作。我們看看是怎麼實作的;

/**
   * 計算 bit 數組的長度公式
   * n : 預估資料量
   * p : 誤差率 0-1
   */
   @VisibleForTesting
   static long optimalNumOfBits(long n, double p) {
       if (p == 0.0D) {
           p = 4.9E-324D;
       }

       return (long)((double)(-n) * Math.log(p) / (Math.log(2.0D) * Math.log(2.0D)));
   }           
/**
    * 計算 hash 函數個數的方法
    * n : 預估資料量
    * m : bit 數組長度
    */
    @VisibleForTesting
    static int optimalNumOfHashFunctions(long n, long m) {
        return Math.max(1, (int)Math.round((double)(m / n) * Math.log(2.0D)));
    }           

動手玩一玩

  • expectedInsertions 代表預估數量,越大越準确,在下面的例子中,可以自己随意設定 p 值,過小會發現後面會傳回 true
  • fpp : 誤差率 0-1
import com.google.common.base.Charsets;
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;

public class BloomFilterTest {

    public static void main(String[] args) {

        int expectedInsertions = 800000000;
        double fpp = 0.00001;

        BloomFilter<CharSequence> bloomFilter = BloomFilter.create(Funnels.stringFunnel(Charsets.UTF_8), expectedInsertions, fpp);
        int i = 10000;
        while (i > 1){
            bloomFilter.put("aa" + i);
            System.out.println(bloomFilter.mightContain("ab" + i));
            i--;
        }

    }
}

           

喜歡文章請關注我

程式領域