为什么 Redis 和搜索引擎都在用布隆过滤器?

Scroll Down

3W 学习法:布隆过滤器(Bloom Filter)

1. What:什么是布隆过滤器?

布隆过滤器(Bloom Filter)是一种概率型数据结构,用于判断某个元素是否存在于集合中。它比传统的数据结构(如哈希表、集合)更节省空间,但存在误判率——可能会误认为某个不存在的元素存在,但不会漏判已存在的元素。

布隆过滤器的核心特点:

  • 高效存储:使用位数组(bit array)存储元素信息,占用空间远小于哈希表。
  • 快速判断:使用多个哈希函数计算元素位置,插入和查询的时间复杂度为 O(k)(k 是哈希函数个数)。
  • 允许误判:可能错误地判断一个元素存在,但不会漏掉真实存在的元素。

2. Why:为什么要使用布隆过滤器?

布隆过滤器的核心优势在于节省存储空间快速查询,特别适用于大规模数据的集合判定,如:

  • 黑名单检查(如垃圾邮件过滤、恶意 IP 检测)
  • 缓存穿透防护(避免数据库频繁查询不存在的数据)
  • 去重检测(如搜索引擎的 URL 去重、区块链的 UTXO 过滤)
  • 数据库查询优化(如 HBase、Redis 等存储系统)

对比哈希表:

方案 空间占用 查询速度 误判可能性
哈希表
布隆过滤器 可能误判

3. How:布隆过滤器的工作原理

(1) 数据存储

  1. 初始化一个位数组(bit array),所有位初始为 0
  2. 使用 k 个哈希函数 计算元素的 k 个哈希值,并将对应的位设为 1

(2) 数据查询

  1. k 个哈希函数 计算查询元素的 k 个哈希值。
  2. 检查对应的 k 个位是否都是 1
    • 都是 1:可能存在(有误判可能)。
    • 存在 0:一定不存在(不会漏判)。

(3) 误判原因
由于不同元素可能映射到相同的位置,导致误判。因此,哈希函数的选取和位数组的大小都会影响误判率。


代码实战:使用 Java 实现布隆过滤器

下面是一个简单的 Java 实现,使用 BitSetMurmurHash3 作为哈希函数。

import java.util.BitSet;
import java.util.Random;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;

public class BloomFilter {
    private BitSet bitSet;
    private int bitSize;
    private int hashFunctions;
    private Random random;

    public BloomFilter(int bitSize, int hashFunctions) {
        this.bitSize = bitSize;
        this.hashFunctions = hashFunctions;
        this.bitSet = new BitSet(bitSize);
        this.random = new Random();
    }

    private int[] hash(String data) {
        int[] hashes = new int[hashFunctions];
        byte[] bytes = data.getBytes(StandardCharsets.UTF_8);
        for (int i = 0; i < hashFunctions; i++) {
            hashes[i] = Math.abs(Arrays.hashCode(bytes) ^ random.nextInt()) % bitSize;
        }
        return hashes;
    }

    public void add(String data) {
        int[] hashes = hash(data);
        for (int h : hashes) {
            bitSet.set(h);
        }
    }

    public boolean mightContain(String data) {
        int[] hashes = hash(data);
        for (int h : hashes) {
            if (!bitSet.get(h)) {
                return false;
            }
        }
        return true;
    }

    public static void main(String[] args) {
        BloomFilter filter = new BloomFilter(1000, 3);
        filter.add("hello");
        filter.add("world");

        System.out.println(filter.mightContain("hello")); // true
        System.out.println(filter.mightContain("world")); // true
        System.out.println(filter.mightContain("java"));  // false 或 误判 true
    }
}

布隆过滤器的应用场景

  1. 搜索引擎

    • 去重 URL:搜索引擎抓取网页时,需要避免重复抓取相同网页。
    • 关键词过滤:快速判断某个词是否在索引库中。
  2. 缓存系统(Redis + 布隆过滤器)

    • 解决 缓存穿透 问题,避免数据库被高频访问的无效请求击穿。
  3. 区块链 & 分布式存储

    • 比特币等区块链系统使用布隆过滤器减少存储和查询的开销。
  4. 推荐系统 & 广告系统

    • 防止重复推荐相同广告给用户,提高用户体验。

总结

维度 传统哈希表 布隆过滤器
存储占用
查询时间 O(1) O(k)
误判 可能误判
漏判

布隆过滤器是一种高效的集合判断工具,在搜索引擎、缓存优化、网络安全等领域被广泛应用。结合 3W 学习法,我们理解了它的原理、应用场景,并通过 Java 代码实践了其核心逻辑。希望这篇文章能帮助你掌握布隆过滤器的核心概念! 🚀