布隆过滤器(BloomFilter)——简介(一)

布隆过滤器(BloomFilter)——简介(一)

1970年布隆提出了BloomFilter,它被用作对数据进行初级的过滤,降低过滤数据的成本。BloomFilter就像粗糙的大筛子一样,使用它过滤细沙,先将所有的沙石倒进大筛子,能够初步将太大的沙石过滤掉,但是不能保证所有留下的就是我们想要的细沙(因为粗糙的筛子的孔大小不一,同时制作成本较低,即使制作一个能够过滤大量沙石的大筛子,也能接受)。接着再用一个精细的小筛子来对剩下的沙石进行过滤,就能够过滤出所有符合大小限制的细沙。(精细化的筛子制作成本高,但是因为足够小,所以其成本是能够接受的)。这就是一个关于布隆过滤器的产生的描述。

1. 基本概念

  • 描述
    BloomFilter是一种概率型数据结构,利用概率学进行设计的数据结构 。我们先可以直接理解为它是一个"集合"(当然这不是真的^_^,后面会再写一篇说明它是如何设计的) ,可以高效地插入和查询数据。它主要的功能是用来判断BloomFilter这个"集合"中是否存在某个元素,不过它只能告诉你“这个元素在BloomFilter中一定不存在或者可能存在”,也就是说BloomFilter可能会错误的认为某个不存在其中的元素存在其中,但是BloomFilter认为不存在的元素那就一定不存在其中
    BloomFilter 不存在 存在
    实际 不存在 不存在 存在
  • 特点
    • 基于概率学的数据结构
    • 用来判断集合中是否存在某个元素,但是有一定的误判率(非常低,可自定),常用作超大数据的初级过滤
    • 插入和查询的效率都是常量级别(查询只能看元素是否存在)
    • 无法删除元素
    • 内存占用低(内存占用比HashSet少很多,占用大小的计算可参考bloom-calculator

2. BloomFilter和HashSet的性能比较

  • 依次修改数据量大小,记录不同容量的查询效率(每次查询1000000次)

    判断器 10万 100万 500万 1000万 2000万 3000万 5000万 1亿 2亿 5亿
    BloomFilter 285ms 288ms 186ms 182ms 197ms 198ms 188ms 189ms 191ms 173ms
    HashSet 22ms 30ms 21ms 17ms 23ms - - - - -
  • 依次修改插入的数据量,记录每次的插入时间

    判断器 10万 100万 500万 1000万 2000万 3000万 5000万 1亿 2亿 5亿
    BloomFilter 96ms 518ms 2807ms 5888ms 13147ms 20713ms 35675ms 73075ms 152333ms 380256ms
    HashSet 21ms 121ms 2971ms 6632ms 22494ms 内存溢出 内存溢出 内存溢出 内存溢出 内存溢出

    注: 在我的PC上测试时,此处BloomFilter的容错率设定为1%

  • 分析
    可以看出:查询方面,随着容器数据量的增大,HashSet的查询效率几乎不变,BloomFilter的查询效率会逐步提高趋于稳定;插入方面,随着插入数据量的增加,刚开始HashSet比BloomFilter快,到后面被BloomFilter超越,直接被远远甩在后面,甚至因为占用内存空间太大的问题,导致内存溢出。

3. BloomFilter使用示例

  • 导包 pom.xml
<!-- 这里选择的是谷歌guava提供的BloomFilter,使用maven导入依赖 -->
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>28.0-jre</version>
</dependency>
  • 使用示例
import com.google.common.base.Charsets;
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnel;
import com.google.common.hash.PrimitiveSink;

/**
 * Description: 布隆过滤器示例
 *
 * @author ALion
 * @version 2019/8/17 1:52
 */
public class BloomFilterDemo {

    public static void main(String[] args) {
        // Funnel用于告诉BloomFilter需要根据Person的哪些字段来计算其信息指纹
        Funnel<Person> personFunnel = new Funnel<Person>() {
            @Override
            public void funnel(Person person, PrimitiveSink into) {
                into.putInt(person.id)
                    .putString(person.firstName, Charsets.UTF_8);
            }
        };

        int count = 100 * 10000;
        
        // arg1: Funnel
        // arg2: 指定你的BloomFilter要容纳多少条数据
        // arg3: 指定创建的BloomFilter的错误率
        BloomFilter<Person> friends = BloomFilter.create(personFunnel, count, 0.01);

        // 向BloomFilter中加入数据
        for (int i = 0; i < count; i++) {
            friends.put(new Person(i, "jack" + i));
        }

        // 测试效果
        Person person1 = new Person(10, "jack");
        boolean exist1 = friends.mightContain(person1);
        System.out.println("person1: exist = " + exist1);

        Person person2 = new Person(10, "jack10");
        boolean exist2 = friends.mightContain(person2);
        System.out.println("person2: exist = " + exist2);

    }

    static class Person {

        int id;

        String firstName;

        public Person(int id, String firstName) {
            this.id = id;
            this.firstName = firstName;
        }

    }

}
发布了128 篇原创文章 · 获赞 45 · 访问量 15万+

猜你喜欢

转载自blog.csdn.net/alionsss/article/details/99687080