Redis系列补充:聊聊布隆过滤器(go语言实践篇)

1 介绍

布隆过滤器(Bloom Filter)是 Redis 4.0 版本之后提供的新功能,我们一般将它当做插件加载到 Redis Service服务器中,给 Redis 提供强大的滤重功能。

它是一种概率性数据结构,可用于判断一个元素是否存在于一个集合中。相比较之 Set 集合的去重功能,布隆过滤器空间上能节省 90% +,不足之处是去重率大约在 99% 左右,那就是有 1% 左右的误判率,这种误差是由布隆过滤器的自身结构决定的。它有如下优缺点:

  • 优点:空间效率和查询时间都比一般的算法要好的多

  • 缺点:有一定的误识别率和删除困难

2 应用场景说明

我们在遇到数据量大的时候,为了去重并避免大批量的重复计算,可以考虑使用 Bloom Filter 进行过滤。具体常用的经典场景如下:

  • 解决大流量下缓存穿透的问题,参考笔者这篇《一次缓存雪崩的灾难复盘》。

  • 过滤被屏蔽、拉黑、减少推荐的信息,一般你在浏览抖音或者百度App的时候,看到不喜欢的会设置减少推荐、屏蔽此类信息等,都可以采用这种原理设计。

  • 各种名单过滤,使用布隆过滤器实现第一层的白名单或者黑名单过滤,可用于各种AB场景。

下面以缓存穿透为解决目标进行案例介绍。

3 案例分析

布隆过滤器的一个经典应用场景就是解决缓存穿透问题!

缓存穿透是指访问一个不存在的key,缓存不起作用,请求会穿透到DB,流量井喷时会导致DB挂掉。

比如 我们查询用户的信息,程序会根据用户的编号去缓存中检索,如果找不到,再到数据库中搜索。如果你给了一个不存在的编号:XXXXXXXX,那么每次都比对不到,就透过缓存进入数据库。这样风险很大,如果因为某些原因导致大量不存在的编号被查询,甚至被恶意伪造编号进行大规模攻击,那将是灾难。

解决方案质疑就是在缓存之前在加一层 BloomFilter :

  • 把存在的key记录在BloomFilter中,在查询的时候先去 BloomFilter 去查询 key 是否存在,如果不存在则说明数据库和缓存都没有,就直接返回,

  • 存在再走查缓存 ,投入数据库去查询,这样减轻了数据库的压力。

3.1 巨量查询场景

下面以火车票订购和查询为案例进行说明,如果火车票被恶意攻击,模拟了一样结构的火车票订单编号,那很可能通过大量的请求穿透过缓存层把数据库打雪崩了,所以使用布隆过滤器为服务提供一层保障。具体的做法就是,我们在购买火车票成功的时候,把订单号的ID写入(异步或者消息队列的方式)到布隆过滤器中,保障后续的查询都在布隆过滤器中走一遍再进到缓存中去查询。

3.2 创建Bloom Filter

创建 Bloom Filter 的语法如下:

# BF.RESERVE {key} {error_rate} {capacity} [EXPANSION {expansion}] [NONSCALING]
BF.RESERVE ticket_orders 0.01 1000000

这边的命令是通过BF.RESERVE命令手动创建一个名字为 ticket_orders,错误率为 0.01 ,初始容量为 1000000 的布隆过滤器。这边需要注意的一些点是:

  • error_rate 越小,对碰撞的容忍度越小,需要的存储空间就越大。如果允许一定比例的不准确,对精确度要求不高的场景,error_rate 可以设的稍大一点。

  • capacity 设置的过大,会浪费存储空间,设置过小,准确度不高。所以评估的时候需要精准一点,既要避免浪费空间也要保证准确比例。

3.3 创建车票订单

# BF.ADD {key}  {value ... }# 添加单个订单号
BF.ADD ticket_orders 1725681193-350000
(integer) 1# 添加多个订单号
BF.MADD ticket_orders 1725681193-350000 1725681197-270001 1725681350-510007
1) (integer) 1
2) (integer) 1
3) (integer) 1

以上的语句是将已经订好的车票订单号存储到Bloom Filter中,包括一次存储单个和一次存储多个。

