布隆过滤器

布隆过滤器

什么是布隆过滤器?

简单来说就是,可以判断一个元素在不在一个集合中的一个比较剩内存的方法。

假设我们有如下场景:

有一家公司需要做一个排除黑名单的业务,希望在黑名单中的url不会在自己公司的业务中被使用到,现在假设有100亿条黑名单url,假定每条url的字符串长度占用64个字节,我们使用传统的hash表的方式,创建了一个很大的hashmap,那么这hashmap占用的内存至少是 64 * 100 * 100000000 / 1024 / 1024 /1024 = 596G 内存。就为了这么一个非核心业务消耗这么多的内存显然是不值得的,这个时候就轮到我们的布隆过滤器登场啦!

布隆过滤器适用的场景是,只有增加和查询的集合,查询也就是查询这条数据是否存在于这个集合中,且在这个集合中不存在删除操作,查询的业务,存在一定的误判容忍率,这里的误判是指把原本不存在于黑名单中的url,误判成黑名单中的了。但是只要设计合理,这个误判率就会很低。

在讲解布隆过滤器之前,我们还需要先了解一下什么是位图。

什么是位图?

一个长度为100的int型数组,它所占用的内存为 100 * 4 = 400字节

一个长度为100的long型数组,它所占用的内存为 100 * 8 = 800字节

那么一个长度为100的bit(计算机中内存都是按照字节来计算的,一个字节=8bit)数组,它所占用的内存为 100 * 1 / 8 = 12.5字节

如果我们所需的业务场景,可以用int数组实现,可以用long数组实现,也可以用bit数组来实现,那显然,使用bit数组来实现是最节省内存的做法。

bit数组使用的简单代码如下:

/**
 * @author bwzfy
 * @create 2023/5/18
 **/
public class BitMap {

    public int[] arr;

    public int size;

    public int capacity;

    /**
     * 因为int和bit之间是32倍的关系
     */
    public int ratio = 32;

    public BitMap(int capacity) {
        this.capacity = capacity;
        // 创建长度为 100bit的位图数组
        // 这里因为一个int表示四个字节,所以一个int可以表示32个bit的信息,使用逻辑是bit,但是实际使用的数据类型是int,当然也可以使用其他的数据类型
        size = (capacity / ratio + 1);
        arr = new int[size];
    }


    /**
     * 获取index位置上的bit的状态,是0还是1
     * @param index
     * @return
     */
    public int getBit(int index) {
        if (index > capacity) {
            throw new RuntimeException("不能超出当前bit数组的容量");
        }
        // 获取第index个bit对应的int数组是第几个
        int arrIndex = index / ratio;
        // 找到对应的int值之后,找出int对应的32个bit的,具体bit位
        int bitIndex = index % ratio;
        // 找到对应的int值,将int值往右移动bitIndex位,然后我们只需要看移动后最靠右的位置的值就行了,如果前面还有多余的bit位,
        // 那就&上1将前面干扰我们观察的bit位都清0
        // 最后的返回值不是0就是1
        return (this.arr[arrIndex] >> bitIndex) & 1;
    }

    /**
     * 给某个bit位设置0
     * @param index
     */
    public void setBit0(int index) {
        if (index > capacity) {
            throw new RuntimeException("不能超出当前bit数组的容量");
        }
        // 获取第index个bit对应的int数组是第几个
        int arrIndex = index / ratio;
        // 找到对应的int值之后,找出int对应的32个bit的,具体bit位
        int bitIndex = index % ratio;
        this.arr[arrIndex] = this.arr[arrIndex] & (~(1 << bitIndex));
    }

    /**
     * 给某个bit位设置1
     * @param index
     */
    public void setBit1(int index) {
        if (index > capacity) {
            throw new RuntimeException("不能超出当前bit数组的容量");
        }
        // 获取第index个bit对应的int数组是第几个
        int arrIndex = index / ratio;
        // 找到对应的int值之后,找出int对应的32个bit的,具体bit位
        int bitIndex = index % ratio;
        this.arr[arrIndex] = this.arr[arrIndex] | (1 << bitIndex);
    }

    public static void main(String[] args) {
        BitMap bitMap = new BitMap(100);
        bitMap.setBit1(8);
        bitMap.setBit1(9);
        System.out.println(bitMap.getBit(7));
        System.out.println(bitMap.getBit(8));
        System.out.println(bitMap.getBit(9));
        bitMap.setBit0(8);
        bitMap.setBit0(9);
        System.out.println(bitMap.getBit(7));
        System.out.println(bitMap.getBit(8));
        System.out.println(bitMap.getBit(9));
    }
}

可以使用位运算,对int以及各种数据类型进行各种各样的bit操作。

了解了bit数组之后,我们就可以开始了解布隆过滤器的运行流程了。

布隆过滤器运行流程

将需要加入布隆过滤器的元素进行k个不同的hash函数进行计算,再对m取余。将取余后的结果对应到一开始创建好的bit数组上,将对应数组下标的元素涂黑(置1),当我们将100亿个URL都这样操作完成之后,布隆过滤器就创建完成了。

当查询一个url是否存在与布隆过滤器时,只需要再将url,进行三个hash的运算,然后将三个值分别对m取余,然后判断三个下标对应bit数组上的值是否为1,如果都为1,那就说明,布隆过滤器中存在这个url。

由于布隆过滤器的机制,所以不会出现将存在于布隆过滤器中的url误判为不存在,但是可能会将不存在于布隆过滤器中的url误判为存在。因此在使用布隆过滤器的时候需要设置好合适的m(bit数组长度),以及k(hash函数的数量)。

如何设定布隆过滤器?

设计布隆过滤器,需要的几个重要的参数: n = 样本量(上面案例中的100亿个url), p = 失误率,m = 布隆过滤器占用比特数, k = 使用k个hash函兽计算

前置条件,先看一下需求:

  • 是不是只有增加和查询,没有删除操作,类似于黑名单查询的
  • 问失误率

设置布隆过滤器,只和这两个参数有关,和单样本本身的大小没有关系。

公式:

m可能算到小数,那就向上取整

m = − n ∗ l n p ( l n 2 ) 2 m=-\frac{n*lnp} {(ln2)^2} m=(ln2)2nlnp

k可能算到小数,那就向上取整

k = l n 2 ∗ m n k = ln2 * \frac{m} {n} k=ln2nm

最后的实际失误率 p 真 p_真 p

p 真 = ( 1 − e − n ∗ k 真 m 真 ) 真 k p_真=(1-e^{- \frac{n * k_真} {m_真}})^k_真 p=(1emnk)k

最后只要我们把mk计算出来之后,剩下来的就是coding的事情了

上面的100亿url案例使用布隆过滤器实现之后的占用内存为26G,相较于传统的hash表需要来596G内存来实现,节省了相当大的内存,单台服务器就可轻松解决。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值