ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

布隆过滤器

2021-03-21 19:58:40  阅读:201  来源: 互联网

标签:bf 误判 元素 布隆 哈希 过滤器


什么是布隆过滤器(Bloom Filter)

1.引言
上一篇博客: Redis缓存雪崩、缓存击穿、缓存穿透
在上一篇博客中,我们了解了Redis缓存雪崩、缓存击穿、缓存穿透,并且知道了解决缓存穿透可以使用布隆过滤器,接下来我们就一起来看看这个布隆过滤器吧!

2.布隆过滤器

首先,我们需要了解布隆过滤器的概念。
布隆过滤器(Bloom Filter)是一个叫做 Bloom 的老哥于1970年提出的。我们可以把它看作由二进制向量(或者说位数组)和一系列随机映射函数(哈希函数)两部分组成的数据结构。相比于我们平时常用的的 List、Map 、Set 等数据结构,它占用空间更少并且效率更高,但是缺点是其返回的结果是概率性的,而不是非常准确的。理论情况下添加到集合中的元素越多,误报的可能性就越大。并且,存放在布隆过滤器的数据不容易删除。
如果想要判断一个元素是不是在一个集合里,一般想到的是将所有元素保存起来,然后通过比较确定。链表,树等等数据结构都是这种思路. 但是随着集合中元素的增加,我们需要的存储空间越来越大,检索速度也越来越慢(O(n),O(logn))。不过世界上还有一种叫作散列表(又叫哈希表,Hash table)的数据结构。它可以通过一个Hash函数将一个元素映射成一个位阵列(Bit array)中的一个点。这样一来,我们只要看看这个点是不是1就可以知道集合中有没有它了。这就是布隆过滤器的基本思想。

布隆过滤器的原理

在这里插入图片描述
每个元素都只占用 1 bit ,并且每个元素只能是 0 或者 1。我们用不同位置的1来组合成不同的数据。

当我们向布隆过滤器中添加一个元素的时候,布隆过滤器是这样将它存储的:
第一步、使用哈希函数对元素进行计算 ,然后得到哈希值。(有几个哈希函数就会得到几个值)。
第二步、根据得到的哈希值,在位数组中把对应下标的值置为 1。

如图所示:
在布隆过滤器中存储 hello 这个数据,那么首先会计算hello 的哈希函数值, 然后算出来对应的下标值, 然后把对应的值改为1即可。(注意,最开始的时候数组里所有元素均为0)

在这里插入图片描述

注意这里不一定必须是三个hash函数,这个可以自己设置的,待会我们就用java语言来模拟一下这个过程。

如果我们需要判断某个字符串是否在布隆过滤器中时,只需要对给定字符串再次进行相同的哈希计算,得到值之后判断位数组中的每个元素是否都为 1,如果值都为 1,那么说明这个值在布隆过滤器中,如果存在一个值不为 1,说明该元素不在布隆过滤器中。

不同的字符串可能通过哈希函数计算的出来的位置相同,这种情况我们可以适当增加位数组大小或者调整我们的哈希函数

布隆过滤器使用场景

1.首先肯定是解决Redis缓存穿透问题。(判断请求是否无效)
2.在海量元素中(例如 10 亿无序、不定长、不重复)快速判断一个元素是否存在。
3.识别恶意 URL;
4.从数十亿个垃圾邮件列表中判断某邮箱是否垃圾邮箱;
。。。

布隆过滤器缺陷

布隆过滤器有一个缺陷,就是误判率。
什么叫误判率?
误判率是指当我们使用布隆过滤器查询一个数据时,结果返回的都是1,则说明它存在对吧。这样的话就错了!它有可能不存在,因为有可能其他数据的哈希结果也是这样。

我们可以总结一下:

如果布隆过滤器判断元素在集合中存在,不一定存在;
如果布隆过滤器判断不存在,一定不存在;
如果元素实际存在,布隆过滤器一定判断存在
如果元素实际不存在,布隆过滤器可能判断存在

3.java实现

接下来我们就用Google开源的Guava来实现一下布隆过滤器。

第一步、引入guava依赖

<dependency> 
          <groupId>com.google.guava</groupId> 
          <artifactId>guava</artifactId> 
          <version>21.0</version> 
      </dependency>

第二步

import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;

public class BloomFilterTest {
     //插入10000条数据数据
    private  static  final  int NUMBER = 100000;
    //误判率
    private static double fpp = 0.02;

    public static void main(String[] args) {
        //初始化布隆过滤器,默认误判率 0.02
        BloomFilter<Integer> bf = BloomFilter.create(Funnels.integerFunnel(), NUMBER, fpp);
        // 判断指定元素是否存在
        System.out.println(bf.mightContain(1));
        System.out.println(bf.mightContain(2));
        // 将元素添加进布隆过滤器
        bf.put(1);
        bf.put(2);
        System.out.println(bf.mightContain(1));
        System.out.println(bf.mightContain(2));


    }
}

输出结果:

false
false
true
true

方法返回true时,我们可以99%确定该元素在过滤器中,当过滤器返回false时,我们可以100%确定该元素不存在于过滤器中。

下面我们来改造一下代码,来看看误判率:

import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;

public class BloomFilterTest {
     //插入10000条数据数据
    private  static  final  int NUMBER = 100000;
    //误判率
    private static double fpp = 0.02;

    public static void main(String[] args) {
        //初始化布隆过滤器,默认误判率 0.02
        BloomFilter<Integer> bf = BloomFilter.create(Funnels.integerFunnel(), NUMBER, fpp);

        //插入10w样本数据
        for (int i = 0; i <NUMBER ; i++) {
            bf.put(i);
        }
        int count = 0;
        //用另外10w的数据来测试误判率
        for (int i = NUMBER; i < NUMBER + 100000; i++) {
             if(bf.mightContain(i)) {
                 count++;
                 System.out.println(i+"误判");
             }

        }
        System.out.println("总共的误判:"+count);
    }
}

结果:
在这里插入图片描述

误判了1924次,来算一下,1924 / 100000 约等于 0.02 ,嗯。跟想的一样。

这样得出一个结论:
虽然我们可以手动设置误判率,但是要设置合理。误判率越小,那么误判的结果就越少。 但是误判率越小,hash计算的时间就越来越长,所需要的Hash函数也越多,性能也就越差。

标签:bf,误判,元素,布隆,哈希,过滤器
来源: https://blog.csdn.net/weixin_46295656/article/details/115053404

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有