当前位置: 首页 > news >正文

Spring Boot集成Redisson布隆过滤器案例

1 什么是布隆过滤器

布隆过滤器实际上是一个非常长的二进制向量(bitmap)和一系列随机哈希函数。那什么又叫哈希函数呢?哈希函数指将哈希表中元素的关键键值通过一定的函数关系映射为元素存储位置的函数。(HashMap源码)

 布隆过滤器的优点:

  • 布隆过滤器存储空间和插入/查询时间都是常数
  • Hash函数相互之间没有关系,方便由硬件并行实现
  • 布隆过滤器不需要存储元素本身,在某些对保密要求非常严格的场合有优势
  • 布隆过滤器可以表示全集,其它任何数据结构都不能

  布隆过滤器的缺点:

  • 有一定的误判率(常见的弥补措施是:建立一个小的白名单,存储那些可能被误判的元素。但是如果元素数量太少,使用散列表足矣。)
  • 一般情况下不能从布隆过滤器中删除元素。()

2 布隆过滤器的作用

布隆过滤器可以用于检索一个元素是否在一个集合中,常用于解决如下问题:

  • 解决Redis缓存穿透
  • 邮件过滤,使用布隆过滤器来做邮件黑名单过滤
  • 解决视频推荐过的不再推荐

3 布隆过滤器的基本原理

  • 首先,建立一个二进制向量,并将所有位设置为0。
  • 然后,选定K个散列函数,用于对元素进行K次散列,计算向量的位下标。
  • 添加元素:当添加一个元素到集合中时,通过K个散列函数分别作用于元素,生成K个值作为下标,并将向量的相应位设置为1。
  • 检查元素:如果要检查一个元素是否存在集合中,用同样的散列方法,生成K个下标,并检查向量的相应位是否全部是1。如果全为1,则该元素很可能在集合中;否则(只要有1个或以上的位为0),该元素肯定不在集合中。

在这里插入图片描述

4 在Spring Boot中集成Redisson实现布隆过滤器 

在这里插入图片描述

 4.1 添加maven依赖

不再需要spring-boot-starter-data-redis依赖,但是都添加也不会报错

<!--redisson-->
<dependency><groupId>org.redisson</groupId><artifactId>redisson-spring-boot-starter</artifactId><version>3.17.0</version>
</dependency>

 4.2 配置yml

spring:datasource:username: xxpassword: xxxxxxdriver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&serverTimezone=CTTcache:type: redisredis:database: 0port: 6379               # Redis服务器连接端口host: localhost          # Redis服务器地址password: xxxxxx         # Redis服务器连接密码(默认为空)timeout: 5000            # 超时时间

 4.3 配置RedissonConfig

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;import java.time.Duration;
import java.util.Random;@EnableCaching
@Configuration
public class RedissonConfig {@Value("${spring.redis.host}")private String host;@Value("${spring.redis.port}")private String port;@Value("${spring.redis.password}")private String password;@Bean(destroyMethod = "shutdown")  // bean销毁时关闭Redisson实例,但不关闭Redis服务public RedissonClient redisson() {//创建配置Config config = new Config();/***  连接哨兵:config.useSentinelServers().setMasterName("myMaster").addSentinelAddress()*  连接集群: config.useClusterServers().addNodeAddress()*/config.useSingleServer().setAddress("redis://" + host + ":" + port).setPassword(password).setTimeout(5000);//根据config创建出RedissonClient实例return Redisson.create(config);}@Beanpublic CacheManager RedisCacheManager(RedisConnectionFactory factory) {RedisSerializer<String> redisSerializer = new StringRedisSerializer();Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);// 解决查询缓存转换异常的问题ObjectMapper om = new ObjectMapper();om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);/*** 新版本中om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL)已经被废弃* 建议替换为om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL)*/om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);jackson2JsonRedisSerializer.setObjectMapper(om);// 配置序列化解决乱码的问题RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()// 设置缓存过期时间  为解决缓存雪崩,所以将过期时间加随机值.entryTtl(Duration.ofSeconds(60 * 60 + new Random().nextInt(60 * 10)))// 设置key的序列化方式.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))// 设置value的序列化方式.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer));// .disableCachingNullValues(); //为防止缓存击穿,所以允许缓存null值RedisCacheManager cacheManager = RedisCacheManager.builder(factory).cacheDefaults(config)// 启用RedisCache以将缓存 put/evict 操作与正在进行的 Spring 管理的事务同步.transactionAware().build();return cacheManager;}
}

4.4 工具类BloomFilterUtil

