布隆过滤器介绍

判断目标值是否在一个集合中是比较常见的业务场景。在Go语言中通常使用map来实现给功能。但是当集合比较大时,使用map会消耗大量的内存。 这种情况下可使用BitMap来代替map。BitMap虽然能够在一定情况下减少的内存的消耗,但是BitMap也存在以下局限性:

  • 当样本分布极度不均匀的时候,BitMap会造成很大空间上的浪费。
    若数据的类型Int64,并且数据分布的跨度比较大,则也无法满足对内存的要求。
  • 当元素不是整型的时候,BitMap就不适用了。
    BitMap只能保存整形数据,对于字符串类型的数据则不合适使用。

BitMap只能处理整形数据,对于字符串则不能说适用。若能够把字符串映射为整形,就可以使用BitMap来存储字符串的状态了。 hash函数可以将字符串映射为整形数据,但是hash函数映射为整形是存在hash冲突。为了减少hash冲突,可以使用多个hash函数来将一个字符串映射为多个整数,并将映射后的整数存在BitMap中。在查询字符串时,使用同样的hash函数来计算hash值,并使用同样的hash值来查询BitMap,若其中有一个hash值没有命中,则该url不存在。

上述思路就是布隆过滤器的核心思路。布隆过滤器是由布隆(Burton Howard Bloom)在1970年提出的,它实际上是由一个很长的二进制向量和一系列随机映射函数组成,布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率:即布隆过滤器报告某个值存在于BitMap中中,但是实际上该值可能并不在集合中。但是布隆过滤器若报告某个值不在BitMap中,则该值肯定不在集合中。

减少布隆过滤器误识别率的方法
布隆过滤器误识别的原因在于hash冲突,因此减少hash冲突可以降低布隆过滤器误识别率。hash冲突和BitMap数组的大小以及hash函数的个数以及每个hash函数本身的好坏有关。可以采用以下方法降低hash冲突的概率:

  • 多个hash,增大随机性,减少hash碰撞的概率。
  • 扩大数组范围,使hash值均匀分布,进一步减少hash碰撞的概率。

布隆过滤器的Go语言实现

接下来我们给出布隆过滤器的Go语言实现,目前代码已经上传到github中,下载地址

定义

首先给出布隆过滤器结构的定义:

type BloomFilter struct {
    bset *BitMap
    size uint
}

其中:

  • BitMap的实现在bitmap.go文件中
  • size是BitMap二进制位的数量

创建BloomFilter结构

func NewBloomFilter(size_val ...uint) *BloomFilter {
    var size uint = 1024*1024
    if len(size_val) > 0 && size_val[0] > 0 {
        size = size_val[0]
    }

    bf := &BloomFilter{}
    bf.bset = NewBitMap(size)
    bf.size = size
    return bf
}

BloomFilter使用的hash函数

var seeds = []uint{3011, 3017, 3031}
func (bf *BloomFilter)hashFun(seed uint, value string) uint64 {
    hash := uint64(seed)
    for i := 0; i < len(value); i++ {
        hash = hash*33 + uint64(value[i])
    }
    return hash
}

将数据添加到BloomFilter

func (bf *BloomFilter)Set(value string) {
    for _, seed := range seeds {
        hash := bf.hashFun(seed, value)
        hash = hash % uint64(bf.size)
        bf.bset.Set(uint(hash))
    }
}

判断元素是否存在

func (bf *BloomFilter)Check(value string) bool {
    for _, seed := range seeds {
        hash := bf.hashFun(seed, value)
        hash = hash % uint64(bf.size)
        ret := bf.bset.Check(uint(hash))
        if !ret {
            return false
        }
    }
    return true
}

haming
4 声望146 粉丝

WEB开发, 服务器端开发, C/C++, Linux/Unix, Golang