go-zero源码阅读之布隆过滤器实现代码

 更新时间:2023年02月08日 09:30:39   作者:飞飞羽毛球  
布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都比一般的算法要好的多,缺点是有一定的误识别率和删除困难,这篇文章主要介绍了go-zero源码阅读-布隆过滤器,需要的朋友可以参考下

一. 布隆过滤器简介

布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都比一般的算法要好的多,缺点是有一定的误识别率和删除困难。

二. 常用场景

1. 解决缓存穿透

2. 数据去重,如用户是否发送过短信

3. 特定数据识别

三. go-zero的布隆过滤器实现

1. 简介

依赖redis.bitmap, 将数据多次hash后,插入到多个特定位,并设置为1。当进行数据检测时,经过相同hash后,检测所有位,只要其中一位为0,则代表数据不存在,否则数据可能存在。

2. 布隆过滤器结构体

type (
	// A Filter is a bloom filter.
    // 结构体
	Filter struct {
		bits   uint
		bitSet bitSetProvider
	}
 
    // 位数组接口定义 
	bitSetProvider interface {
		check([]uint) (bool, error)
		set([]uint) error
	}
)

3. 初始化方法

func New(store *redis.Redis, key string, bits uint) *Filter {
	return &Filter{
		bits:   bits,
		bitSet: newRedisBitSet(store, key, bits),
	}
}

初始化方法比较简单,具体操作依赖newRedisBitSet

4. newRedisBitSet方法

func newRedisBitSet(store *redis.Redis, key string, bits uint) *redisBitSet {
	return &redisBitSet{
		store: store,
		key:   key,
		bits:  bits,
	}
}

简单的初始化, 初始化结束

5. 数据添加--Add

func (f *Filter) Add(data []byte) error {
    // 获取数据多次hash后的各key
	locations := f.getLocations(data)
    // 插入数据
	return f.bitSet.set(locations)
}

首先获取hash后的key的切片,然后调用set方法,将数据插入位数组(redis.bitmap)

6. 数据添加--set

func (r *redisBitSet) set(offsets []uint) error {
    // 将[]uint转为[]string
	args, err := r.buildOffsetArgs(offsets)
	if err != nil {
		return err
	}
    // 执行lua脚本
	_, err = r.store.Eval(setScript, []string{r.key}, args)
	if err == redis.Nil {
		return nil
	}
 
	return err
}

首先将[]uint转为[]string, 因为redis lua需要[]string,然后执行lua脚本进行数据插入,使用lua是为了保证原子性

7. 数据添加--lua脚本

setScript = `
for _, offset in ipairs(ARGV) do
	redis.call("setbit", KEYS[1], offset, 1)
end
`

for循环获取到每个偏移量,使用setbit命令设置各偏移量为1

8. 数据检测--Exists

func (f *Filter) Exists(data []byte) (bool, error) {
    // 同数据set一致,获取数据多次hash后,偏移量切片
	locations := f.getLocations(data)
    // 调用check方法进行检测
	isSet, err := f.bitSet.check(locations)
	if err != nil {
		return false, err
	}
 
	return isSet, nil
}

首先调用getLocations方法获取数据多次hash后偏移量切片,调用check方法进行数据检测

9. 数据检测--check

func (r *redisBitSet) check(offsets []uint) (bool, error) {
    // []uint转为[]string,和set调用的一致
	args, err := r.buildOffsetArgs(offsets)
	if err != nil {
		return false, err
	}
 
    //执行lua脚本,检测各偏移量数据是否都存在 
	resp, err := r.store.Eval(testScript, []string{r.key}, args)
    // 根据返回值判断数据是否存在
   // key不存在特殊处理
	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
}

执行lua脚本判断数据是否存在,根据返回值返回数据是否存在

10. 数据检测--lua脚本

testScript = `
for _, offset in ipairs(ARGV) do
	if tonumber(redis.call("getbit", KEYS[1], offset)) == 0 then
		return false
	end
end
return true
`

fou循环判断各偏移量是否存在,只要有一个为0,就代表数据不存在,各offset都为1则代表数据存在

到此这篇关于go-zero源码阅读-布隆过滤器的文章就介绍到这了,更多相关go-zero布隆过滤器内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 浅析go语言如何实现协程的抢占式调度的

    浅析go语言如何实现协程的抢占式调度的

    go语言通过GMP模型实现协程并发,为了避免单协程持续持有线程导致线程队列中的其他协程饥饿问题,设计者提出了一个抢占式调度机制,本文会基于一个简单的代码示例对抢占式调度过程进行深入讲解剖析
    2024-04-04
  • golang时间及时间戳的获取转换

    golang时间及时间戳的获取转换

    本文主要介绍了golang时间及时间戳的获取转换,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-06-06
  • Go语言strconv包实现字符串和数值类型的相互转换

    Go语言strconv包实现字符串和数值类型的相互转换

    这篇文章主要介绍了Go语言strconv包实现字符串和数值类型的相互转换,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-03-03
  • 深入讲解Go语言中函数new与make的使用和区别

    深入讲解Go语言中函数new与make的使用和区别

    大家都知道Go语言中的函数new与函数make一直是新手比较容易混淆的东西,看着相似,但其实不同,不过解释两者之间的不同也非常容易,下面这篇文章主要给大家介绍了关于Go语言中函数new与make区别的相关资料,需要的朋友可以参考下。
    2017-10-10
  • Go中的新增对模糊测试的支持

    Go中的新增对模糊测试的支持

    这篇文章主要为大家介绍了Go中的新增对模糊测试的支持,文中还包含了一些功能实验性测试分析有需要的朋友可以借鉴参考下,希望能够有所帮助
    2022-03-03
  • Go语言context上下文管理的使用

    Go语言context上下文管理的使用

    本文主要介绍了Go语言context上下文管理的使用,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-03-03
  • go-micro微服务JWT跨域认证问题

    go-micro微服务JWT跨域认证问题

    JWT 以 JSON 对象的形式安全传递信息。因为存在数字签名,因此所传递的信息是安全的,这篇文章主要介绍了go-micro微服务JWT跨域认证,需要的朋友可以参考下
    2023-01-01
  • Go语言实现单端口转发到多个端口

    Go语言实现单端口转发到多个端口

    这篇文章主要为大家详细介绍了Go语言实现单端口转发到多个端口,文中的示例代码讲解详细,具有一定的参考价值,对大家的学习或工作有一定的帮助,需要的小伙伴可以了解下
    2024-02-02
  • go 生成器模式的具体使用

    go 生成器模式的具体使用

    生成器是一种创建型设计模式,使你能够分步骤创建复杂对象,本文主要介绍了go生成器模式的具体使用,具有一定的参考价值,感兴趣的可以了解一下
    2024-01-01
  • Golang 错误捕获Panic与Recover的使用

    Golang 错误捕获Panic与Recover的使用

    对于Go语言的错误是通过返回值的方式,本文主要介绍了Golang 错误捕获Panic与Recover的使用,文中根据实例编码详细介绍的十分详尽,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-03-03

最新评论