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…...
无法与IP建立连接,未能下载VSCode服务器
如题,在远程连接服务器的时候突然遇到了这个提示。 查阅了一圈,发现是VSCode版本自动更新惹的祸!!! 在VSCode的帮助->关于这里发现前几天VSCode自动更新了,我的版本号变成了1.100.3 才导致了远程连接出…...
CentOS下的分布式内存计算Spark环境部署
一、Spark 核心架构与应用场景 1.1 分布式计算引擎的核心优势 Spark 是基于内存的分布式计算框架,相比 MapReduce 具有以下核心优势: 内存计算:数据可常驻内存,迭代计算性能提升 10-100 倍(文档段落:3-79…...
【Go】3、Go语言进阶与依赖管理
前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课,做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程,它的核心机制是 Goroutine 协程、Channel 通道,并基于CSP(Communicating Sequential Processes࿰…...
Caliper 配置文件解析:config.yaml
Caliper 是一个区块链性能基准测试工具,用于评估不同区块链平台的性能。下面我将详细解释你提供的 fisco-bcos.json 文件结构,并说明它与 config.yaml 文件的关系。 fisco-bcos.json 文件解析 这个文件是针对 FISCO-BCOS 区块链网络的 Caliper 配置文件,主要包含以下几个部…...
如何在网页里填写 PDF 表格?
有时候,你可能希望用户能在你的网站上填写 PDF 表单。然而,这件事并不简单,因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件,但原生并不支持编辑或填写它们。更糟的是,如果你想收集表单数据ÿ…...
基于TurtleBot3在Gazebo地图实现机器人远程控制
1. TurtleBot3环境配置 # 下载TurtleBot3核心包 mkdir -p ~/catkin_ws/src cd ~/catkin_ws/src git clone -b noetic-devel https://github.com/ROBOTIS-GIT/turtlebot3.git git clone -b noetic https://github.com/ROBOTIS-GIT/turtlebot3_msgs.git git clone -b noetic-dev…...
django blank 与 null的区别
1.blank blank控制表单验证时是否允许字段为空 2.null null控制数据库层面是否为空 但是,要注意以下几点: Django的表单验证与null无关:null参数控制的是数据库层面字段是否可以为NULL,而blank参数控制的是Django表单验证时字…...
在 Spring Boot 中使用 JSP
jsp? 好多年没用了。重新整一下 还费了点时间,记录一下。 项目结构: pom: <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://ww…...
深度学习之模型压缩三驾马车:模型剪枝、模型量化、知识蒸馏
一、引言 在深度学习中,我们训练出的神经网络往往非常庞大(比如像 ResNet、YOLOv8、Vision Transformer),虽然精度很高,但“太重”了,运行起来很慢,占用内存大,不适合部署到手机、摄…...
快速排序算法改进:随机快排-荷兰国旗划分详解
随机快速排序-荷兰国旗划分算法详解 一、基础知识回顾1.1 快速排序简介1.2 荷兰国旗问题 二、随机快排 - 荷兰国旗划分原理2.1 随机化枢轴选择2.2 荷兰国旗划分过程2.3 结合随机快排与荷兰国旗划分 三、代码实现3.1 Python实现3.2 Java实现3.3 C实现 四、性能分析4.1 时间复杂度…...
