布隆過濾器與布谷鳥過濾器

一、布隆過濾器

1.1 原理

1.1.1 布隆過濾器基礎(chǔ)版

原理就是一個對一個key進行k個hash算法獲取k個值,在比特數(shù)組中將這k個值散列后設定為1,然后查的時候如果特定的這幾個位置都為1,那么布隆過濾器判斷該key存在。

布隆過濾器可能會誤判,如果它說不存在那肯定不存在,如果它說存在,那數(shù)據(jù)有可能實際不存在;

Redis的bitmap只支持2^32大小,對應到內(nèi)存也就是512MB,誤判率萬分之一,可以放下2億左右的數(shù)據(jù),性能高,空間占用率及小,省去了大量無效的數(shù)據(jù)庫連接。

image.png

存入過程: 通過三個hash函數(shù)計算出三個哈希值,然后將三個值映射到數(shù)組中將0改成1。
查詢過程:通過三個hash函數(shù)計算出查詢數(shù)據(jù)的哈希值,然后檢查布隆過濾器對應位置上的值是否為1,如果有一個不為1表示該值不存在,如果都為1表示該值可能存在。(查詢時間復雜度為O(k),k為哈希函數(shù)個數(shù))
刪除過程:不能進行刪除,因為會刪除掉其他數(shù)據(jù)。
更新過程:也不能進行更新。

1.1.2 布隆過濾器增強版

為了解決上面布隆過濾器的問題,出現(xiàn)了一個增強版的布隆過濾器(Counting Bloom Filter),這個過濾器的思路是將布隆過濾器的bitmap更換成數(shù)組,當數(shù)組某位置被映射一次時就+1,當刪除時就-1,這樣就避免了普通布隆過濾器刪除數(shù)據(jù)后需要重新計算其余數(shù)據(jù)包Hash的問題,但是依舊沒法避免誤判。

image.png


1.2 應用

image.png
  1. redis緩存穿透(大量查詢不存在于數(shù)據(jù)庫中的數(shù)據(jù)):使用布隆過濾器進行過濾,如果不存在直接跳過查詢數(shù)據(jù)庫,返回結(jié)果。

  2. 新聞客戶端的推送去重功能,當推薦系統(tǒng)推薦新聞時會從每個用戶的歷史記錄里進行篩選,過濾掉那些已經(jīng)存在的記錄。它在起到去重的同時,在空間上還能節(jié)省 90% 以上,只是稍微有那么點不精確,也就是有一定的誤判概率。

  3. 黑白名單

  4. 塊索引是HBase固有的一個特性,因為HBase的底層數(shù)據(jù)是存儲在HFile中的,而每個HFile中存儲的是有序的<key, value>鍵值對,HFile文件內(nèi)部由連續(xù)的塊組成,每個塊中存儲的第一行數(shù)據(jù)的行鍵組成了這個文件的塊索引,這些塊索引信息存儲在文件尾部。當HBase打開一個HFile時,塊索引信息會優(yōu)先加載到內(nèi)存;HBase首先在內(nèi)存的塊索引中進行二分查找,確定可能包含給定鍵的塊,然后讀取磁盤塊找到實際想要的鍵。
    但實際應用中,僅僅只有塊索引滿足不了需求,這是因為,塊索引能幫助我們更快地在一個文件中找到想要的數(shù)據(jù),但是我們可能依然需要掃描很多文件。而布隆過濾器就是為解決這個問題而生。因為布隆過濾器的作用是,用戶可以立即判斷一個文件是否包含特定的行鍵,從而幫我們過濾掉一些不需要掃描的文件。

1.3 代碼實現(xiàn)

1.3.1 實現(xiàn)一(Guava)

調(diào)用谷歌的guava包的api就可以。

缺點:guava版實現(xiàn)主要問題在于無法支持集群環(huán)境. 為了支持集群環(huán)境主要考慮通過redis setbit來實現(xiàn)BloomFilter。

創(chuàng)建布隆過濾器對象:

  // 參數(shù)Funnels.integerFunnel()是默認參數(shù),size是預計存入的數(shù)據(jù)量,fpp是設置的誤判率
  public static <T> BloomFilter<T> create(
      Funnel<? super T> funnel, int expectedInsertions, double fpp) {
    return create(funnel, (long) expectedInsertions, fpp);
  }

誤判率越低,哈希函數(shù)個數(shù)和布隆過濾器數(shù)組長度越大,運算效率越低。

放入數(shù)據(jù):

  @CanIgnoreReturnValue
  public boolean put(T object) {
    return strategy.put(object, funnel, numHashFunctions, bits);
  }

判斷是否存在:

  public boolean mightContain(T object) {
    return strategy.mightContain(object, funnel, numHashFunctions, bits);
  }

1.3.2 實現(xiàn)二(Redisson)

使用redis實現(xiàn)

依賴

<dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.6.5</version>
</dependency>

代碼

public class RedissonBloomFilter {

