布隆过滤器原理与golang实现

12 篇文章 1 订阅

一 什么是布隆过滤器

Bloom Filters - the mathBloom Filters - the mathhttps://pages.cs.wisc.edu/~cao/papers/summary-cache/node8.html
维基百科https://zh.m.wikipedia.org/zh/%E5%B8%83%E9%9A%86%E8%BF%87%E6%BB%A4%E5%99%A8布隆过滤器实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中

1.工作原理

当一个元素被加入集合时,通过 K 个散列函数将这个元素映射成一个位数组中的 K 个点位(offset),把它们置为 1。
判断是否存在时,只要看这些点是不是都是 1。
如果这些点有任何一个 0,则被检元素一定不在;
如果都是 1,则被检元素很可能。这就是布隆过滤器的基本思想。


2.优点

  • 空间占用小,相比于其它的数据结构,布隆过滤器在空间和时间方面都有巨大的优势。
  • 布隆过滤器存储空间和插入/查询时间都是常数 O(k) 。
  • 散列函数相互之间没有关系,方便由硬件并行实现。
  • 布隆过滤器不需要存储元素本身,在某些对保密要求非常严格的场合有优势。


3.缺点

  • 随着存入的元素数量增加,误算率随之增加
    例如元素a hash后的bit点位是 1,2,3; 元素b hash后bit点位也是1,2,3 ,然而b并不存在于布隆表中。
  • 元素无法删除

4.误判率推导

m 是该位数组的大小,k 是 Hash 函数的个数。

//todo

二 基于redis bitmap实现布隆过滤器


1.lua实现位bitmap操作, 保证整个操作的原子性

设置bloom 位

--ARGV:偏移量offset数组
--KYES[1]: setbit操作的key
--全部设置为1
for _, offset in ipairs(ARGV) do
	redis.call("setbit", KEYS[1], offset, 1)
end

读取

--ARGV:偏移量offset数组
--KYES[1]: getbit操作的key
--检查是否全部为1 ,全部为1 说明已经存在了
for _, offset in ipairs(ARGV) do
	if tonumber(redis.call("getbit", KEYS[1], offset)) == 0 then
		return false
	end
end
return true

2.代码实现

type redisBitSet struct {
	store *redis.Redis
	key   string //redis key
	bits  uint //bloom表长度
}


//检查bit位 是否是1 ,元素是否已经存在
func (r *redisBitSet) check(offsets []uint) (bool, error) {
	args, err := r.buildOffsetArgs(offsets)
	if err != nil {
		return false, err
	}

	resp, err := r.store.Eval(testScript, []string{r.key}, args)
	if err == redis.Nil {
		return false, nil
	} else if err != nil {
		return false, err
	}

	exists, ok := resp.(int64)
	if !ok {
		return false, nil
	}

	return exists == 1, nil
}


//设置元素已经存在
func (r *redisBitSet) set(offsets []uint) error {
	args, err := r.buildOffsetArgs(offsets)
	if err != nil {
		return err
	}

	_, err = r.store.Eval(setScript, []string{r.key}, args)
	if err == redis.Nil {
		return nil
	}

	return err
}



func (r *redisBitSet) buildOffsetArgs(offsets []uint) ([]string, error) {
	var args []string

	for _, offset := range offsets {
		if offset >= r.bits {
			return nil, ErrTooLargeOffset
		}

		args = append(args, strconv.FormatUint(uint64(offset), 10))
	}

	return args, nil
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是用Golang实现布隆过滤器的示例代码: ```go package main import ( "bufio" "fmt" "hash/fnv" "os" ) type BloomFilter struct { bitset []bool k uint64 } func NewBloomFilter(m uint64, k uint64) *BloomFilter { return &BloomFilter{make([]bool, m), k} } func (bf *BloomFilter) Add(s string) { for i := uint64(0); i < bf.k; i++ { h := fnv.New64a() h.Write([]byte(s)) index := h.Sum64() % uint64(len(bf.bitset)) bf.bitset[index] = true } } func (bf *BloomFilter) Contains(s string) bool { for i := uint64(0); i < bf.k; i++ { h := fnv.New64a() h.Write([]byte(s)) index := h.Sum64() % uint64(len(bf.bitset)) if !bf.bitset[index] { return false } } return true } func main() { bf := NewBloomFilter(10000, 3) // Add some strings to the Bloom filter bf.Add("apple") bf.Add("banana") bf.Add("orange") // Check if some strings are in the Bloom filter fmt.Println(bf.Contains("apple")) // true fmt.Println(bf.Contains("banana")) // true fmt.Println(bf.Contains("orange")) // true fmt.Println(bf.Contains("grape")) // false fmt.Println(bf.Contains("peach")) // false fmt.Println(bf.Contains("pineapple")) // false // Read some strings from a file and check if they are in the Bloom filter file, _ := os.Open("words.txt") defer file.Close() scanner := bufio.NewScanner(file) for scanner.Scan() { word := scanner.Text() if bf.Contains(word) { fmt.Printf("%s may be in the set\n", word) } } } ``` 以上是一个简单的布隆过滤器实现,它使用了FNV-1a哈希算法来计算字符串的哈希值,并将哈希值映射到一个布隆过滤器的位数组中,如果位数组中的所有位都被置位了,那么就认为这个字符串可能在集合中。注意,这个实现中的位数组大小是固定的,如果集合中的元素数量超过了位数组的大小,那么误判率就会增加。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值