【业务场景实战】你知道布隆过滤器怎么用吗?

布隆过滤器想必大家都听过,背过Redis面试题的兄弟应该都知道,布隆过滤器是解决缓存穿透问题的一种方法。但可能很少用过

布隆过滤器主要是为了解决海量数据的存在性问题。对于海量数据中判定某个数据是否存在且容忍轻微误差这一场景(比如缓存穿透、海量数据去重)来说,非常适合。

一、简介

布隆过滤器是一个非常神奇的数据结构,通过它我们可以非常方便地判断一个给定数据是否存在于海量数据中。我们可以把它看作由二进制向量(或者说位数组)和一系列随机映射函数(哈希函数)两部分组成的数据结构。相比于我们平时常用的 List、Map、Set 等数据结构,它占用空间更少并且效率更高,但是缺点是其返回的结果是概率性的,而不是非常准确的。理论情况下添加到集合中的元素越多,误报的可能性就越大。并且,存放在布隆过滤器的数据不容易删除

布隆过滤:布隆过滤器其实采用的是哈希思想来解决这个问题,通过一个庞大的二进制数组,走哈希思想去判断当前这个要查询的这个数据是否存在,如果布隆过滤器判断存在,则放行,这个请求会去访问redis,哪怕此时redis中的数据过期了,但是数据库中一定存在这个数据,在数据库中查询出来这个数据后,再将其放入到redis中。

假设布隆过滤器判断这个数据不存在,则直接返回

简单来说,布隆过滤器就是一个集合,用来存放数据,同时用以判断数据是否存在于这个集合中。这种方式优点在于节约内存空间,但存在误判,误判原因在于:布隆过滤器走的是哈希思想,只要哈希思想,就可能存在哈希冲突。

正常来说,数据量越大,出现误判的可能性就越大。

二、使用场景

  1. 判断给定数据是否存在:比如判断一个数字是否存在于包含大量数字的数字集中(数字集很大,上亿)、 防止缓存穿透(判断请求的数据是否有效避免直接绕过缓存请求数据库)等等、邮箱的垃圾邮件过滤(判断一个邮件地址是否在垃圾邮件列表中)、黑名单功能(判断一个 IP 地址或手机号码是否在黑名单中)等等。

  2. 去重:比如爬给定网址的时候对已经爬取过的 URL 去重、对巨量的 QQ 号/订单号去重。

去重场景也需要用到判断给定数据是否存在,因此布隆过滤器主要是为了解决海量数据的存在性问题。

下面我们来简单使用一下

三、简单使用

1、导入依赖

<dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>28.0-jre</version>
</dependency>

2、代码测试

// 创建布隆过滤器对象:
// 参数1 = 预计插入的元素数量, 参数2 = 容错率(期望的误判率)
BloomFilter<Integer> filter = BloomFilter.create(
Funnels.integerFunnel(),
1500,
0.01);
// 判断指定元素是否存在
System.out.println(filter.mightContain(1));//false
System.out.println(filter.mightContain(2));//false
// 将元素添加进布隆过滤器
filter.put(1);
filter.put(2);
System.out.println(filter.mightContain(1));//true
System.out.println(filter.mightContain(2));//true

四、项目实战

下面我们用布隆过滤器来解决缓存穿透

虽然缓存穿透问题可以用布隆过滤器来解决,但是布隆过滤器中的数据不易被删除,如果数据库中的数据有新增或者删除,布隆过滤器无法及时删除或新增数据。

上面所介绍的布隆过滤器其实有个问题

它最大的一个问题是它只能在同一个方法里面进行判断,不同的线程所创建的布隆过滤器会不一样。

比如说你在一个方法里面创建好了一个布隆过滤器,也添加了一些元素;但如果你在另一个方法里面去判断是查询不到的,也就类似于本地和分布式的区别。

所以我们这里采用Redis布隆过滤器。

Redis布隆过滤器需要在Redis上集成一个插件,选择对应Redis版本的插件,我这里使用的是Redis6,具体下载流程看:下载流程:硬核 | Redis 布隆(Bloom Filter)过滤器原理与实战-腾讯云开发者社区-腾讯云 (tencent.com)

1、缓存穿透思路

在数据到达数据库时,应该先判断布隆过滤器中是否存在key值,但是有个问题

