布隆过滤器

什么是布隆过滤器

  • 布隆过滤器(Bloom Filter)是有布隆在 1970 年提出的。它实际上是由一个很长的二进制向量和一系列随机映射函数组成。布隆过滤器是用来检索一个元素是否在一个集合中。
    * 优点:空间效率和查询时间都远远超过一般算法,因为使用数组存入,同时使用二进制,占用空间小
    * 缺点:
    1. 有一定的误识别率
    2. 很难做删除操作

原理

对 Key 进行 n 个 hash 算法获取 n 个值,在比特数组中将这 k 个值三列后设定为1。查的时候如果特定的这几个位置都为1,那么布隆过滤器判断该 Key 存在

增删查

  • 增:经过多个 hash 函数,把
  • 删:

说明

布隆过滤器是一种数据结构,一种概率性的数据结果,它会告诉你“某样东西一定不存在或者可能存在”

布隆过滤器是一个 bit 向量或者说 bit 数组,如下:

现在,我们把 “AliPay” 给存储进去,存储过程:将要映射的值,使用多个不同的 hash 函数生产多个哈希值,然后每个生产的哈希值执行的 bit 置为1

同样,现在我要存储另外一个值 “WechatPay” ,那么可能映射之后就是下面这样

4 号位置的值,刚开始给 “AliPay” 了,后来 “WechatPay” 也在那里,这样的话,值不就给覆盖掉了嘛

嗯,没错,是被覆盖掉了

接下来,我们查询 “Ali” 那么查询之后,布隆过滤器可能会给你 “0,1,2” 的值, 结果呢 “2” 的位置是 0 ,说明没有任何值映射到这个位置上来,所以我们就可以判定数据库里面没有 “Ali” 这个值

那查询 “AliPay” 的话,毫无疑问,肯定会返回给我 “1,4,6” ,那我们能说数据库里面一定有 “AliPay” 么?不能,因为 “1,4,6” 的值有可能被其他的值给覆盖到了,所以我们只能说,数据库里可能存在 “AliPay”

这就是布隆过滤器说的"某个值一定不存在或者可能存在"

布隆过滤器使用场景

  1. 针对缓存穿透,使用布隆过滤器,防止不存在的数据直接打到数据库上
  2. 去重:比如新闻推荐,当推荐系统推荐新闻时会从每个用户的历史记录里进行筛选,过滤掉已经存在的记录。如果使用 Mysql 类似的关系型数据库,需要进行大量的查询,并发量很大时,数据库压力很大。在用户浏览记录存入数据库时,把记录也保存一份到布隆过滤器,推送新闻的时候,通过布隆过滤器判断,推送内容存在就不推送了,不存在就推送。

实际开发使用布隆过滤器

直接采用 Guava 的 BloomFiler

布隆过滤器实现

  1. MyBloomFilter.java
import java.util.Arrays;
import java.util.BitSet;

/**
 * 布隆过滤器
 */
public class MyBloomFilter {
    //布隆过滤器容量
    private static final int DEFAULT_SIZE = 2 << 28;
    // bit 数组,用来存放结果
    private static BitSet bitSet = new BitSet(DEFAULT_SIZE);
    // hash 函数会用到,用来生成不同的 hash 值,可随意设置
    private static final int[] ints = {1, 6, 16, 38, 58, 68};

    // hash 函数,借鉴了 hashmap 的扰动算法
    private int hash(Object key, int i) {
        int h;
        return key == null ? 0 : (i * (DEFAULT_SIZE - 1) & ((h = key.hashCode()) ^ (h >>> 16)));
    }

    // add 方法,计算出 key 的 hash 值,并将对应下标置为 true
    public void add(Object key) {
        Arrays.stream(ints).forEach(i -> bitSet.set(hash(key, i)));
    }

    // 判断 key 是否存在,true 不一定说明 key 存在,但是 false 一定说明不存在
    public boolean isContain(Object key) {
        boolean result = true;
        for (int i : ints) {
            // 短路与,只要有一个 bit 位为false,则返回false
            result = result && bitSet.get(hash(key, i));
        }
        return result;
    }
}
  1. 测试数据
public class MyBloomFilterTest {
    public static void main(String[] args) {
        MyBloomFilter bloomFilter = new MyBloomFilter();
        bloomFilter.add("张学友");
        bloomFilter.add("郭德纲");
        bloomFilter.add("刘德华");
        System.out.println(bloomFilter.isContain("张学友"));
        System.out.println(bloomFilter.isContain("郭德纲"));
        System.out.println(bloomFilter.isContain("刘德华"));
        System.out.println(bloomFilter.isContain("666"));
        System.out.println(bloomFilter.isContain("888"));
    }
}

测试结果:
true
true
true
false
false

参考:
https://blog.csdn.net/qq_33709582/article/details/108407706
https://mp.weixin.qq.com/s/SlfLgsfbvytxNS46fTFUdA

posted @ 2020-10-17 21:58  熠然  阅读(196)  评论(0编辑  收藏  举报