修正了布隆过滤器和位图中出现的由位运算导致的溢出问题

This commit is contained in:
hlccd 2021-12-28 22:47:34 +08:00 committed by GitHub
parent 44b92350cb
commit f64e02a719
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 87 additions and 42 deletions

View File

@ -1,5 +1,15 @@
package singleFlight
//@Title singleFlight
//@Description
// 单次请求-single flight
// 利用可重入锁避免对于一个同类的请求进行多次从而导致的缓存击穿的问题
// 缓存击穿:
// 缓存在某个时间点过期的时候
// 恰好在这个时间点对这个Key有大量的并发请求过来
// 这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存
// 这个时候大并发的请求可能会瞬间把后端DB压垮。
import "sync"
//呼叫请求结构体
@ -9,61 +19,88 @@ type call struct {
err error //错误反馈
}
//一组请求工作
//每一类请求对应一个call,利用其内部的可重入锁避免一类请求在短时间内频繁执行
//请求组工作由使用者自行分配空间来实现
type Group struct {
mu sync.Mutex // protects m
m map[string]*call//所有请求
m map[string]*call //一类请求与同一类呼叫的映射表
mu sync.Mutex //并发控制锁,保证线程安全
}
//防止击穿缓存,对同一个key进行请求时需要分别进行,利用可重入锁实现
func (g *Group) Do(key string, fn func() (interface{}, error)) (interface{}, error) {
//@title Do
//@description
// 以请求做做接收者
// 传入一个请求类的key和请求调用函数fn
// 请求时候需要等待之前有的同类请求先完成在进行
// 防止击穿缓存,对同一个key进行请求时需要分别进行,利用可重入锁实现
// 请求完成后返回结果和错误信息即可
//@receiver g *Group 接受者请求组的指针
//@param key string 请求的关键词key
//@param fn func() (interface{}, error) 请求执行函数
//@return v interface{} 请求执行得到的结果
//@return err error 执行请求后的错误信息
func (g *Group) Do(key string, fn func() (interface{}, error)) (v interface{}, err error) {
g.mu.Lock()
if g.m == nil {
g.m = make(map[string]*call)
}
//判断以key为关键词的该类请求是否存在
if c, ok := g.m[key]; ok {
g.mu.Unlock()
c.wg.Wait() // 如果请求正在进行中,则等待
return c.val, c.err // 请求结束,返回结果
// 如果请求正在进行中,则等待
c.wg.Wait()
return c.val, c.err
}
//该类请求不存在,创建个请求
c := new(call)
c.wg.Add(1) // 发起请求前加锁
g.m[key] = c // 添加到 g.m表明 key 已经有对应的请求在处理
// 发起请求前加锁,并将请求添加到请求组内以表示该类请求正在处理
c.wg.Add(1)
g.m[key] = c
g.mu.Unlock()
c.val, c.err = fn() // 调用 fn发起请求
c.wg.Done() // 请求结束
//调用请求函数获取内容
c.val, c.err = fn()
//请求结束
c.wg.Done()
g.mu.Lock()
delete(g.m, key) // 更新 g.m
//从请求组中删除该呼叫请求
delete(g.m, key)
g.mu.Unlock()
return c.val, c.err // 返回结果
return c.val, c.err
}
//@title hash
//@description
// 以请求做做接收者
// 传入一个请求类的key和请求调用函数fn
// 请求时候需要等待之前有的同类请求先完成在进行
// 防止击穿缓存,对同一个key进行请求时需要分别进行,利用可重入锁实现
// 返回一个channel,利用fn函数获取到的数据将会传入其中
// 可利用channel做超时控制
//@receiver g *Group 接受者请求组的指针
//@param key string 请求的关键词key
//@param fn func() (interface{}, error) 请求执行函数
//@return ch chan interface{} 执行结果将会传入其中,可做超时控制
func (g *Group) DoChan(key string, fn func() (interface{}, error)) (ch chan interface{}) {
ch = make(chan interface{})
ch = make(chan interface{}, 1)
g.mu.Lock()
if g.m == nil {
g.m = make(map[string]*call)
}
if c, ok := g.m[key]; ok {
if _, ok := g.m[key]; ok {
g.mu.Unlock()
c.wg.Wait() // 如果请求正在进行中,则等待
ch <- c.val
return ch
}
c := new(call)
c.wg.Add(1) // 发起请求前加锁
g.m[key] = c // 添加到 g.m表明 key 已经有对应的请求在处理
g.mu.Unlock()
c.val, c.err = fn() // 调用 fn发起请求
c.wg.Done() // 请求结束
ch <- c.val
g.mu.Lock()
delete(g.m, key) // 更新 g.m
g.mu.Unlock()
go func() {
c.val, c.err = fn() // 调用 fn发起请求
c.wg.Done() // 请求结束
g.mu.Lock()
delete(g.m, key) // 更新 g.m
ch <- c.val
g.mu.Unlock()
}()
return ch
}

View File

@ -138,7 +138,7 @@ func (bm *Bitmap) Check(num uint) (b bool) {
return false
}
//判断第num是否为1,为1返回true,否则为false
if bm.bits[num/64]&(1<<num%64) > 0 {
if bm.bits[num/64]&(1<<(num%64)) > 0 {
return true
}
return false

View File

@ -15,6 +15,7 @@ import "fmt"
//选用uint64是为了更多的利用bit位
type bloomFilter struct {
bits []uint64
hash Hash
}
//bloomFilter布隆过滤器接口
@ -26,6 +27,9 @@ type bloomFilteror interface {
Clear() //清空该布隆过滤器
}
//允许自行传入hash函数
type Hash func(v interface{}) (h uint32)
//@title hash
//@description
// 传入一个虚拟节点id和实际结点
@ -33,13 +37,13 @@ type bloomFilteror interface {
// 逐层访问并利用素数131计算其hash值随后返回
//@receiver nil
//@param v interface{} 待计算的值
//@return h uint64 计算得到的hash值
func hash(v interface{}) (h uint64) {
h = uint64(0)
s := fmt.Sprintf("v", v)
//@return h uint32 计算得到的hash值
func hash(v interface{}) (h uint32) {
h = uint32(0)
s := fmt.Sprintf("131-%v-%v", v,v)
bs := []byte(s)
for i := range bs {
h += uint64(bs[i]) * 131
h += uint32(bs[i]) * 131
}
return h
}
@ -49,11 +53,15 @@ func hash(v interface{}) (h uint64) {
// 新建一个bloomFilter布隆过滤器容器并返回
// 初始bloomFilter的切片数组为空
//@receiver nil
//@param nil
//@param h Hash hash函数
//@return bf *bloomFilter 新建的bloomFilter指针
func New() (bf *bloomFilter) {
func New(h Hash) (bf *bloomFilter) {
if h == nil {
h = hash
}
return &bloomFilter{
bits: make([]uint64, 0, 0),
hash: h,
}
}
@ -74,12 +82,12 @@ func (bf *bloomFilter) Insert(v interface{}) {
return
}
//开始插入
h := hash(v)
if h/64+1 > uint64(len(bf.bits)) {
h := bf.hash(v)
if h/64+1 > uint32(len(bf.bits)) {
//当前冗余量小于num位,需要扩增
var tmp []uint64
//通过冗余扩增减少扩增次数
if h/64+1 < uint64(len(bf.bits)+1024) {
if h/64+1 < uint32(len(bf.bits)+1024) {
//入的位比冗余的多不足2^16即1024*64时,则新增1024个uint64
tmp = make([]uint64, len(bf.bits)+1024)
} else {
@ -110,13 +118,13 @@ func (bf *bloomFilter) Check(v interface{}) (b bool) {
if bf == nil {
return false
}
h := hash(v)
h := bf.hash(v)
//h超出范围,直接返回false并结束
if h/64+1 > uint64(len(bf.bits)) {
if h/64+1 > uint32(len(bf.bits)) {
return false
}
//判断第num是否为1,为1返回true,否则为false
if bf.bits[h/64]&(1<<h%64) > 0 {
if bf.bits[h/64]&(1<<(h%64)) > 0 {
return true
}
return false