布隆过滤器中原本不存在数据,那么又如何去判断数据是否存在?

我们需要先将数据添加到过滤器中,然后再让其发挥作用

布隆过滤器中数据Key的对象应该对应数据库,而不是缓存。

代码逻辑:

  • 首先判断请求值key是否存在于布隆过滤器中

  • 不存在,直接返回

  • 存在,查询缓存中是否有对应数据

    • 有,直接返回数据

    • 没有,查询数据库

      • 不存在,返回空数据

      • 存在,更新缓存,返回数据

key过了布隆过滤器表示数据库中存在id对应的数据,剩下两种:

  • 缓存key不存在—查询数据库

  • 缓存key存在

    1. key为空值

    2. key中数据存在,直接返回

所以我们这里需要过滤掉key中数据为空的情况,保证放回的是真实存在的数据.

2、代码实现

1)导入依赖
<dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.21.3</version></dependency>
2)配置Redisson

注意:如果项目中用到了Redis,数据库要和它的区别开,不然运行会发生冲突的

# 分布式限流Redisson 配置redisson:database: 1host: port: 6379timeout: 5000password: 
3)Redisson配置类
package com.example.config;import lombok.Data;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** 分布式限流*/
@Configuration
@ConfigurationProperties(prefix = "spring.redisson")
@Data
public class RedissonConfig {private String host;private Integer database;private Integer port;private String password;//spring启动时,自动创建RedissonClient对象@Beanpublic RedissonClient getRedissonClient() {Config config = new Config();// 设置使用单个服务器config.useSingleServer()// 设置数据库.setDatabase(database)// 设置地址.setAddress("redis://" + host + ":" + port)// 设置密码.setPassword(password);// 创建Redisson客户端RedissonClient redisson = Redisson.create(config);return redisson;}
}
2)布隆过滤管理类

创建布隆过滤器管理类,设置插入的元素数量和容错率

一般来说,使用管理类是为了降低代码的耦合性,可以让代码看上去更加美观,当然最重要的是实用。管理类不依赖于项目中的逻辑,在任何项目中都可以写入复用。


@Servicepublic class BloomFilterManager {​@Resourceprivate RedissonClient redissonClient;/*** 创建布隆过滤器** @param filterName         过滤器名称* @param expectedInsertions 预测插入数量* @param falsePositiveRate  误判率*/public <T> RBloomFilter<T> create(String filterName, long expectedInsertions, double falsePositiveRate) {RBloomFilter<T> bloomFilter = redissonClient.getBloomFilter(filterName);bloomFilter.tryInit(expectedInsertions, falsePositiveRate);return bloomFilter;}}
3)查询数据ID
@Select("SELECT id FROM tb_shop")List<Long> selectAllIds();

在shopMapper中添加这段代码,查询数据库中所有数据的ID

4)布隆过滤器初始化

这步不知道你们能不能想到,有没有觉得很熟悉,嗯?

对了,上面一篇文章我讲过的,初始化注解,代码都还没删呢

项目启动时,初始化创建布隆过滤器,并把查询到的ID都插入过滤器中

通过Redis布隆过滤器,就相当于在Redis中创造了一个特殊容器来存储数据

这段代码的逻辑就是定义一个布隆过滤器,然后把查询到的shopid都写入到Redis布隆过滤器中

然后就可以在实际方法里面去查询指定的shopId元素是否真的存在了