import org.redisson.api.RBloomFilter;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;import javax.annotation.Resource;@Component
public class BloomFilterUtil {@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;}
}

 4.5 编写service实现层

 其它层正常编写即可,与之前并无差

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.company.springboot.entity.User;
import com.company.springboot.mapper.UserMapper;
import com.company.springboot.service.UserService;
import com.company.springboot.util.BloomFilterUtil;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RedissonClient;
import org.redisson.client.codec.StringCodec;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User>implements UserService {// 预期插入数量static long expectedInsertions = 200L;// 误判率static double falseProbability = 0.01;// 非法请求所返回的JSONstatic String illegalJson = "[\"com.company.springboot.entity.User\",{\"id\":null,\"userName\":\"null\",\"sex\":null,\"age\":null}]";private RBloomFilter<Long> bloomFilter = null;@Resourceprivate BloomFilterUtil bloomFilterUtil;@Resourceprivate RedissonClient redissonClient;@Resourceprivate UserMapper userMapper;@PostConstruct // 项目启动的时候执行该方法,也可以理解为在spring容器初始化的时候执行该方法public void init() {// 启动项目时初始化bloomFilterList<User> userList = this.list();bloomFilter = bloomFilterUtil.create("idWhiteList", expectedInsertions, falseProbability);for (User user : userList) {bloomFilter.add(user.getId());}}@Cacheable(cacheNames = "user", key = "#id", unless = "#result==null")public User findById(Long id) {// bloomFilter中不存在该key,为非法访问if (!bloomFilter.contains(id)) {System.out.println("所要查询的数据既不在缓存中,也不在数据库中,为非法key");/*** 设置unless = "#result==null"并在非法访问的时候返回null的目的是不将该次查询返回的null使用* RedissonConfig-->RedisCacheManager-->RedisCacheConfiguration-->entryTtl设置的过期时间存入缓存。** 因为那段时间太长了,在那段时间内可能该非法key又添加到bloomFilter,比如之前不存在id为1234567的用户,* 在那段时间可能刚好id为1234567的用户完成注册,使该key成为合法key。** 所以我们需要在缓存中添加一个可容忍的短期过期的null或者是其它自定义的值,使得短时间内直接读取缓存中的该值。** 因为Spring Cache本身无法缓存null,因此选择设置为一个其中所有值均为null的JSON,*/redissonClient.getBucket("user::" + id, new StringCodec()).set(illegalJson, new Random().nextInt(200) + 300, TimeUnit.SECONDS);return null;}// 不是非法访问,可以访问数据库System.out.println("数据库中得到数据*****");return userMapper.selectById(id);}// 先执行方法体中的代码,成功执行之后删除缓存@CacheEvict(cacheNames = "user", key = "#id")public boolean delete(Long id) {// 删除数据库中具有的数据,就算此key从此之后不再出现,也不能从布隆过滤器删除return userMapper.deleteById(id) == 1;}// 如果缓存中先前存在,则更新缓存;如果不存在,则将方法的返回值存入缓存@CachePut(cacheNames = "user", key = "#user.id")public User update(User user) {userMapper.updateById(user);// 新生成key的加入布隆过滤器,此key从此合法,因为该更新方法并不更新id,所以也不会产生新的合法的keybloomFilter.add(user.getId());return user;}@CachePut(cacheNames = "user", key = "#user.id")public User insert(User user) {userMapper.insert(user);// 新生成key的加入布隆过滤器,此key从此合法bloomFilter.add(user.getId());return user;}
}

相关文章:

Spring Boot集成Redisson布隆过滤器案例

1 什么是布隆过滤器 布隆过滤器实际上是一个非常长的二进制向量(bitmap)和一系列随机哈希函数。那什么又叫哈希函数呢&#xff1f;哈希函数指将哈希表中元素的关键键值通过一定的函数关系映射为元素存储位置的函数。&#xff08;HashMap源码&#xff09; 布隆过滤器的优点&…...

使用 VSCode SSH 公网远程连接本地服务器开发 - cpolar内网穿透

文章目录 前言视频教程1、安装OpenSSH2、vscode配置ssh3. 局域网测试连接远程服务器4. 公网远程连接4.1 ubuntu安装cpolar内网穿透4.2 创建隧道映射4.3 测试公网远程连接 5. 配置固定TCP端口地址5.1 保留一个固定TCP端口地址5.2 配置固定TCP端口地址5.3 测试固定公网地址远程 转…...

portraiture宿主插件最新v4中文版本下载及使用教程

自拍怎么可以不修图呢&#xff1f;如果要修图的话&#xff0c;磨皮就是其中非常重要的一环。皮肤看起来细腻光滑了&#xff0c;整个人的颜值都会瞬间拉高。下面就让我们介绍一下磨皮用什么软件好用&#xff0c;什么软件可以手动磨皮的相关内容。portraiture是ps人像修图中常用的…...

一. ATR技术指标的定义与运用

