開篇思考
- 你能想到哪些方式判斷一個元素是否存在集合中?
- 布隆過濾器並不存儲數據本身,那麼是怎麼做到過濾的?
- 布隆過濾器實現?參數配置?
一般我們用來判斷一個元素是否存在,會想到用 List,Map,Set 等,會將元素先保存下來,然後進行篩選。 但是這樣的形式都有一個弊端就是一定要保存數據才行,可是我們僅僅想知道是否存在數據,並不要求獲取實際數據,
這時候就會覺得這種方式實在是浪費空間。
什麼情況下我們只需要判斷是否存在這個元素呢? 在系統設計的時候,我們會考慮大量併發的形式,但是很多請求可能是在訪問不存在的數據,
那麼我們就沒有必要繼續這個請求,可以在 API 網關層就直接過濾掉。
Bloom Filter 布隆過濾器原理
Bloom filter 是由 Howard Bloom 在 1970 年提出的二進制向量數據結構,它具有很好的空間和時間效率, 被用來檢測一個元素是不是集合中的一個成員。
布隆過濾器實現是不保存數據本身,而是通過 K 個 hash 函數來計算在 byte[] 數組中的存放位置,
並把這個位置的值設置爲 1, 而這個 K 到底是多少個呢,要根據公式來算出,待會列出。 除了這個 K 值,我們還要計算 byte[] 數組的長度 m ,下面一併列出計算公式:
-
fpp : 誤判率參數,(must be 0 < fpp < 1)
-
n :預估的需要過濾的總數量
-
ln :求對數,不會的把高中老師的名字寫下來
-
m :數組長度
-
n :預估的需要過濾的總數量
下面我們以數字 11 爲例來使用,有個網站可以測試布隆過濾器,
在線測試布隆
布隆過濾器的優點、缺點
優點:
- 節省空間,不用保存所有數據,知識通過 hash 值來計算位置,並通過 byte[] 記錄下來。
- 速度快,時間複雜度低 O(1);
缺點:
-
精度低,假設:a 計算的位置 1 ,3 ;b 計算的位置 5,7;c 計算的位置 1,7,那麼 c 一定存在嗎?
-
不能直接刪除,因爲想要刪除就要把對應的位置置爲 0 ,如果這樣做,可能會影響其他值的過濾。
布隆過濾器實現
這個其實在 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--;
}
}
}
喜歡文章請關注我
程序領域
點擊關注+轉發,私信發送【面試】或者【資料】可以收穫更多資源