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布隆过滤器内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Golang语言中fs.ReadDir的全面指南

    Golang语言中fs.ReadDir的全面指南

    欢迎来到Golang语言中fs.ReadDir的全面指南!这个简短的引导将让你掌握如何高效地使用这个功能,一起来深入探索吧,保证你会有所收获!
    2023-12-12
  • 浅谈Go切片的值修改是否会覆盖数组的值 

    浅谈Go切片的值修改是否会覆盖数组的值 

    本文主要介绍了浅谈Go切片的值修改是否会覆盖数组的值,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下 
    2022-02-02
  • 使用Go语言发送邮件的示例代码

    使用Go语言发送邮件的示例代码

    很多朋友想试试用Go语言发送邮件,所以接下来小编给大家介绍一下如何用Go语言发送邮件,文中通过代码实例讲解的非常详细,需要的朋友可以参考下
    2023-07-07
  • Go语言 channel如何实现归并排序中的merge函数详解

    Go语言 channel如何实现归并排序中的merge函数详解

    这篇文章主要给大家介绍了关于Go语言 channel如何实现归并排序中merge函数的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧。
    2018-02-02
  • 我放弃Python转Go语言的9大理由(附优秀书籍推荐)

    我放弃Python转Go语言的9大理由(附优秀书籍推荐)

    这篇文章主要给大家介绍了关于我放弃Python转Go语言的9大理由,以及给大家推荐了6本优秀的go语言书籍,对同样想学习golang的朋友们具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧。
    2017-10-10
  • 详解Go多协程并发环境下的错误处理

    详解Go多协程并发环境下的错误处理

    这篇文章主要介绍了详解Go多协程并发环境下的错误处理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-08-08
  • Golang熔断器的开发过程详解

    Golang熔断器的开发过程详解

    Golang熔断器是一种用于处理分布式系统中服务调用的故障保护机制,它可以防止故障服务的连锁反应,提高系统的稳定性和可靠性,本文将给大家详细的介绍一下Golang熔断器的开发过程,需要的朋友可以参考下
    2023-09-09
  • Go整合captcha实现验证码功能

    Go整合captcha实现验证码功能

    最近在使用Go语言搞一个用户登录&注册的功能,我们油然会产生一种增加验证码的想法。后来在GitHub上找到了这个名叫captcha的插件,于是就利用文档进行了初步的学习,并融入到自己的项目中,整个过程下来感觉这个插件的设计非常巧妙
    2023-03-03
  • Go语言入门学习之Channel通道详解

    Go语言入门学习之Channel通道详解

    go routine可以使用channel来进行通信,使用通信的手段来共享内存,下面这篇文章主要给大家介绍了关于Go语言入门学习之Channel通道的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考下
    2022-07-07
  • Go语言使用defer+recover解决panic导致程序崩溃的问题

    Go语言使用defer+recover解决panic导致程序崩溃的问题

    如果协程出现了panic,就会造成程序的崩溃,这时可以在goroutine中使用recover来捕获panic,进行处理,本文就详细的介绍一下,感兴趣的可以了解一下
    2021-09-09

最新评论