Bloom Filter 布隆过滤器 | 青训营

98 阅读3分钟

简介

布隆过滤器(Bloom Filter)是一种用于快速判断一个元素是否属于一个集合的概率型数据结构。它通过使用位数组(bit array)和一系列哈希函数来实现。它通常被应用到redis中来防止缓存穿透。一些redis客户端往往实现了默认的布隆过滤器。

基本原理

布隆过滤器的基本原理如下:

  1. 初始化:创建一个包含 m 个位的位数组,所有位都初始化为 0。
  2. 添加元素:当要向布隆过滤器中添加一个元素时,将该元素经过一系列哈希函数的计算,得到多个哈希值。然后将位数组中对应的这些位都设置为 1。
  3. 查询元素:当要查询一个元素是否存在于布隆过滤器中时,将该元素经过相同的一系列哈希函数的计算,得到多个哈希值。然后检查位数组中对应的这些位,如果其中有任何一个位为 0,则可以确定该元素一定不存在于集合中;如果所有位都为 1,则表示该元素可能存在于集合中,但并不能确定一定存在,可能存在一定的误判率。

需要注意的是,布隆过滤器中的哈希函数往往不是一个而是多个,哈希函数越多,误判的概率也会越小,但是通用也会影响其性能。

Golang实现

以下是一个简单的Golang实现的布隆过滤器实例,大概介绍了布隆过滤器的工作原理,与实际应用的布隆过滤器有一定差异:

package main
​
import (
    "fmt"
    "hash/fnv"
    "math"
)
​
type BloomFilter struct {
    bitSet  []bool
    hashers []hasher
}
​
type hasher interface {
    hash(data []byte) uint32
}
​
type fnvHasher struct {
}
​
func (h *fnvHasher) hash(data []byte) uint32 {
    hash := fnv.New32()
    hash.Write(data)
    return hash.Sum32()
}
// size表示位空间大小,numHashers表示哈希函数的个数
func NewBloomFilter(size int, numHashers int) *BloomFilter {
    return &BloomFilter{
        bitSet:  make([]bool, size),
        hashers: generateHashers(numHashers),
    }
}
​
func (bf *BloomFilter) Add(data []byte) {
    for _, h := range bf.hashers {
        index := h.hash(data) % uint32(len(bf.bitSet))
        bf.bitSet[index] = true
    }
}
​
func (bf *BloomFilter) Contains(data []byte) bool {
    for _, h := range bf.hashers {
        index := h.hash(data) % uint32(len(bf.bitSet))
        if !bf.bitSet[index] {
            return false
        }
    }
    return true
}
​
func generateHashers(count int) []hasher {
    hashers := make([]hasher, count)
    for i := 0; i < count; i++ {
        hashers[i] = &fnvHasher{}
    }
    return hashers
}
​
func main() {
    bf := NewBloomFilter(1000, 3)
​
    data1 := []byte("apple")
    data2 := []byte("banana")
    data3 := []byte("orange")
​
    bf.Add(data1)
    bf.Add(data2)
​
    fmt.Println(bf.Contains(data1)) // Output: true
    fmt.Println(bf.Contains(data2)) // Output: true
    fmt.Println(bf.Contains(data3)) // Output: false
}

总结

由于布隆过滤器使用的是位数组和哈希函数,所以它具有高效的插入和查询操作,并且占用的空间相对较小。但是它也存在一定的缺点,即有一定的误判率。当要查询的元素不存在于集合中时,由于哈希函数的碰撞,有可能会导致某些位被错误地设置为 1,从而判断该元素存在于集合中。因此,布隆过滤器适用于那些可以容忍一定误判率的场景,例如缓存、垃圾邮件过滤等。

布隆过滤器同时也很难做删除操作。假设有两个key,分别是key1和key2,他们经过一系列的hash之后得到了相同的hash值,那么如果要删除key1的话,也会将key2也删除掉,这种由哈希碰撞带来的影响同时也会影响到删除操作。

总的来说,布隆过滤器是一种概率型数据结构,用于快速判断一个元素是否属于一个集合。它通过位数组和哈希函数实现高效的插入和查询操作,但存在一定的误判率。