Skip to content
HeZzz
Go back

布隆过滤器

在处理海量数据时,我们常常面临一个核心问题:如何高效判断某个数据是否存在于集合中? 这个问题在缓存穿透、数据去重等场景中尤为常见。布隆过滤器(Bloom Filter)正是为了解决这类问题而设计的一种概率型数据结构。它以极低的空间复杂度和时间复杂度,提供了高效的解决方案,尽管其结果存在一定的误差率。

本文将从布隆过滤器的原理、使用场景到实战实现(包括 Java 手动实现、Guava 库和 Redis 实现)进行全面解析,帮助你快速掌握这一实用工具。


布隆过滤器是什么?

核心概念

布隆过滤器由 Burton Howard Bloom 于 1970 年提出,是一种基于 位数组(bit array)多个哈希函数 的概率型数据结构。它的核心思想是通过哈希函数将元素映射到位数组中的多个位置,并将这些位置标记为 1。当判断元素是否存在时,只需检查所有对应的位是否为 1

优点与局限


布隆过滤器的工作原理

数据结构组成

布隆过滤器由两个核心部分组成:

  1. 位数组(Bitset):初始时所有位均为 0
  2. 哈希函数集合:通常使用多个不同的哈希函数(如 6 个)。

插入与查询流程

插入元素

  1. 使用多个哈希函数对元素进行计算,得到一组哈希值。
  2. 将位数组中对应位置的位设置为 1

查询元素

  1. 对元素进行相同的哈希计算,得到哈希值。
  2. 检查所有哈希值对应的位是否为 1
    • 如果全部为 1,则认为元素可能存在(可能误判)。
    • 如果存在任意一个为 0,则元素一定不存在。

误判率分析

布隆过滤器的误判率与以下因素相关:

公式估算误判率:
P(1eknm)kP \approx \left(1 - e^{-\frac{kn}{m}}\right)^k


布隆过滤器的典型应用场景

存在性检测

数据去重


布隆过滤器的实战实现

Java 手动实现

import java.util.BitSet;

public class MyBloomFilter {
    private static final int DEFAULT_SIZE = 2 << 24; // 位数组大小
    private static final int[] SEEDS = {3, 13, 46, 71, 91, 134}; // 哈希种子

    private BitSet bits = new BitSet(DEFAULT_SIZE);
    private SimpleHash[] func = new SimpleHash[SEEDS.length];

    public MyBloomFilter() {
        for (int i = 0; i < SEEDS.length; i++) {
            func[i] = new SimpleHash(DEFAULT_SIZE, SEEDS[i]);
        }
    }

    public void add(Object value) {
        for (SimpleHash f : func) {
            bits.set(f.hash(value), true);
        }
    }

    public boolean contains(Object value) {
        if (value == null) return false;
        boolean ret = true;
        for (SimpleHash f : func) {
            ret = ret && bits.get(f.hash(value));
        }
        return ret;
    }

    static class SimpleHash {
        private int cap;
        private int seed;

        public SimpleHash(int cap, int seed) {
            this.cap = cap;
            this.seed = seed;
        }

        public int hash(Object value) {
            int h = value.hashCode();
            return Math.abs((cap - 1) & (seed * ((h ^ (h >>> 16)))));
        }
    }
}

测试代码

MyBloomFilter filter = new MyBloomFilter();
System.out.println(filter.contains("https://hez2z.github.io/")); // false
filter.add("https://hez2z.github.io/");
System.out.println(filter.contains("https://hez2z.github.io/")); // true

使用 Guava 库实现

Guava 提供了开箱即用的布隆过滤器实现,适合单机场景。

Maven 依赖

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>31.1-jre</version>
</dependency>

代码示例:

BloomFilter<Integer> filter = BloomFilter.create(
    Funnels.integerFunnel(),
    1500,
    0.01); // 容量 1500,误判率 1%

filter.put(123);
System.out.println(filter.mightContain(123)); // true
System.out.println(filter.mightContain(456)); // false
```xml
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>31.1-jre</version>
</dependency>

代码示例:

BloomFilter<Integer> filter = BloomFilter.create(
    Funnels.integerFunnel(),
    1500,
    0.01); // 容量 1500,误判率 1%

filter.put(123);
System.out.println(filter.mightContain(123)); // true
System.out.println(filter.mightContain(456)); // false
```xml
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>31.1-jre</version>
</dependency>

代码示例

BloomFilter<Integer> filter = BloomFilter.create(
    Funnels.integerFunnel(), 
    1500, 
    0.01); // 容量 1500,误判率 1%

filter.put(123);
System.out.println(filter.mightContain(123)); // true
System.out.println(filter.mightContain(456)); // false

Redis 分布式布隆过滤器

Redis 通过 RedisBloom 模块 支持分布式布隆过滤器,适用于高并发、分布式场景。

安装 RedisBloom

docker run -p 6379:6379 --name redis-redisbloom redislabs/rebloom:latest

常用命令

# 初始化布隆过滤器(容量 10000,误判率 0.001)
BF.RESERVE myFilter 0.001 10000

# 添加元素
BF.ADD myFilter "user123"

# 查询元素是否存在
BF.EXISTS myFilter "user123"  # 1(存在)
BF.EXISTS myFilter "user456"  # 0(不存在)

布隆过滤器的优化与注意事项

降低误判率

可删除场景的解决方案

标准布隆过滤器不支持删除,但可通过以下方式改进:


Share this post on:

上一篇
Knife4jAndSpringDoc
下一篇
关于OpenWrt路由器及哆点Drcom校园网WEB自动登录