火车票订单同步到 Bloom Filter 的步骤如下:

image

3.4 判断火车票订单Id是否存在

# BF.EXISTS {key} {value} ,存在的话返回 1,不存在返回 0
BF.EXISTS ticket_orders 1725681193-350000
(integer) 1# 批量判断多个值是否存在于布隆过滤器,语句如下:
BF.MEXISTS ticket_orders 1725681193-350000 1725681197-270001 1725681350-510007
1) (integer) 0
2) (integer) 1
3) (integer) 0

BF.EXISTS 判断一个元素是否存在于 Bloom Filter中,返回值 = 1 表示存在,返回值 = 0 表示不存在。可以一次性判断单个元素,或者一次性判断多个元素。

image

综上,我们通过几个指令就能实现布隆过滤器的建设,避免缓存穿透的情况发生。如果你要查询缓存信息,必须先到Bloom Filter中先跑一次,不存在的直接过滤掉,这样就不会因为无效的key把缓存打穿。

4 程序实现说明

可以在 Golang 中使用 go-redis/redis 库来封装布隆过滤器功能。你需要先确保你的 Redis 服务器已经安装了 RedisBloom 模块,因为 Redis 本身并不直接支持布隆过滤器。一旦 RedisBloom 安装并配置好,你就可以在 Go 代码中通过 go-redis/redis 库来调用相关的 RedisBloom 命令。

package bloomfilter  import (  "context"  "fmt"  "github.com/go-redis/redis/v8"  
)  // BloomFilter 封装了与布隆过滤器相关的操作  
type BloomFilter struct {  rdb  *redis.Client  name string  
}  // NewBloomFilter 创建一个新的布隆过滤器实例  
func NewBloomFilter(rdb *redis.Client, name string) *BloomFilter {  return &BloomFilter{  rdb:  rdb,  name: name,  }  
}  // Add 将元素添加到布隆过滤器中  
func (bf *BloomFilter) Add(ctx context.Context, item string, capacity int64, errorRate float64) error {  // 注意:RedisBloom 的 BF.ADD 命令通常不需要显式设置容量和错误率,  // 因为这些是在创建布隆过滤器时设置的。这里我们简化为只添加元素。  // 如果需要动态调整这些参数,你可能需要重新创建布隆过滤器。  // 但为了示例,我们假设这些参数在创建布隆过滤器时已经设置好了。  _, err := bf.rdb.Do(ctx, "BF.ADD", bf.name, item).Result()  return err  
}  // Exists 检查元素是否可能存在于布隆过滤器中  
func (bf *BloomFilter) Exists(ctx context.Context, item string) (bool, error) {  result, err := bf.rdb.Do(ctx, "BF.EXISTS", bf.name, item).Int()  if err != nil {  return false, err  }  // BF.EXISTS 返回 1 表示可能存在,0 表示一定不存在  return result == 1, nil  
}  // 注意:在实际应用中,你可能还需要封装更多操作,比如删除布隆过滤器(虽然布隆过滤器通常不支持删除单个元素)  
// 或者调整布隆过滤器的容量和错误率(这通常意味着需要重新创建布隆过滤器)。  func main() {  rdb := redis.NewClient(&redis.Options{  Addr:     "localhost:6379", // Redis 地址  Password: "",              // 密码(如果有的话)  DB:       0,               // 使用的数据库  })  bf := NewBloomFilter(rdb, "myBloomFilter")  ctx := context.Background()  // 添加元素  err := bf.Add(ctx, "item1", 100000, 0.01) // 注意:BF.ADD 命令通常不需要 capacity 和 errorRate  if err != nil {  panic(err)  }  // 检查元素是否存在  exists, err := bf.Exists(ctx, "item1")  if err != nil {  panic(err)  }  fmt.Println("Exists:", exists)  exists, err = bf.Exists(ctx, "item2")  if err != nil {  panic(err)  }  fmt.Println("Exists:", exists)  
}  // 注意:上面的 Add 方法中的 capacity 和 errorRate 参数在 BF.ADD 命令中并不直接使用,  
// 因为 RedisBloom 的 BF.ADD 命令主要用于添加元素到已存在的布隆过滤器中。  
// 容量和错误率通常在创建布隆过滤器时通过 BF.RESERVE 命令设置。