    public static void main(String[] args) {
        Config config = new Config();
        config.useSingleServer().setAddress("redis://192.168.32.128:6379");
//        config.useSingleServer().setPassword("");
        // 構(gòu)造Redisson
        RedissonClient redissonClient = Redisson.create(config);

        // 初始化布隆過濾器:預計元素為100000000L個,誤差率為3%
        RBloomFilter<Object> bloomFilter = redissonClient.getBloomFilter("phoneList");
        bloomFilter.tryInit(100000000L, 0.03);

        // 將號碼插入到布隆過濾器中
        bloomFilter.add("10086");

        // 判斷下面的號碼是否在布隆過濾器中
        System.out.println(bloomFilter.contains("10000"));
        System.out.println(bloomFilter.contains("10086"));

    }

}


1.4 布隆過濾器的缺點

  • 不支持刪除元素:一旦對位數(shù)組進行了賦值,無法將其刪除??梢酝ㄟ^增加每個數(shù)組元素大小使用增強版過濾器實現(xiàn)刪除功能。
  • 查詢性能弱:布隆過濾器使用多個hash函數(shù)計算位圖多個不同位點,由于多個位點在內(nèi)存中不連續(xù),CPU尋址花銷較大。
  • 空間利用率低。


二、布谷鳥過濾器

2.1 原理

image.png

布谷鳥哈希結(jié)構(gòu):布谷鳥過濾器由一個數(shù)組組成,數(shù)組中元素大小為4個字節(jié),可以存儲4個指紋,每個指紋占一個字節(jié)(128種)。同一個數(shù)組元素 上的多個座位在內(nèi)存空間上是連續(xù)的,可以有效利用 CPU 高速緩存。

type bucket [4]byte   // 一個桶,4個座位
type cuckoo_filter struct {
    buckets [size]bucket  // 一維數(shù)組
    nums int   // 容納的元素個數(shù)
    kick_max   // 最大擠兌次數(shù)
}

插入:首先計算數(shù)據(jù)的指紋和哈希值,并通過指紋和哈希值計算另一個哈希值,兩個哈希值映射到兩個位置(因為計算得到兩個位置,每個位置存儲4個指紋,所以相同對象最多存儲8個)。接下來進行插入,會嘗試插入兩個位置,如果都失敗,隨機擠走一個指紋,并重新為該指紋尋找新的位置(超過最大擠兌次數(shù)后擴容)。
擴容:如果數(shù)組過小,會發(fā)生循環(huán)擠兌的情況,就可以設置最大擠兌次數(shù),如果超過該次數(shù),進行擴容,重新計算每個指紋的位置。
查找:通過計算哈希值得到兩個元素,對兩個元素中的8個位置進行指紋對比,如果對比成功則表示數(shù)據(jù)存在。如果哈希值和指紋相同時會發(fā)生誤判(小概率)。
刪除:因為每個對象的指紋會存儲到一個位置中,所以可以通過刪除這個指紋來刪除數(shù)據(jù)。刪除功能無法使用的情況:如果相同對象存儲超過8個,就無法使用刪除功能;如果倆數(shù)據(jù)的哈希值和指紋相同時,會出現(xiàn)誤刪除情況。
更新:即刪除后再添加新指紋。

2.2 應用

同布隆過濾器。

2.3 代碼實現(xiàn)

貌似目前沒有現(xiàn)成的java實現(xiàn)。
可以參考如下項目:

2.4 布谷鳥過濾器的優(yōu)缺點

對比布隆過濾器:
布谷鳥過濾器在錯誤率小于3%的時候空間性能是優(yōu)于布隆過濾器的,而這個條件在實際應用中常常滿足,所以一般來說它的空間性能是要優(yōu)于布隆過濾器的。布谷鳥過濾器按照普通設計,只有兩個Hash表,在查找的時候可以確保兩次訪存就可以做完,相比于布隆過濾器的K個Hash函數(shù)K次訪存,在數(shù)據(jù)量很大不能全部裝載在內(nèi)存中的情況下,多次訪問內(nèi)存性能較差。
當然,布谷過濾器也有其相應的缺點,當裝填數(shù)據(jù)過多的時候,容易出現(xiàn)循環(huán)的問題,即插入失敗的情況。另外,它還跟布隆過濾器共有的一個缺點,就是訪問空間地址不連續(xù),內(nèi)存尋址消耗大。

優(yōu)點:

  • 訪問內(nèi)存次數(shù)低
  • Hash函數(shù)計算簡單
  • 存在刪除操作。如果相同數(shù)據(jù)個數(shù)不超過8個,那么刪除操作可用;但是因為存儲的是計算后的指紋信息,存在誤刪除的可能,所以不好用。

缺點:

  • 內(nèi)存空間不聯(lián)系,CPU消耗大。
  • 容易出現(xiàn)裝填循環(huán)問題:Hash沖突踢出原數(shù)據(jù),原數(shù)據(jù)還是存在沖突。
  • 刪除數(shù)據(jù)時,Hash沖突會引起誤刪:查詢有誤判,那么刪除也會有誤刪。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容