一. ATR的定义 1. 什么是ATR ATR英文全名是Average true range&#xff0c;翻译过来就是平均真实波幅&#xff0c;这个指标主要用来衡量最近N天TR(真实波幅)的平均值。 2. ATR相关计算公式 T R [ ( 最高价 − 最低价 ) &#xff0c; ( 前一次收盘价 − 最高价 ) &#xff0…...

linux find帮助文档

以下是完整的find命令帮助文档&#xff1a; 用法&#xff1a;find [-H] [-L] [-P] [-D debugopts] [-Olevel] [起始路径…] [表达式] 选项&#xff1a; -H 跟随命令行符号链接 -L 跟随所有符号链接 -P 不跟随任何符号链接&#xff08;默认&#xff09; -D debugopts 调试标志…...

搜索与图论(acwing算法基础)

文章目录 DFS排列数字n皇后 BFS走迷宫 拓扑序列单链表树与图的深度优先搜索模拟队列有向图的拓扑序列 bellman-ford有边数限制的最短路 spfaspfa求最短路spfa判断负环 FloydFloyd求最短路 PrimPrim算法求最小生成树 KruskalKruskal算法求最小生成树 染色法判定二分图染色法判定…...

【数据结构】何为数据结构。

&#x1f6a9; WRITE IN FRONT &#x1f6a9; &#x1f50e; 介绍&#xff1a;"謓泽"正在路上朝着"攻城狮"方向"前进四" &#x1f50e;&#x1f3c5; 荣誉&#xff1a;2021|2022年度博客之星物联网与嵌入式开发TOP5|TOP4、2021|2022博客之星T…...

【P57】JMeter 保存响应到文件(Save Responses to a file)

文章目录 一、保存响应到文件&#xff08;Save Responses to a file&#xff09;参数说明二、准备工作三、测试计划设计 一、保存响应到文件&#xff08;Save Responses to a file&#xff09;参数说明 可以将结果树保存到文件 使用场景&#xff1a;当结果太大&#xff0c;使…...

Visual Studio 2022 v17.6 正式发布

Visual Studio 17.6 正式发布&#xff0c;这个最新版本提供了一系列强大的工具和功能&#xff0c;旨在使你能够制作出最先进的应用程序。 提高生产力 通过 Visual Studio 2022&#xff0c;目标是帮助你在更短的时间内完成 IDE 内的所有开发任务&#xff0c;在这个版本中&…...

std::chrono时间处理

std::chrono是C11引入的标准库&#xff0c;用于时间的计算和处理。它按照ISO8601标准定义了多个时间类&#xff0c;例如&#xff1a;duration&#xff08;持续时间&#xff09;、time_point&#xff08;时间点&#xff09;和clock&#xff08;时钟&#xff09;。以下是一些常见…...

ieda codeformatV2.xml

ieda codeformatV2.xml 目录概述需求&#xff1a; 设计思路实现思路分析1.codeformatV22.codeformatV23.codeformatV24.codeformatV25.数据处理器 拓展实现 参考资料和推荐阅读 Survive by day and develop by night. talk for import biz , show your perfect code,full busy&…...

Hbase

java客户端 导入maven依赖 XML<dependencies> <dependency> <groupId>org.apache.zookeeper</groupId> <artifactId>zookeeper</artifactId> <version>3.4.6</version> </dependency>…...

[golang 微服务] 5. 微服务服务发现介绍,安装以及consul的使用,Consul集群

一.服务发现介绍 引入 上一节讲解了使用 gRPC创建微服务,客户端的一个接口可能需要调用 N个服务,而不同服务可能存在 不同的服务器,这时&#xff0c;客户端就必须知道所有服务的 网络位置&#xff08;ipport&#xff09;&#xff0c;来进行连接服务器操作,如下图所示: 以往的做…...

【数据结构】哈希应用

目录 一、位图 1、位图概念 2、位图实现 2.1、位图结构 2.2、比特位置1 2.3、比特位置0 2.4、检测位图中比特位 3、位图例题 3.1、找到只出现一次的整数 3.2、找到两个文件交集 3.3、找到出现次数不超过2次的所有整数 二、布隆过滤器 1、布隆过滤器提出 2、布隆过…...

【 Python 全栈开发 - WEB开发篇 - 31 】where条件查询

文章目录 一、where条件查询1.关系运算符查询2.IN关键字查询3.BETWEEN AND关键字查询4.空值查询5.AND关键字查询6.OR关键字查询7.LIKE关键字查询普通字符串含有%通配的字符串含有_通配的字符串 一、where条件查询 MySQL 的 where 条件查询是指在查询数据时&#xff0c;通过 wh…...

Android系统的Ashmem匿名共享内存子系统分析(5)- 实现共享的原理

