- 布隆过滤器(Bloom Filter)是一种非常巧妙的数据结构,用来判断一个元素是否在一个集合中。它的特点是空间效率极高,但也有一定的误判率。我们来一步步拆解讲清楚它的原理、特点和应用
一句话总结:布隆过滤器是一个基于位数组和多个哈希函数的概率型数据结构,它能快速判断一个元素是否不在集合中,但不能保证判断元素在集合中一定准确(可能会误判)。
布隆过滤器的结构
布隆过滤器主要包含两个核心组成:
- 位数组(Bit Array):长度为 m,初始化时每一位都是 0。
- k 个哈希函数(Hash Functions):每个哈希函数将输入元素映射到 [0, m-1] 范围内的一个整数。
插入元素的过程(add)
举个例子,把字符串 “apple” 加入布隆过滤器:
- 用 k 个哈希函数分别计算 “apple” 的 k 个位置,比如是 [5, 12, 29]。
- 把这几个位置的位(bit)都设置为 1。
即:bit[5] = 1, bit[12] = 1, bit[29] = 1
查询元素的过程(contains / mightContain)
以 “apple” 为例,想判断它是否在集合中:
- 用同样的 k 个哈希函数计算出它对应的 k 个位置。
- 如果这些位置中有任意一个是 0,那说明 “apple” 一定不在。
- 如果这些位置都是 1,可能在(但不保证一定在,可能是别的元素碰巧把这些位置都设置成了 1)。
布隆过滤器的特点
特性 | 说明 |
---|---|
查询速度快 | 哈希+位数组,时间复杂度为 O(k) |
空间效率高 | 相比普通集合结构(如哈希表),占用内存非常少 |
存在误判 | 有可能会误判元素在集合中(假阳性),但不会漏报(不会错过实际存在的元素) |
不可删除元素 | 标准布隆过滤器无法删除元素(否则可能影响其他元素) |
布隆过滤器的应用场景
- 缓存穿透拦截(比如 Redis 缓存系统中先判断 key 是否可能存在)
- 黑名单系统(快速判断用户/IP 是否命中黑名单)
- 爬虫去重(URL 去重)
- 数据库/搜索引擎中判断关键词是否存在于某个倒排索引中
- 区块链、分布式系统中的去重、同步校验等场景
golang 代码
package main
import (
"fmt"
"hash/fnv"
)
// BloomFilter 结构体
type BloomFilter struct {
bitset []bool
size uint
hashFuncs []func(string) uint
}
// 创建新的布隆过滤器
func NewBloomFilter(size uint, hashCount int) *BloomFilter {
bf := &BloomFilter{
bitset: make([]bool, size),
size: size,
hashFuncs: make([]func(string) uint, hashCount),
}
// 初始化多个哈希函数(使用不同的种子)
for i := 0; i < hashCount; i++ {
seed := uint32(i + 1)
bf.hashFuncs[i] = func(data string) uint {
h := fnv.New32a()
h.Write([]byte(fmt.Sprintf("%d%s", seed, data))) // 模拟不同种子
return uint(h.Sum32()) % size
}
}
return bf
}
// 添加元素
func (bf *BloomFilter) Add(item string) {
for _, hashFunc := range bf.hashFuncs {
index := hashFunc(item)
bf.bitset[index] = true
}
}
// 检查元素是否可能存在
func (bf *BloomFilter) MightContain(item string) bool {
for _, hashFunc := range bf.hashFuncs {
index := hashFunc(item)
if !bf.bitset[index] {
return false
}
}
return true
}
// 示例用法
func main() {
bf := NewBloomFilter(1000, 3)
bf.Add("apple")
bf.Add("banana")
fmt.Println("Contains apple:", bf.MightContain("apple")) // true
fmt.Println("Contains banana:", bf.MightContain("banana")) // true
fmt.Println("Contains orange:", bf.MightContain("orange")) // false(大概率)
}
代码说明
- bitset 是一个布尔数组,用来存储位信息。
- 使用 fnv.New32a() 哈希函数,模拟多个哈希函数是通过加不同前缀种子来实现的。
- Add() 方法将多个哈希索引位置设置为 true。
- MightContain() 检查对应的所有位是否都为 true,否则就说明元素一定不在集合中。