重要提示

  • 在上面的代码中,Add 方法的 capacity 和 errorRate 参数并未直接用于 BF.ADD 命令,因为 BF.ADD 只是用于向已存在的布隆过滤器中添加元素。如果你需要设置布隆过滤器的容量和错误率,你应该在创建布隆过滤器时使用 BF.RESERVE 命令。

  • 布隆过滤器不支持传统意义上的“删除”操作,因为一旦一个位被设置为 1,它就不能再被设置为 0(除非重新创建布隆过滤器)。

  • 在实际部署之前,请确保你的 Redis 服务器已经安装了 RedisBloom 模块,并且 go-redis/redis 库与你的 Redis 服务器版本兼容。

5 总结

本篇介绍了布隆过滤器的几种实现场景。并以火车票订单信息查询为案例进行说明,如何使用布隆过滤器避免缓存穿透,避免被恶意攻击。

文章转载自:Hello-Brand

原文链接:https://www.cnblogs.com/wzh2010/p/18030915

体验地址:引迈 - JNPF快速开发平台_低代码开发平台_零代码开发平台_流程设计器_表单引擎_工作流引擎_软件架构

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/149838.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

Register Two Point Sets 注册两个点集

文章目录 Register Two Point Sets 注册两个点集Visualize Gradient Descent 可视化梯度下降Hyperparameter Search 超参数搜索JensenHavrdaCharvatTsallisPointSetToPointSetMetricv4类说明 原文url: https://examples.itk.org/src/registration/metricsv4/registertwopointse…

2024/9/24有关1x1卷积核

