布隆过滤器--你可以永远相信布隆

一看到布隆这个名字想必大多数人都想到了LOL里的英雄布隆,作为一名常玩辅助的玩家,我也喜欢玩布隆。

哈哈哈,言归正传,我们来认识下一另一个布隆。

image-20221020093506925

1. 什么是布隆过滤器

布隆过滤器(Bloom Filter)是1970年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都比一般的算法要好的多,缺点是有一定的误识别率和删除困难。

我们可以先假定布隆过滤器就是一个set集合,判断某个元素是否存在这个集合中。

有同学可能会问:那我们直接用HashMap去检索元素行不行呢,为啥还要用布隆过滤器呢?HashMap 确实可以帮助我们实现这个功能,而且 HashMap 通过一次运算就能确定某个元素的位置,也就可以帮助我们检查某个元素是否在一个集合中。那么我们接下来再思考一个问题:如果这个元素集合中有十亿个随机数,那我们怎样来判断某个数是否存在呢?首先就会带来非常大的空间消耗。

为了解决这个问题,我们的布隆过滤器出现了。

但是由于它是基于概率的,因此它存在一定的误判率,它的Contains()操作如果返回true只是表示元素可能存在集合内,返回false则表示元素一定不存在集合内。因此适合用于能够容忍一定误判元素存在集合内的场景,比如缓存。

它一秒能够进行上百万次操作(主要取决于哈希函数的速度),并且1亿数据在误判率1%的情况下,只需要114MB内存。

2. 布隆过滤器的原理

2.1 数据结构

布隆过滤器的数据结构是一个位向量,也就是一个由01所组成的向量(下面是一个初始向量):

image-20221020100501964

2.2 添加元素

每个元素添加进布隆过滤器前,都会经过多个不同的哈希函数,计算出不同的哈希值,然后映射到位向量上,也就是对应的位上面置1:

image-20221020101332058

2.3 判断是否存在

判断元素是否存在也是如上图流程,根据哈希函数映射的位置,判断所有映射位置是否都为1,如果是则元素可能存在,否则元素一定不存在。

由于不同的值通过哈希函数之后可能会映射到相同的位置,因此如果一个不存在的元素对应的位位置都被其他元素所设置位1,则查询时就会误判:

image-20221020101432107

假设上图元素3334并没有加入集合,但是由于它映射的位置已经被其他元素所映射,则查询时会误判。

2.4 删除元素

布隆过滤器对元素的删除不太支持,目前有一些变形的特定布隆过滤器支持元素的删除!关于为什么对删除不太支持,其实也非常好理解,hash冲突必然存在,删除肯定是带来连锁反应

3. 布隆过滤器的优缺点

优点:

  • 时间复杂度低,增加和查询元素的时间复杂度位O(N)(N位哈希函数的个数,通常情况下比较小)
  • 保密性强,布隆过滤器不存储元素本身
  • 存储空间小,如果允许存在一定的误判,布隆过滤器是非常节省空间的。

缺点:

  • 存在一定的误判率,但可以通过调整参数来降低
  • 无法获取元素本身
  • 很难删除元素

4布隆过滤器的应用

布隆过滤器可以告诉我们 “某样东西一定不存在或者可能存在”,也就是说布隆过滤器说这个数不存在则一定不存,布隆过滤器说这个数存在可能不存在,利用这个判断是否存在的特点可以做很多有趣的事情。

  • 解决Redis缓存穿透问题(面试重点)
  • 邮件过滤,使用布隆过滤器来做邮件黑名单过滤
  • 对爬虫网址进行过滤,爬过的不再爬
  • 解决新闻推荐过的不再推荐(类似抖音刷过的往下滑动不再刷到)
  • HBase\RocksDB\LevelDB等数据库内置布隆过滤器,用于判断数据是否存在,可以减少数据库的IO请求

5 模拟实现布隆过滤器

定义一个hash函数

/**
 * 构建hash函数
 */
public class HashFunction {
    // 比特位的大小
    private int size;
    // hash算法的种子
    private int seed;

    public HashFunction() {
    }

    public HashFunction(int size, int seed) {
        this.size = size;
        this.seed = seed;
    }

    public int hash(String value) {
        int result = 0;
        int len = value.length();
        // 执行hash算法
        for (int i = 0; i < len; i++) {
            result = result * seed + value.charAt(i);
        }
        return (size - 1) & result;
    }
}

测试类

import java.util.BitSet;

public class MyBloomFilter {
    // 一个长度为10亿的比特位 2^30
    private static final int DEFAULT_SIZE = 256 << 22;

    // 为了降低错误率,使用假发hash算法,定义一个8个元素的质数数组作为种子
    private static final int[] seeds = {3, 5, 7, 11, 13, 31, 37, 61};

    // 构建了八个不同的hash算法
    private static HashFunction[] functions = new HashFunction[seeds.length];

    // 初始化布隆过滤器的bitmap,即位数组
    private static BitSet bitSet = new BitSet(DEFAULT_SIZE);

    // 添加数据
    public static void add(String value) {
        if (value != null) {
            for (HashFunction f : functions) {
                // 计算hash值并修改bitmap中相应位置位true
                bitSet.set(f.hash(value), true);
            }
        }
    }

    // 判断元素是否存在
    public static boolean contains(String value) {
        if (value == null)
            return false;
        boolean ok = true;
        for (HashFunction f : functions) {
            // 只要有一个hash值不存在就退出循环
            if (!bitSet.get(f.hash(value))) {
                ok = false;
                break;
            }
        }
        return ok;
    }

    public static void main(String[] args) {
        for (int i = 0; i < seeds.length; i++) {
            functions[i] = new HashFunction(DEFAULT_SIZE, seeds[i]);
        }

        // 添加1亿个元素
        for (int i = 0; i < 100000000; i++) {
            add(String.valueOf(i));
        }

        String id = "123456789";
        add(id);

        System.out.println("123456789 是否存在:" + contains(id));
        System.out.println("234567890 是否存在:" + contains("234567890"));
    }

}

image-20221020155625954

效果还是可以的。

笔记参考:

布隆(Bloom Filter)过滤器——全面讲解,建议收藏

布隆过滤器:一种低空间成本的判断元素是否存在的方式 - 掘金 (juejin.cn))

大聪明教你学Java | 深入浅出聊布隆过滤器(Bloom Filter) - 掘金 (juejin.cn))

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值