声明 其实对于Android系统的Ashmem匿名共享内存系统早就有分析的想法&#xff0c;记得2019年6、7月份Mr.Deng离职期间约定一起对其进行研究的&#xff0c;但因为我个人问题没能实施这个计划&#xff0c;留下些许遗憾…文中参考了很多书籍及博客内容&#xff0c;可能涉及的比较…...

谈一谈冷门的C语言爬虫

C语言可以用来编写爬虫程序&#xff0c;但是相对于其他编程语言&#xff0c;C语言的爬虫开发可能会更加复杂和繁琐。因为C语言本身并没有提供现成的爬虫框架和库&#xff0c;需要自己编写网络请求、HTML解析等功能。 不过&#xff0c;如果你对C语言比较熟悉&#xff0c;也可以…...

基于状态的维护(CBM)如何推动设备效率提高?

基于状态的维护&#xff08;Condition-Based Maintenance&#xff0c;CBM&#xff09;是一种先进的维护策略&#xff0c;通过实时监测和分析设备的状态数据&#xff0c;预测设备故障并采取相应的维护措施。CBM基于数据驱动的方法&#xff0c;能够提高设备的可用性、降低维修成本…...

DC LAB8SDC约束四种时序路径分析

DC LAB 1.启动DC2.读入设计3. 查看所有违例的约束报告3.1 report_constraint -all_violators (alias rc)3.2 view report_constraint -all_violators -verbose -significant_digits 4 (打印详细报告) 4.查看时序报告 report_timing -significant_digits 45. 约束组合逻辑(adr_i…...

学生考试作弊检测系统 yolov8

学生考试作弊检测系统采用yolov8网络模型人工智能技术&#xff0c;学生考试作弊检测系统过在考场中安装监控设备&#xff0c;对学生的作弊行为进行实时监测。当学生出现作弊行为时&#xff0c;学生考试作弊检测系统将自动识别并记录信息。YOLOv8 算法的核心特性和改动可以归结为…...

C++实现分布式网络通信框架RPC(3)--rpc调用端

目录 一、前言 二、UserServiceRpc_Stub 三、 CallMethod方法的重写 头文件 实现 四、rpc调用端的调用 实现 五、 google::protobuf::RpcController *controller 头文件 实现 六、总结 一、前言 在前边的文章中&#xff0c;我们已经大致实现了rpc服务端的各项功能代…...

应用升级/灾备测试时使用guarantee 闪回点迅速回退

1.场景 应用要升级,当升级失败时,数据库回退到升级前. 要测试系统,测试完成后,数据库要回退到测试前。 相对于RMAN恢复需要很长时间&#xff0c; 数据库闪回只需要几分钟。 2.技术实现 数据库设置 2个db_recovery参数 创建guarantee闪回点&#xff0c;不需要开启数据库闪回。…...

51c自动驾驶~合集58

我自己的原文哦~ https://blog.51cto.com/whaosoft/13967107 #CCA-Attention 全局池化局部保留&#xff0c;CCA-Attention为LLM长文本建模带来突破性进展 琶洲实验室、华南理工大学联合推出关键上下文感知注意力机制&#xff08;CCA-Attention&#xff09;&#xff0c;…...

React hook之useRef

React useRef 详解 useRef 是 React 提供的一个 Hook&#xff0c;用于在函数组件中创建可变的引用对象。它在 React 开发中有多种重要用途&#xff0c;下面我将全面详细地介绍它的特性和用法。 基本概念 1. 创建 ref const refContainer useRef(initialValue);initialValu…...

解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八

现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet&#xff0c;点击确认后如下提示 最终上报fail 解决方法 内核升级导致&#xff0c;需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...

CMake基础:构建流程详解

目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...

【磁盘】每天掌握一个Linux命令 - iostat

目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat&#xff08;I/O Statistics&#xff09;是Linux系统下用于监视系统输入输出设备和CPU使…...

【C语言练习】080. 使用C语言实现简单的数据库操作

080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...

EtherNet/IP转DeviceNet协议网关详解

一&#xff0c;设备主要功能 疆鸿智能JH-DVN-EIP本产品是自主研发的一款EtherNet/IP从站功能的通讯网关。该产品主要功能是连接DeviceNet总线和EtherNet/IP网络&#xff0c;本网关连接到EtherNet/IP总线中做为从站使用&#xff0c;连接到DeviceNet总线中做为从站使用。 在自动…...

06 Deep learning神经网络编程基础 激活函数 --吴恩达

深度学习激活函数详解 一、核心作用 引入非线性:使神经网络可学习复杂模式控制输出范围:如Sigmoid将输出限制在(0,1)梯度传递:影响反向传播的稳定性二、常见类型及数学表达 Sigmoid σ ( x ) = 1 1 +...