深度学习笔记(六):1x1卷积核的作用归纳和实例分析_1x1卷积降维-CSDN博客 从这篇文章写的很好,主要讲的就是1x1卷积核有降维作用,还有就是线性映射作用(一般步进长度设置为的为1,也相当于是全连…

R包:ggspatial空间画图

ggplot2语法的空间图形画图 Spatial data plus the power of the ggplot2 framework means easier mapping. 加载R包 # install.packages("ggspatial")library(ggplot2) library(ggspatial) load_longlake_data()Using layer_spatial() and annotation_spatial() g…

Sql Developer期显示格式设置

默认时间格式显示 设置时间格式:工具->首选项->数据库->NLS->日期格式: DD-MON-RR 修改为: YYYY-MM-DD HH24:MI:SS 设置完格式显示:

数学家发现一种新空间镶嵌形状

不知道你没有读过空间嵌合的相关理论。嵌合或者平铺在欧拉的时代就被研究透了,被认为是低矮的树木上已经没有果子。不过最近发现了一种新的镶嵌结构。 数学家这样描述了这种新的形状,这种形状在自然界中很常见ーー从鹦鹉螺标志性的螺旋壳的腔室&#xf…

百度C++一面-面经总结

1、你知道网络编程服务端建立连接的流程吗?把用到的api说出来? server: 1.socket() int sockfd socket(AF_INET, SOCK_STREAM, 0);2.bind() struct sockaddr_in serv_addr; serv_addr.sin_family AF_INET; serv_addr.sin_addr.s_addr I…

C语言初识(一)

目录 前言 一、什么是C语言? 二、第一个C语言程序 (1)创建新项目 (2)编写代码 (3)main函数 三、数据类型 四、变量、常量 (1)变量的命名 (2&#x…

003_动手实现MLP(详细版)

常见的激活的有:RELU,sigmoid,tanh代码 import torch import numpy as np import sys import d2lzh_pytorch as d2l import torchvision from torchvision import transforms # 1.数据预处理 mnist_train torchvision.datasets.FashionMNIST(root/Users/wPycharmP…

2024年9月24日历史上的今天大事件早读

1550年9月24日 明代戏剧家汤显祖出生 1852年9月24日 法国人吉法尔制造的用蒸汽机推进的飞船试飞成功 1884年9月24日 中国近代化学的先驱徐寿逝世 1905年9月24日 吴樾壮炸五大臣,身殉革命 1909年9月24日 京张铁路通车 1910年9月24日 剧作家曹禺诞生 1930年9月2…

java并发工具包JUC(Java Util Concurrent)

1. 什么是JUC 1.1 JUC简介 JUC(Java Util Concurrent)是Java中的一个并发工具包,提供了一系列用于多线程编程的类和接口,旨在简化并发编程并提高其效率和可维护性。JUC库包含了许多强大的工具和机制,用于线程管理、同…

【Python报错已解决】NameError: name ‘reload‘ is not defined

🎬 鸽芷咕:个人主页 🔥 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想,就是为了理想的生活! 专栏介绍 在软件开发和日常使用中,BUG是不可避免的。本专栏致力于为广大开发者和技术爱好者提供一个关于BUG解决的经…

【JVM】JVM执行流程和内存区域划分

是什么 Java 虚拟机 JDK,Java 开发工具包JRE,Java 运行时环境JVM,Java 虚拟机 JVM 就是 Java 虚拟机,解释执行 Java 字节码 JVM 执行流程 编程语言可以分为: 编译型语言:先将高级语言转换成二进制的机器…

飞腾平台perf工具PMU事件集成指南

【写在前面】 飞腾开发者平台是基于飞腾自身强大的技术基础和开放能力,聚合行业内优秀资源而打造的。该平台覆盖了操作系统、算法、数据库、安全、平台工具、虚拟化、存储、网络、固件等多个前沿技术领域,包含了应用使能套件、软件仓库、软件支持、软件适…

linux信号| 学习信号三步走 | 学习信号需要打通哪些知识脉络?

前言: 本节内容主要讲解linux下信号的预备知识以及信号的概念, 信号部分我们将会分为几个阶段进行讲解:信号的概念, 信号的产生, 信号的保存。本节主要讲解信号 ps:本节内容适合学习了进程相关概念的友友们进行观看哦 目录 什么是…

大模型算法岗常见面试题100道(值得收藏)

大模型应该是目前当之无愧的最有影响力的AI技术,它正在革新各个行业,包括自然语言处理、机器翻译、内容创作和客户服务等等,正在成为未来商业环境的重要组成部分。 截至目前大模型已经超过200个,在大模型纵横的时代,不…

测试从业者需要了解心理学和经济学

对于测试从业者来说,测试工作是一项技术活,但同时它也涉及到经济学和人类心理学一些重要因素。 在理想情况下,我们会测试程序的所有可能执行情况,而在大多数情况下,这几乎是不可能的。即使一个看起来非常简单的程序&a…

828华为云征文|使用华为云Flexus云服务器X搭建部署茶叶商城小程序uniapp

在当今数字化时代,小程序以其便捷、高效的特点成为了众多商家拓展业务的重要渠道。 本文将详细介绍如何使用新购买的华为云 Flexus 云服务器 X 搭建,一个带商品采集功能、H5积分商城、集合拼团、砍价、秒杀、会员、分销等等功能一个茶叶商城小程序。 后端…

共享wifi公司哪家正规合法?具体流程全公开!

随着共享经济时代的到来,以共享wifi为代表的多个项目逐渐成为众多创业赛道中的一大热门,推出共享wifi及其他项目的公司数量也因此呈现出了快速增长的态势。而这也让共享wifi等市场出现了鱼龙混杂的情况,连带着共享wifi哪家公司正规合法等相关…

写作高质量文案很难,文案自动生成器轻松解决

在当今信息爆炸的网络环境中,拥有一篇高质量的文案对于吸引受众、传达信息和实现目标至关重要。然而,写作高质量文案并非易事,它需要作者具备深厚的语言功底、创新的思维以及对目标受众的精准把握。这一系列的要求常常让许多人陷入写作的困境…

Windows电脑使用VNC远程桌面本地局域网内无公网IP树莓派5

目录 前言 1. 使用 Raspberry Pi Imager 安装 Raspberry Pi OS 2. Windows安装VNC远程树莓派 3. 使用VNC Viewer公网远程访问树莓派 3.1 安装Cpolar步骤 3.2 配置固定的公网地址 3.3 VNC远程连接测试 4. 固定远程连接公网地址 4.1 固定TCP地址测试 作者简介&#xff1…