面試點-布隆過濾器

直入主題,布隆過濾器是什麼?

布隆過濾器是一個很長的二進制向量和一系列隨機映射函數。布隆過濾器可以用於檢索一個元素是否在一個集合中,它的優點是空間效率和查詢時間都比一般的算法要好的多,缺點是有一定 的誤差率 和刪除困難 。

面試題–騰訊2019有一道面試題就說:

一個網站有 20 億 url 存在一個黑名單中,這個黑名單要怎麼存?
若此時隨便輸入一個 url,你如何快速判斷該 url 是否在這個黑名單中?
並且需在給定內存空間(比如:500M)內快速判斷出

我們假設就用hashset基於haspMap的特性,保證鍵的唯一,理論上時間複雜度是:O(1)。這時候的空間複雜度呢?URL字符串通過hash函數得到一個integer的值,integer佔4個字節,那這裏的20條數據就需要
20(億) *4 /1024/1024/1024 = 7.45G 的內存

簡單說一下:1G= 1024MB = 1024*1024KB = 1024*1024*1024 B 
           1B = 8bit 

那根據計算遠遠超出空間存儲大小,不符合空間複雜度的要求。

使用布隆過濾器

首先,一個hash 算法得出的最大Integer爲 : Integer.MAX_VALUE = 2147483647,意思就是任何一個URL的哈希都會在0~2147483647之間。

那麼我們假如先定義一個2147483647 長度的byte數組,用來存儲集合所有可能的值,爲了存儲這個byte數組,系統只需要 2147483647 / 8 /1024 /1024 = 256M。

那麼,回到實際問題上,這些URL都經過hash算法之後,假如 一個hash值爲2,那麼落到這個byte數組在第二位上的數就是1,這個byte數組將是 000…000000010,基於此我們就可以將這20億數全部hash並落到byte數組中。

使用布隆過濾器所導致的問題

回到上面,假如說我現在byte數組第二位是1,那麼這個URL可能存在?因爲有可能其它URL因爲hash碰撞hash出來的也是2,這就是誤判。

但是如果這個byte數組上的第二位是0。那麼這個URL就一定不存在。

這裏就可以進行多次hash的策略了
爲了減少因hash碰撞導致的誤判概率,可以對這個URL用不同的hash算法進行N次hash,得出N個hash值,落到這個byte數組上,如果這N個位置都沒有爲1,那麼就說明這個URL一定不存在這個集合中。

public static <T> BloomFilter<T> create(Funnel<? super T> funnel, int expectedInsertions, double fpp);
public static <T> BloomFilter<T> create(Funnel<? super T> funnel, long expectedInsertions, double fpp);
public static <T> BloomFilter<T> create(Funnel<? super T> funnel, int expectedInsertions);
public static <T> BloomFilter<T> create(Funnel<? super T> funnel, long expectedInsertions);

Guava提供BloomFilter的靜態create方法。

static <T> BloomFilter<T> create(Funnel<? super T> funnel, long expectedInsertions, double fpp, Strategy strategy);
// 參數含義:
// funnel 指定布隆過濾器中存的是什麼類型的數據,有:IntegerFunnel,LongFunnel,StringCharsetFunnel。
// expectedInsertions 預期需要存儲的數據量
// fpp 誤判率,默認是0.03。

最終調用。

BloomFilter裏的 byte數組空間大小是有expectedINsertions , fpp 參數決定

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)));
}

真正的byte數組 在維護類是 bitArray 。

最後可以通過put 或者 mightContain方法 ,添加元素 和是否包含。

這個算法的特點在於

布隆過濾器 解決的僅僅只是判斷該元素一定不存在該集合中。
如果說一定存在其實是有誤判的可能。另外因爲無法分辨hash碰撞,所以不是很好做刪除操作。

使用場景

1、諸如 黑名單的校驗
2、URL去重 。這裏如果對於安全性要求不是很高的系統。需要設置對應ip及端口映射,使用訪問路徑定點登錄說不定是可以實現的,但是一般系統在這裏使用的應該是API 網關zuul之類的。
3、key-value 緩存key校驗 。redis 的緩存穿透 中,一個數據如果既不存在redis中,也不存在數據庫中,這時候黑客數據大量攻擊/訪問數據庫,造成系統奔潰。這裏在redis判斷沒有該熱點數據的時候可以加一個布隆過濾器。對於數據庫中真實存在的數據我才處理這個請求。
4、ID校驗。面試中很多面試官的實際場景問題也都是ID校驗,比如訂單系統中查詢某個訂單ID是否存在。如果不存在就直接返回。

布隆過濾器就聊到這裏啦。

最後說一下最近跟一個朋友聊天,他問我寫博客真的有用麼?
其實博客本身的價值在於你對一個知識點的認識和總結。開始寫的時候可能都是認識一些知識。隨着寫的多了理解的高度就不同了。像現在讓我談HashMap我感覺不只是在大學時候那種淺顯的指導key不重複。甚至我很推薦和我一樣剛剛畢業的大學生寫博客。不止記錄技術的學習。對生活和其它各個學科都可以寫。

ok,週末愉快!

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