布隆过滤器原理与golang实现

本文介绍了布隆过滤器的工作原理、优点和缺点,它是一种空间效率极高的概率型数据结构,用于判断一个元素是否可能在一个集合中。在Redis中,可以通过位图操作实现布隆过滤器,利用Lua脚本保证操作的原子性。文章还展示了如何在Go语言中创建一个基于Redis的布隆过滤器实现,包括设置和检查元素的存在性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一 什么是布隆过滤器

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
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值