@PostConstructpublic void init() {//myMessageProducer.sendMessage("hmdp_exchange", "hmdp_routingKey", String.valueOf(1L));// 启动项目时初始化bloomFilterbloomFilter = bloomFilterManager.create(BLOOM_FILTER_SHOP, expectedInsertions, falseProbability);// 设置缓存时间int timeToLive = new Random().nextInt(200) + 300;// 设置bloomFilter的过期时间redissonClient.getBucket(BLOOM_FILTER_SHOP).set(null, timeToLive, TimeUnit.SECONDS);List<Long> list = shopMapper.selectAllIds();for (Long shopId : list) {bloomFilter.add(shopId);}long elementCount = bloomFilter.count();log.info("布隆过滤器加入元素个数为:{}.", elementCount);}

初始化后,启动项目后,控制台也会自动执行查询方法,初始化成功

方法一: 启动项目时初始化bloomFilter,将数据库中查询的ID都添加到过滤器中

方法二:

  • 首先判断请求值key是否存在于布隆过滤器中

  • 不存在,直接返回

  • 存在,查询缓存中是否有对应的数据

    • 有,直接返回数据

    • 没有,查询数据库

      • 不存在,返回空

      • 存在,更新缓存,返回数据

接下来我们需要来模拟情况:

1、数据库中存在,缓存中不存在

预期:能通过布隆,最后到达数据库,并将查询到的数据存入Redis

2、数据库和缓存中数据均存在

预期:通过布隆,到达缓存直接返回

3、布隆中key不存在

预期:直接失败

4、布隆中key存在,缓存中key存在但为空,数据库中数据不存在

代码小抄

最后来看结果:

今天的讲解就到这里,期待下期再见!希望的话,欢迎点赞关注!

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

03 网络编程 TCP传输控制协议

目录 1、TCP基本特征 2、TCP通信流程基本原理 &#xff08;1&#xff09;基本原理 &#xff08;2&#xff09;TCP通信代码实现 &#xff08;3&#xff09;核心API解析 1&#xff09;地址绑定--bind 2)设置监听-listen 3)等待连接请求-accept-产生一个已连接套接字 4&a…

Transformer2

1.编解码 外国人来到中国&#xff0c;是如何知晓“梨”的中文&#xff1f; 相同的词&#xff0c;上下文应该都是相关的&#xff0c;又因为是计算机&#xff0c;所以需要将语义关系码进行数字化&#xff0c;这些数字需要体现出语义关系。 1.编解码的两个标准 编解码的两个标准包…

复习之 java 锁

裁员在家&#xff0c;没有面试机会&#xff0c;整理整理面试知识点吧&#xff01; 不得不知道的java 锁 Java 中&#xff0c;提供了两种方式来实现同步互斥访问&#xff08;也就是锁&#xff09;&#xff1a;synchronized 和 Lock 多线程编程中&#xff0c;有可能会出现多个线…

基于YOLOv8的缺陷检测任务模型训练

文章目录 一、引言二、环境说明三、缺陷检测任务模型训练详解3.1 PCB数据集3.1.1 数据集简介3.1.2 数据集下载3.1.3 构建yolo格式的数据集 3.2 基于ultralytics训练YOLOv83.2.1 安装依赖包3.2.2 ultralytics的训练规范说明3.2.3 创建训练配置文件3.2.4 下载预训练模型3.2.5 训练…

招聘|头部云厂商招 PG 核心骨干 DBA【上海】

我们的招聘专区又回来了&#xff01;&#x1f3c3; Bytebase 作为先进的数据库 DevOps 团队协同工具 &#x1f527;&#xff0c;用户群里汇聚了 &#x1f497; 业界优秀的 DBA&#xff0c;SRE&#xff0c;运维的同学们 &#x1f31f;。 上周用户群里有小伙伴发招聘信息 &…

webrtc学习笔记1

WebRTC开发环境 配置vscode 安装插件 Prettier Code Formatter 使用 Prettier 来统一代码风格&#xff0c;当保存 HTML/CSS/JavaScript 文件时&#xff0c;它会自动调整代码格式。 Live Server&#xff1a;在本地开发环境中&#xff0c;实时重新加载(reload)页面。 第一个简…

QT:Telnet客户端与服务器的创建

客户端 telnetClient类 #ifndef TELNETCLIENT_H #define TELNETCLIENT_H#include <QObject> #include <QTcpSocket>class TelnetClient : public QObject {Q_OBJECTpublic:explicit TelnetClient(QObject *parent nullptr);~TelnetClient();// 连接到指定的主机…

启发式算法之模拟退火算法

文章目录 1. 模拟退火算法概述1.1 算法起源与发展1.2 算法基本原理 2. 算法实现步骤2.1 初始化过程2.2 迭代与降温策略 3. 模拟退火算法的优化策略3.1 冷却进度表的设计3.2 参数调整与策略 4. 模拟退火算法的应用领域4.1 组合优化问题4.1.1 旅行商问题&#xff08;TSP&#xff…

YOLO好像也没那么难?

“学YOLO的念头是想整个游戏外挂&#xff01;” 目录 基本原理 模型推理 IOU交并比 NMS非极大值抑制 模型训练 损失函数LOSS 代码实现 YOLO学习渠道 基本原理 模型推理 学习一个新的神经网络结构&#xff0c;作者认为整明白输入和输出是怎么回事就OK了&#xff0c;至于…

HTML静态网页成品作业(HTML+CSS)——安徽宣笔设计制作(5个页面)

&#x1f389;不定期分享源码&#xff0c;关注不丢失哦 文章目录 一、作品介绍二、作品演示三、代码目录四、网站代码HTML部分代码 五、源码获取 一、作品介绍 &#x1f3f7;️本套采用HTMLCSS&#xff0c;未使用Javacsript代码&#xff0c;共有6个页面。 二、作品演示 三、代…

回调函数,字符函数,字符串函数

前言&#xff1a;上一趴我们学习了指针。那么今天我们来学习新的知识&#xff0c;回调函数&#xff0c;字符函数&#xff0c;字符串函数。 1 回调函数 什么是回调函数呢&#xff1f;回调函数就是通过函数指针调用的函数。 如果你把函数的指针&#xff08;地址&#xff09;作…

【Docker系列】Docker 容器时区设置指南

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

尚硅谷MYSQL(5-6章)

排序和分页 排序 如果没有使用排序操作的话 查询出来的数据是按添加的顺序排序的 ORDER BY是来进行排序的 后面可以添加ASC升序 DESC降序 如果后面没有显示指明排序的方式的话 则默认按照升序排序 where中不能使用列的别名 我们在使用sql语句的时候 她的执行顺序不是从第一…

FastCopy文件快速复制v5.7.15

软件介绍 FastCopy文件快速复制工具。Windows平台上最快的文件复制、删除软件&#xff01;功能强劲&#xff0c;性能优越&#xff01;它是源于日本的高效文件复制加速软件&#xff0c;支持拖拽操作&#xff0c;三种不同HDD模式&#xff1b;支持通配符&#xff0c;任务管理/命令…

微信小程序保存图片到相册

申请权限 代码如下 wx.downloadFile({url: image, //仅为示例&#xff0c;并非真实的资源success(res) {// 只要服务器有响应数据&#xff0c;就会把响应内容写入文件并进入 success 回调&#xff0c;业务需要自行判断是否下载到了想要的内容if (res.statusCode 200) {consol…

XSS Game练习

1.Ma Spaghet 直接get传参 ?somebodyaaaa直接使用img标签 ?somebody<img%20src1%20onerror"alert(1337)">官方文档 应使用innertext&#xff0c;安全性更高 2.Jefff 通过代码可以知道是通过eval的代码执行&#xff0c;setTimeout中的内容表示在一秒后执行…

uniapp预览图片uni.previewImage图片放大

<image v-if"file.image!" :src"file.image" click"previewImage(file.image)"></image>file: {image: ,status: 1}, // 预览 图片previewImage() {uni.previewImage({current: 1,urls: [this.img] // 是个 数组 单张的&#xff08…

JAVA打车小程序APP打车顺风车滴滴车跑腿源码微信小程序打车系统源码

&#x1f697;&#x1f4a8;打车、顺风车、滴滴车&跑腿系统&#xff0c;一键解决出行生活难题&#xff01; 一、出行新选择&#xff0c;打车从此不再难 忙碌的生活节奏&#xff0c;让我们常常需要快速、便捷的出行方式。打车、顺风车、滴滴车系统&#xff0c;正是为了满足…

[C#]winform基于opencvsharp结合Diffusion-Low-Light算法实现低光图像增强黑暗图片变亮变清晰

【训练源码】 https://github.com/JianghaiSCU/Diffusion-Low-Light 【参考源码】 https://github.com/hpc203/Diffusion-Low-Light-onnxrun 【论文地址】 https://arxiv.org/pdf/2306.00306.pdf 【算法原理图】 【效果展示】 【测试环境】 vs2019 netframework4.7.2 …

ffmpeg采用gpu加速增加水印

1.环境需要 系统 windows10 ffmpeg&#xff0c;ffprobe 字体文件 python3以上版本 2.环境配置 从官网上下载ffmpeg版本https://github.com/BtbN/FFmpeg-Builds/releases&#xff0c;这里我用的是这个&#xff0c;解压之后里面包含ffmpeg&#xff0c;ffprobe&#xff0c;f…