1. 程式人生 > 其它 >中國大學MOOC-陳越、何欽銘-資料結構-起步能力自測題

中國大學MOOC-陳越、何欽銘-資料結構-起步能力自測題

技術標籤:Redis資料結構與演算法布隆過濾器redis

布隆過濾器

布隆過濾器(Bloom Filter)是一種來檢索元素是否在給定大集合中的資料結構,這種資料結構是高效且效能很好的,但缺點是具有一定的錯誤識別率和刪除難度。並且,理論情況下,新增到集合中的元素越多,誤報的可能性就越大。

它由位陣列和一系列雜湊函式構成。初始時位陣列的值全為0。

布隆過濾器原理

加入元素

當一個元素加入布隆過濾器中的時候,會進行如下操作:

  1. 使用布隆過濾器中的雜湊函式對元素值進行計算,得到雜湊值(有幾個雜湊函式得到幾個雜湊值)。
  2. 根據得到的雜湊值,在位陣列中把對應下標的值置為 1。

判斷元素是否存在

當我們需要判斷一個元素是否存在於布隆過濾器的時候,會進行如下操作:

  1. 使用布隆過濾器中的雜湊函式對該元素值進行計算;
  2. 得到多個雜湊值之後判斷位陣列中的對應為位置元素是否都為 1,如果值都為 1,那麼說明這個值在布隆過濾器中,如果存在一個值不為 1,說明該元素不在布隆過濾器中。

由於不同的元素值通過相同的雜湊函式可能為得到相同的雜湊值,所以布隆過濾器在判斷元素存在時為存在誤判的情況,但判斷元素不存在時是確定的。

使用場景

  1. 判斷給定資料是否存在於一個大量資料集合中:比如判斷一個數字是否存在於包含大量數字的數字集中(數字集很大,5億以上!)、 防止快取穿透(判斷請求的資料是否有效避免直接繞過快取請求資料庫)等等、郵箱的垃圾郵件過濾、黑名單功能等等。
  2. 去重:比如爬給定網址的時候對已經爬取過的 URL 去重。

Java 實現布隆過濾器

可以自己實現,也可以使用 Guava 提供的實現。

package top.zysite.bloom.filter;

import java.util.BitSet;

/**
 * 布隆過濾器
 *
 * @author Leo
 * @create 2020/12/25 15:12
 **/
public class MyBloomFilter {
    /**
     * 位陣列的大小
     */
    private static final int DEFAULT_SIZE = 2 << 24;
    /**
     * 通過這個陣列可以建立 6 個不同的雜湊函式
     */
private static final int[] SEEDS = new int[]{3, 13, 46, 71, 91, 134}; /** * 位陣列。陣列中的元素只能是 0 或者 1 */ private BitSet bits = new BitSet(DEFAULT_SIZE); /** * 存放包含 hash 函式的類的陣列 */ private SimpleHash[] func = new SimpleHash[SEEDS.length]; /** * 初始化多個包含 hash 函式的類的陣列,每個類中的 hash 函式都不一樣 */ public MyBloomFilter() { // 初始化多個不同的 Hash 函式 for (int i = 0; i < SEEDS.length; i++) { func[i] = new SimpleHash(DEFAULT_SIZE, SEEDS[i]); } } /** * 新增元素到位陣列 */ public void add(Object value) { for (SimpleHash f : func) { bits.set(f.hash(value), true); } } /** * 判斷指定元素是否存在於位陣列 */ public boolean contains(Object value) { boolean ret = true; for (SimpleHash f : func) { ret = ret && bits.get(f.hash(value)); } return ret; } /** * 靜態內部類。用於 hash 操作! */ public static class SimpleHash { private int cap; private int seed; public SimpleHash(int cap, int seed) { this.cap = cap; this.seed = seed; } /** * 計算 hash 值 */ public int hash(Object value) { int h; return (value == null) ? 0 : Math.abs(seed * (cap - 1) & ((h = value.hashCode()) ^ (h >>> 16))); } } }

測試:

package top.zysite.bloom.filter;

import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;
import org.junit.Test;

/**
 * 布隆過濾器測試類
 *
 * @author Leo
 * @create 2020/12/25 15:15
 **/
public class MyBloomFilterTest {

    /**
     * 測試自己實現的布隆過濾器
     */
    @Test
    public void testMyBloomFilter() {
        //建立布隆過濾器物件
        MyBloomFilter myBloomFilter = new MyBloomFilter();

        String str1 = "MyBloomFilterTest";
        String str2 = "testMyBloomFilter";

        System.out.println(myBloomFilter.contains(str1));
        System.out.println(myBloomFilter.contains(str2));

        //將元素放入布隆過濾器
        myBloomFilter.add(str1);
        myBloomFilter.add(str2);

        System.out.println(myBloomFilter.contains(str1));
        System.out.println(myBloomFilter.contains(str2));
    }

    /**
     * 測試 Guava 提供的布隆過濾器
     */
    @Test
    public void testGuavaBloomFilter(){
        // 建立布隆過濾器物件
        BloomFilter<Integer> filter = BloomFilter.create(
                Funnels.integerFunnel(),
                1500,
                0.01);
        // 判斷指定元素是否存在
        System.out.println(filter.mightContain(1));
        System.out.println(filter.mightContain(2));

        // 將元素新增進布隆過濾器
        filter.put(1);
        filter.put(2);

        System.out.println(filter.mightContain(1));
        System.out.println(filter.mightContain(2));
    }

}

使用 Guava 提供的實現需引入以下依賴:

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>28.0-jre</version>
</dependency>

Redis 中使用布隆過濾器

Redis v4.0 之後有了 Module(模組/外掛) 功能,Redis Modules 讓 Redis 可以使用外部模組擴充套件其功能 。布隆過濾器就是其中的 Module。

官網推薦了一個 RedisBloom 作為 Redis 布隆過濾器的 Module 地址:https://github.com/RedisBloom/RedisBloom

根據專案的 Readme 可以快速入門。

我的更多文章盡在:我的個人部落格