Golang基于Redis bitmap实现布隆过滤器(完结版)
Golang基于Redis bitmap实现布隆过滤器(完结版)
为了防止黑客恶意刷接口(请求压根不存在的数据),目前通常有以下几种做法:
- 限制IP(限流)
- Redis缓存不存在的key
- 布隆过滤器挡在Redis前
完整代码地址:
https://github.com/ziyifast/ziyifast-code_instruction/tree/main/blond_filter
1 概念:
1.1 本质:超大bit数组
- 原理:由一个初始值都为0的bit数组和多个hash函数构成(相当于多把锁才能打开一把钥匙,才能确认某个元素是否真的存在,提高布隆过滤器的准确率),用于快速判断集合中是否存在某个元素
- 使用3步骤:初始化bitmap -> 添加元素到bitmap(占坑位) -> 判断是否存在
-Hash冲突: 为了避免hash冲突,我们可以通过多个hash函数进行映射,比如:将player:1982分别通过多个hash函数映射到多个offset。在查询时,就需要判断是否映射的所有的offset都存在。(一个hash函数冲突概率可能很高,但是通过不同多个hash进行映射,大幅降低冲突概率)

注意📢:
- 是否存在:
- 有,可能有;因为存在hash冲突,比如我添加的是王五在1号来上班了,但是王五和李四hash值一样,结果我查询李四时,发现hash定为的offset为1了,我就误以为李四也来上班了
- 无,是肯定无。100%不存在
- 使用时,bit数组尽量大些,防止扩容。当实际元素超过初始化数量时,应重建布隆过滤器,重新分配一个size更大的过滤器,再将所有历史元素批量add
- 避免删除元素,防止误删(hash冲突:我原本想删李四的记录,结果把王五的也删除了,“连坐”)
1.2 应用场景:防止Redis缓存穿透(海量数据中判断某个元素是否存在)
- 应用场景:加在数据库、Redis之前。
- 在查询之前,先查布隆过滤器是否存在,如果不存在直接返回请求。如果存在,再查询Redis、数据库,看是否真的存在。
防止因缓存穿透导致数据库被打挂掉。- 防止被人恶意刷接口
2 环境准备
2.1 安装docker
yum install -y yum-utils
yum-config-manager \--add-repo \https://download.docker.com/linux/centos/docker-ce.repo
yum install docker
systemctl start docker
2.2 搭建Postgres
docker run -d \
-p 5432:5432 \
-e POSTGRES_USER=postgres \
-e POSTGRES_PASSWORD=postgres \
-v /Users/ziyi2/docker-home/pg:/var/lib/postgresql/data \
--name pg \
--restart always \
docker.io/postgres:9.6-alpine# -p port 映射端口,可以通过宿主机的端口访问到容器内的服务
# -d 是detach 保持程序后台运行的意思
# -e environment 设置环境变量
# -v volume 文件或者文件夹的挂载
2.3 搭建Redis
docker run -d \
--name redis \
-v /Users/ziyi2/docker-home/redis:/data \
-p 6379:6379 redis
3 代码实现
完整代码地址:
https://github.com/ziyifast/ziyifast-code_instruction/tree/main/blond_filter
3.1 方案
思路:
- 先搭建Iris+Postgres,然后再数据库前挡一层Redis
- 在Redis之前再加一层布隆过滤器。效果:
请求 - 布隆过滤器 - Redis - Postgres
代码结构:

3.2 Iris+Redis+Postgres
注意:案例中部分代码不规范,主要起演示作用
①blond_filter/pg/pg.go
package pgimport ("fmt"_ "github.com/lib/pq""github.com/ziyifast/log""time""xorm.io/xorm"
)var Cli *xorm.Engineconst (host = "localhost"port = 5432user = "postgres"password = "postgres"dbName = "postgres"
)var Engine *xorm.Enginefunc init() {psqlInfo := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable", host, port, user, password, dbName)engine, err := xorm.NewEngine("postgres", psqlInfo)if err != nil {log.Fatal(err)}engine.ShowSQL(true)engine.SetMaxIdleConns(10)engine.SetMaxOpenConns(20)engine.SetConnMaxLifetime(time.Minute * 10)engine.Cascade(true)if err = engine.Ping(); err != nil {log.Fatalf("%v", err)}Engine = enginelog.Infof("connect postgresql success")
}
②blond_filter/redis/redis.go
package redisimport "github.com/go-redis/redis"var (Client *redis.ClientPlayerPrefix = "player:"
)func init() {Client = redis.NewClient(&redis.Options{Addr: "127.0.0.1:6379",Password: "", // no password setDB: 0, // use default DB})
}
③blond_filter/model/player.go
package modeltype Player struct {Id int64 `xorm:"id" json:"id"`Name string `xorm:"name" json:"name"`Age int `xorm:"age" json:"age"`
}func (p *Player) TableName() string {return "player"
}
④blond_filter/dao/player_dao.go
package daoimport ("github.com/aobco/log""myTest/demo_home/blond_filter/model""myTest/demo_home/blond_filter/pg""time"
)type playerDao struct {
}var PlayerDao = new(playerDao)func (p *playerDao) InsertOne(player model.Player) (int64, error) {return pg.Engine.InsertOne(player)
}func (p *playerDao) GetById(id int64) (*model.Player, error) {log.Infof("query postgres,time:%v", time.Now().String())player := new(model.Player)get, err := pg.Engine.Where("id=?", id).Get(player)if err != nil {log.Errorf("%v", err)}if !get {return nil, nil}return player, nil
}
⑤blond_filter/service/player_service.go
package serviceimport ("github.com/ziyifast/log""myTest/demo_home/blond_filter/dao""myTest/demo_home/blond_filter/model""myTest/demo_home/blond_filter/util"
)type playerService struct {
}var PlayerService = new(playerService)func (s *playerService) FindById(id int64) (*model.Player, error) { query blond filter//if !util.CheckExist(id) {// return nil, nil//}//query redisplayer, err := util.PlayerCache.GetById(id)if err != nil {return nil, err}if player != nil {return player, nil}//query db and cache resultp, err := dao.PlayerDao.GetById(id)if err != nil {log.Errorf("%v", err)return nil, err}if p != nil {err = util.PlayerCache.Put(p)if err != nil {log.Errorf("%v", err)}return p, nil}return p, nil
}
⑥blond_filter/controller/player_controller.go
package controllerimport ("encoding/json""github.com/kataras/iris/v12""github.com/kataras/iris/v12/mvc""myTest/demo_home/blond_filter/service""net/http""strconv"
)type PlayerController struct {Ctx iris.Context
}func (p *PlayerController) BeforeActivation(b mvc.BeforeActivation) {b.Handle("GET", "/find/{id}", "FindById")
}func (p *PlayerController) FindById() mvc.Result {defer p.Ctx.Next()pId := p.Ctx.Params().Get("id")id, err := strconv.ParseInt(pId, 10, 64)if err != nil {return mvc.Response{Code: http.StatusBadRequest,Content: []byte(err.Error()),ContentType: "application/json",}}player, err := service.PlayerService.FindById(id)if err != nil {return mvc.Response{Code: http.StatusInternalServerError,Content: []byte(err.Error()),ContentType: "application/json",}}marshal, err := json.Marshal(player)if err != nil {return mvc.Response{Code: http.StatusInternalServerError,Content: []byte(err.Error()),ContentType: "application/json",}}return mvc.Response{Code: http.StatusOK,Content: marshal,ContentType: "application/json",}
}
⑦blond_filter/util/player_cache.go
Redis缓存模块
package utilimport ("encoding/json""github.com/go-redis/redis""github.com/ziyifast/log""myTest/demo_home/blond_filter/model"redis2 "myTest/demo_home/blond_filter/redis""strconv""time"
)type playerCache struct {
}var (PlayerCache = new(playerCache)PlayerKey = "player"
)func (c *playerCache) GetById(id int64) (*model.Player, error) {log.Infof("query redis,time:%v", time.Now().String())result, err := redis2.Client.HGet(PlayerKey, strconv.FormatInt(id, 10)).Result()if err != nil && err != redis.Nil {log.Errorf("%v", err)return nil, err}if result == "" {return nil, nil}p := new(model.Player)err = json.Unmarshal([]byte(result), p)if err != nil {log.Errorf("%v", err)return nil, err}return p, nil
}func (c *playerCache) Put(player *model.Player) error {marshal, err := json.Marshal(player)if err != nil {log.Errorf("%v", err)return err}_, err = redis2.Client.HSet(PlayerKey, strconv.FormatInt(player.Id, 10), string(marshal)).Result()if err != nil {log.Errorf("%v", err)return err}return nil
}
⑧blond_filter/main.go
package mainimport ("github.com/kataras/iris/v12""github.com/kataras/iris/v12/mvc""myTest/demo_home/blond_filter/controller"
)func main() {//pg.Engine.Sync(new(model.Player))app := iris.New()pMvc := mvc.New(app.Party("player"))pMvc.Handle(new(controller.PlayerController))//util.InitBlondFilter()app.Listen(":9999", nil)
}
演示
我们在请求到达之后,先去查询Redis,如果Redis没有则去查询Postgres,但如果此时有黑客恶意查询压根不合法的数据。就会导致在Redis一直查不到数据而不断请求Postgres。
- 导致Postgres负载过高
- 请求不存在的用户

- 查看

3.3 添加布隆过滤器(通过Redis bitmap实现)
新增布隆过滤器,加在Redis之前。
- 请求流程:请求 - 布隆过滤器 - Redis - 数据库
①blond_filter/util/check_blond_util.go
实现简易版hashCode。
- 为了避免hash冲突,我们可以通过多个hash函数进行映射,比如:将player:1982分别通过多个hash函数映射到多个offset。在查询时,就需要判断是否映射的所有的offset都存在。(一个hash函数冲突概率可能很高,但是通过不同多个hash进行映射,大幅降低冲突概率)
package utilimport ("fmt""github.com/ziyifast/log""math""myTest/demo_home/blond_filter/redis"
)var base = 1 << 32// achieve blond filter
// 1. calculate the hash of key
// 2. preload the players data
func InitBlondFilter() {//get hashCodekey := fmt.Sprintf("%s%d", redis.PlayerPrefix, 1)hashCode := int(math.Abs(float64(getHashCode(key))))//calculate the offsetoffset := hashCode % base_, err := redis.Client.SetBit(key, int64(offset), 1).Result()if err != nil {panic(err)}
}func getHashCode(str string) int {var hash int32 = 17for i := 0; i < len(str); i++ {hash = hash*31 + int32(str[i])}return int(hash)
}func CheckExist(id int64) bool {key := fmt.Sprintf("%s%d", redis.PlayerPrefix, id)hashCode := int(math.Abs(float64(getHashCode(key))))offset := hashCode % baseres, err := redis.Client.GetBit(key, int64(offset)).Result()if err != nil {log.Errorf("%v", err)return false}log.Infof("%v", res)return res == 1
}
②blond_filter/service/player_service.go
在查询Redis之前,先去查询布隆过滤器是否有数据
package serviceimport ("github.com/ziyifast/log""myTest/demo_home/blond_filter/dao""myTest/demo_home/blond_filter/model""myTest/demo_home/blond_filter/util"
)type playerService struct {
}var PlayerService = new(playerService)func (s *playerService) FindById(id int64) (*model.Player, error) {// query blond filterif !util.CheckExist(id) {log.Infof("the player does not exist in the blond filter,return it!!! ")return nil, nil}//query redisplayer, err := util.PlayerCache.GetById(id)if err != nil {return nil, err}if player != nil {return player, nil}//query db and cache resultp, err := dao.PlayerDao.GetById(id)if err != nil {log.Errorf("%v", err)return nil, err}if p != nil {err = util.PlayerCache.Put(p)if err != nil {log.Errorf("%v", err)}return p, nil}return p, nil
}
③blond_filter/main.go
package mainimport ("github.com/kataras/iris/v12""github.com/kataras/iris/v12/mvc""myTest/demo_home/blond_filter/controller""myTest/demo_home/blond_filter/util"
)func main() {//pg.Engine.Sync(new(model.Player))app := iris.New()pMvc := mvc.New(app.Party("player"))pMvc.Handle(new(controller.PlayerController))util.InitBlondFilter()app.Listen(":9999", nil)
}
演示
- 请求不存在的用户

- 查看:已经被布隆过滤器拦截,恶意请求不会打到Redis和Postgres

如果查询存在的数据,当布隆过滤器中包含时,则会继续查询Redis和Postgres,查看数据是否真的存在。(因为存在Hash冲突,导致可能误判。)
- 比如id=1982与id=28算出来的hash值一致,但其实只有28存在Redis。这时我们通过hash值查询1982,bitmap对应offset返回值为表示存在值,但其实这时Redis中只有28的数据。因此我们要继续向下查询看Redis和Postgres是否真的存在1982的数据。
相关文章:
Golang基于Redis bitmap实现布隆过滤器(完结版)
Golang基于Redis bitmap实现布隆过滤器(完结版) 为了防止黑客恶意刷接口(请求压根不存在的数据),目前通常有以下几种做法: 限制IP(限流)Redis缓存不存在的key布隆过滤器挡在Redis前 …...
Java基础-内部类
内部类 引言内部类的共性成员内部类静态内部类非静态内部类 局部内部类匿名内部类内部类的使用场景和好处 引言 Java不仅可以定义变量和方法,还可以定义类. 内部类允许你把一些逻辑相关的类组织在一起,并可以控制内部中类的可见性. 这么看来,内部类就像是代码一种隐藏机制:将类…...
设计模式-行为型模式-职责链模式
在软件系统运行时,对象并不是孤立存在的,它们可以通过相互通信协作完成某些功能,一个对象在运行时也将影响到其他对象的运行。行为型模式(Behavioral Pattern)关注系统中对象之间的交互,研究系统在运行时对…...
代码随想录算法训练营第四十天|LeetCode343 整数拆分、LeetCode96 不同的二叉搜索树
343.整数拆分 思路:确定dp数组以及下标的含义 dp[i]代表 i可以被拆分后的最大乘积。确定递推公式,假如拆成连个数,dp[i] j*(i-j),拆成两个数以上,dp[i]j*dp[i-j],j的范围为1到i-1.dp[i]找到所有情况的最大值。初始化…...
接口自动化测试用例如何设计
说到自动化测试,或者说接口自动化测试,多数人的第一反应是该用什么工具,比如:Python Requests、Java HttpClient、Apifox、MeterSphere、自研的自动化平台等。大家似乎更关注的是哪个工具更优秀,甚至出现“ 做平台的 &…...
弱电综合布线:连接现代生活的纽带
在当今信息化快速发展的时代,弱电网络布线作为信息传输的重要基础设施,其作用日益凸显。它不仅保障了数据的高效流通,还确保了通信的稳定性。从商业大厦到教育机构,从政府机关到医院急救中心,再到我们居住的社区&#…...
Java零基础 - 数组的定义和声明
哈喽,各位小伙伴们,你们好呀,我是喵手。 今天我要给大家分享一些自己日常学习到的一些知识点,并以文字的形式跟大家一起交流,互相学习,一个人虽可以走的更快,但一群人可以走的更远。 我是一名后…...
CSS补充(下),弹性布局(上)
高级选择器 1.兄弟选择器 2.同时满足 div.bg{background-color: red;}p.bg{background-color: green;}spam.bg{background-color: blue;}注:选择器中间没有空格,有明确标识的选择器写在后面 3.各种伪类的应用 3.1作为第几个子元素 选择器:nth-child…...
图数据库 之 Neo4j - 应用场景4 - 反洗钱(9)
原理 Neo4j图数据库可以用于构建和分析数据之间的关系。它使用节点和关系来表示数据,并提供实时查询能力。通过使用Neo4j,可以将大量的交易数据导入图数据库,并通过查询和分析图结构来发现洗钱行为中的模式和关联。 案例分析 假设有一家转账服务公司,有以下交易数据,每个…...
uboot分区介绍
RK平台的U-Boot支持两种分区表 RK paramter格式(旧)和 标准GPT格式(新),当机器上同时存在 两种分区表时,优先使用GPT分区表。无论是 GPT 还是 RK parameter,烧写用的分区表文件都叫parameter.t…...
快速收集诊断信息,敏捷诊断工具obdiag应用实践——《OceanBase诊断系列》之三
1. 前言 作为OceanBase的敏捷诊断工具,obdiag具有以下特点: 部署便捷:提供rpm包和OBD上部署的模式,都能够一键部署安装。用户可以选择将其部署到集群中任意一台能连接到各个节点的设备上,而不仅限于OBServer节点。即…...
C++错误总结(1)
1.定义函数类型时,如果没有返回值,用void void swap(int &x, int &y){ int tem x; x y; y tem; } 2.输入时,不加换行符 cin >> a >> b >> c >> endl ;(红色标记的是错误的部分) 3.【逆序出入…...
std::shared_from_this注意事项:exception bad_weak_ptr
1.不可以在构造函数中调用shared_from_this() 因为它的实现是: _LIBCPP_INLINE_VISIBILITYshared_ptr<_Tp> shared_from_this(){return shared_ptr<_Tp>(__weak_this_);}也就是它依赖的__weak_this_此时还未创建完成。 2.一定要public继承 class MyTy…...
【工具】Raycast – Mac提效工具
引入 以前看到同事们锁屏的时候,不知按了什么键,直接调出这个框,然后输入lock屏幕就锁了。 跟我习惯的按Mac开机键不大一样。个人觉得还是蛮炫酷的~ 调研 但是由于之前比较繁忙,这件事其实都忘的差不多了࿰…...
蓝桥杯集训·每日一题2024 (二分,双指针)
前言: 开学了,平时学习的压力也逐渐大起来了,不过还算可以接受,等到后面阶段考的时候就不一样了,我目前为了转专业退选了很多课,这些课我都需要花时间来刷绩点,不然保研就没有竞争力了。我自己会…...
在Linux(Ubuntu)中使用终端编译 vscode安装
文章目录 📚在Linux(Ubuntu)中使用终端编译🐇.cpp程序编译🐇.py程序编译🐇查看Python、C编程环境 📚vscode安装 📚在Linux(Ubuntu)中使用终端编译 虚拟机安装…...
官网正在被哪些产品蚕食,定制网站又被哪些建站产品挤占。
2023-12-09 16:22贝格前端工场 官网建设是一个被大多数人看衰的市场,本文来理性分析下,谁在蚕食这个市场,谁又在挤占这个产品生存空间,欢迎大家评论,探讨。 网站正在被以下产品形式取代: 1. 移动应用&…...
BUUCTF---[MRCTF2020]你传你呢1
1.题目描述 2.打开题目链接 3.上传shell.jpg文件,显示连接成功,但是用蚁剑连接却连接不上。shell文件内容为 <script languagephp>eval($_REQUEST[cmd]);</script>4.用bp抓包,修改属性 5.需要上传一个.htaccess的文件来把jpg后缀…...
vite+vue3门户网站菜单栏动态路由控制
门户网站用户端需要分板块展示,板块内容由管理端配置,包括板块名称,访问路径,路由组件,展示顺序,是否展示。如下图所示: 用户访问门户网站时,展示菜单跳转通过板块配置,动…...
【C语言】linux内核packet_setsockopt
一、中文注释 // 发送数据包函数。它尝试通过特定的网络设备队列直接传输一个skb(socket缓冲区)。 static int packet_direct_xmit(struct sk_buff *skb) {return dev_direct_xmit(skb, packet_pick_tx_queue(skb)); // 调用dev_direct_xmit函数&#x…...
【根据当天日期输出明天的日期(需对闰年做判定)。】2022-5-15
缘由根据当天日期输出明天的日期(需对闰年做判定)。日期类型结构体如下: struct data{ int year; int month; int day;};-编程语言-CSDN问答 struct mdata{ int year; int month; int day; }mdata; int 天数(int year, int month) {switch (month){case 1: case 3:…...
线程同步:确保多线程程序的安全与高效!
全文目录: 开篇语前序前言第一部分:线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分:synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分ÿ…...
高等数学(下)题型笔记(八)空间解析几何与向量代数
目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...
IT供电系统绝缘监测及故障定位解决方案
随着新能源的快速发展,光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域,IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选,但在长期运行中,例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...
C++八股 —— 单例模式
文章目录 1. 基本概念2. 设计要点3. 实现方式4. 详解懒汉模式 1. 基本概念 线程安全(Thread Safety) 线程安全是指在多线程环境下,某个函数、类或代码片段能够被多个线程同时调用时,仍能保证数据的一致性和逻辑的正确性…...
均衡后的SNRSINR
本文主要摘自参考文献中的前两篇,相关文献中经常会出现MIMO检测后的SINR不过一直没有找到相关数学推到过程,其中文献[1]中给出了相关原理在此仅做记录。 1. 系统模型 复信道模型 n t n_t nt 根发送天线, n r n_r nr 根接收天线的 MIMO 系…...
短视频矩阵系统文案创作功能开发实践,定制化开发
在短视频行业迅猛发展的当下,企业和个人创作者为了扩大影响力、提升传播效果,纷纷采用短视频矩阵运营策略,同时管理多个平台、多个账号的内容发布。然而,频繁的文案创作需求让运营者疲于应对,如何高效产出高质量文案成…...
视觉slam十四讲实践部分记录——ch2、ch3
ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...
20个超级好用的 CSS 动画库
分享 20 个最佳 CSS 动画库。 它们中的大多数将生成纯 CSS 代码,而不需要任何外部库。 1.Animate.css 一个开箱即用型的跨浏览器动画库,可供你在项目中使用。 2.Magic Animations CSS3 一组简单的动画,可以包含在你的网页或应用项目中。 3.An…...
Webpack性能优化:构建速度与体积优化策略
一、构建速度优化 1、升级Webpack和Node.js 优化效果:Webpack 4比Webpack 3构建时间降低60%-98%。原因: V8引擎优化(for of替代forEach、Map/Set替代Object)。默认使用更快的md4哈希算法。AST直接从Loa…...
