脚本之家,脚本语言编程技术及教程分享平台!
分类导航

Python|VBS|Ruby|Lua|perl|VBA|Golang|PowerShell|Erlang|autoit|Dos|bat|shell|

服务器之家 - 脚本之家 - Golang - go-zero源码阅读之布隆过滤器实现代码

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

2023-03-20 12:08飞飞羽毛球 Golang

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

一. 布隆过滤器简介

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

二. 常用场景

1. 解决缓存穿透

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

3. 特定数据识别

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

1. 简介

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

2. 布隆过滤器结构体

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
type (
    // A Filter is a bloom filter.
    // 结构体
    Filter struct {
        bits   uint
        bitSet bitSetProvider
    }
 
    // 位数组接口定义
    bitSetProvider interface {
        check([]uint) (bool, error)
        set([]uint) error
    }
)

3. 初始化方法

?
1
2
3
4
5
6
func New(store *redis.Redis, key string, bits uint) *Filter {
    return &Filter{
        bits:   bits,
        bitSet: newRedisBitSet(store, key, bits),
    }
}

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

4. newRedisBitSet方法

?
1
2
3
4
5
6
7
func newRedisBitSet(store *redis.Redis, key string, bits uint) *redisBitSet {
    return &redisBitSet{
        store: store,
        key:   key,
        bits:  bits,
    }
}

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

5. 数据添加--Add

?
1
2
3
4
5
6
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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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脚本

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

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

8. 数据检测--Exists

?
1
2
3
4
5
6
7
8
9
10
11
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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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脚本

?
1
2
3
4
5
6
7
8
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布隆过滤器内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!

原文链接:https://blog.csdn.net/qq_22323251/article/details/128893869

延伸 · 阅读

精彩推荐
  • GolangGo Gin框架请求自动验证和数据绑定

    Go Gin框架请求自动验证和数据绑定

    今天把使用 Gin 框架开发项目时,经常会用到的请求数据的模型绑定和验证统一梳理了一下,基本上没什么废话都是代码。除了模型绑定和验证,我们还把...

    网管叨bi叨8692022-10-18
  • Golang创建Go工程化项目布局详解

    创建Go工程化项目布局详解

    这篇文章主要介绍了创建Go工程化项目布局详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪...

    范闲7762022-11-23
  • Golang一文带你深入理解Go语言中的sync.Cond

    一文带你深入理解Go语言中的sync.Cond

    sync.Cond 表示的是条件变量,它是一种同步机制,用来协调多个 goroutine 之间的同步。本文将通过示例为大家介绍Go语言中sync.Cond的使用,需要的可以参考一...

    eleven267342023-03-16
  • Golang浅谈Golang 嵌套 interface 的赋值问题

    浅谈Golang 嵌套 interface 的赋值问题

    这篇文章主要介绍了浅谈Golang 嵌套 interface 的赋值问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...

    潘广宇 Leo6342021-06-08
  • Golang使用Go语言时,谨防锁拷贝!

    使用Go语言时,谨防锁拷贝!

    相信大家对 Go 语言的锁拷贝问题并不陌生,那我们应该如何规范使用Go 语言才能规避这个问题呢?一起来看作者是如何处理的。...

    Golang来啦7362021-07-29
  • Golang一篇文章教会你如何使用Go语言Modules

    一篇文章教会你如何使用Go语言Modules

    go moudules是Go的一个包管理工具,官方提供的,还是比较靠谱的,最低Go版本要求1.11+。...

    Go语言进阶学习11482021-09-15
  • Golangaxios gin的GET和POST请求实现示例

    axios gin的GET和POST请求实现示例

    这篇文章主要为大家介绍了axios gin的GET和POST请求实现示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步早日升职加薪...

    Jeff的技术栈7292022-09-20
  • Golanggoalng 结构体 方法集 接口实例详解

    goalng 结构体 方法集 接口实例详解

    这篇文章主要为大家介绍了goalng 结构体 方法集 接口实例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪...

    lambdang10622022-11-20