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

Redis 布隆过滤器总结

Redis 布隆过滤器总结

适用场景

大数据判断是否存在来实现去重:这就可以实现出上述的去重功能,如果你的服务器内存足够大的话,那么使用 HashMap 可能是一个不错的解决方案,理论上时间复杂度可以达到 O(1) 的级别,但是当数据量起来之后,还是只能考虑布隆过滤器。

解决缓存穿透:我们经常会把一些热点数据放在 Redis 中当作缓存,例如产品详情。通常一个请求过来之后我们会先查询缓存,而不用直接读取数据库,这是提升性能最简单也是最普遍的做法,但是 如果一直请求一个不存在的缓存,那么此时一定不存在缓存,那就会有大量请求直接打到数据库上,造成 缓存穿透,布隆过滤器也可以用来解决此类问题。

在我们使用 Redis 时候,经常会面临这么一个问题,缓存穿透,意思是数据库和 redis 中都没有数据,缓存和db完全形同虚设。
面对这种问题,我们一般解决办法是设置null 的空值缓存,还有优雅点的实现方式就是布隆过滤器。

空值缓存

    String key = stringRedisTemplate.opsForValue().get("key");if (StringUtil.isEmpty(key)){//查询dbObject k = "test";if (k!=null){//存redisstringRedisTemplate.opsForValue().set("key",k.toString(),100);return k.toString();}else {stringRedisTemplate.opsForValue().set("key","nullstr",10);return "";}}if ("nullstr".equals(key)){stringRedisTemplate.opsForValue().set("key","nullstr",10);return "";}

布隆过滤器

对于恶意攻击,向服务器请求大量不存在的数据造成的缓存穿透,还可以用布隆过滤器先做一次过滤,对于不 存在的数据布隆过滤器一般都能够过滤掉,不让请求再往后端发送。当布隆过滤器说某个值存在时,这个值可 能不存在;当它说不存在时,那就肯定不存在。

在这里插入图片描述
使用布隆过滤器需要把所有数据提前放入布隆过滤器,并且在增加数据时也要往布隆过滤器里放,使用时候不能删除,如果有必须重新初始化

实现原理

布隆过滤器就是一个大型的位数组和几个不一样的无偏 hash 函数。
所谓无偏就是能够把元素的 hash 值算得 比较均匀。

向布隆过滤器中添加 key 时,会使用多个 hash 函数对 key 进行 hash 算得一个整数索引值然后对位数组长度 进行取模运算得到一个位置,每个 hash 函数都会算得一个不同的位置。再把位数组的这几个位置都置为 1 就 完成了 add 操作。

向布隆过滤器询问 key 是否存在时,跟 add 一样,也会把 hash 的几个位置都算出来,看看位数组中这几个位 置是否都为 1,只要有一个位为 0,那么说明布隆过滤器中这个key 不存在。

如果都是 1,这并不能说明这个 key 就一定存在,只是极有可能存在,因为这些位被置为 1 可能是因为其它的 key 存在所致。如果这个位数组 比较稀疏,这个概率就会很大,如果这个位数组比较拥挤,这个概率就会降低。

这种方法适用于数据命中不高、 数据相对固定、 实时性低(通常是数据集较大) 的应用场景, 代码维护较为 复杂, 但是缓存空间占用很少。

布隆过滤器的优点:

  • 时间复杂度低,增加和查询元素的时间复杂为O(N),(N为哈希函数的个数,通常情况比较小)

  • 保密性强,布隆过滤器不存储元素本身

  • 存储空间小,如果允许存在一定的误判,布隆过滤器是非常节省空间的(相比其他数据结构如Set集合)

布隆过滤器的缺点:

  • 有点一定的误判率,但是可以通过调整参数来降低
  • 无法获取元素本身
  • 很难删除元素

数据结构

布隆过滤器它实际上是一个很长的二进制向量和一系列随机映射函数。以Redis中的布隆过滤器实现为例,Redis中的布隆过滤器底层是一个大型位数组(二进制数组)+多个无偏hash函数。
一个大型位数组(二进制数组):
在这里插入图片描述

多个无偏hash函数:
无偏hash函数就是能把元素的hash值计算的比较均匀的hash函数,能使得计算后的元素下标比较均匀的映射到位数组中。

如下就是一个简单的布隆过滤器示意图,其中k1、k2代表增加的元素,a、b、c即为无偏hash函数,最下层则为二进制数组。

在这里插入图片描述

增加元素的步骤:

通过k个无偏hash函数计算得到k个hash值
依次取模数组长度,得到数组索引
将计算得到的数组索引下标位置数据修改为1
查询元素的步骤:
布隆过滤器最大的用处就在于判断某样东西一定不存在或者可能存在,而这个就是查询元素的结果。其查询元素的过程如下:

通过k个无偏hash函数计算得到k个hash值
依次取模数组长度,得到数组索引
判断索引处的值是否全部为1,如果全部为1则存在(这种存在可能是误判),如果存在一个0则必定不存在
误判的情况: hash函数无法完全避免hash冲突,可能会存在多个元素计算的hash值是相同的,那么它们取模数组长度后的到的数组索引也是相同的,这就是误判的原因。例如彭于晏和程序员(莫打我)的hash值取模后得到的数组索引都是1,但实际上存储的只有彭于晏,如果此时判断程序员在不在这里,误判就出现啦!因此布隆过滤器最大的缺点误判只要知道其判断元素是否存在的原理就很容易明白了!

布隆过滤器不支持删除元素。

代码演示

使用redisson

可以用redisson实现布隆过滤器,引入依赖

 <dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.6.5</version></dependency>

实例代码

单Redis节点模式

 Config config = new Config();config.useSingleServer().setAddress("redis://localhost:6379");//构造RedissonRedissonClient redisson = Redisson.create(config);RBloomFilter<String> bloomFilter = redisson.getBloomFilter("nameList");//初始化布隆过滤器:预计元素为 100000L ,误差率为3%,根据这两个参数会计算出底层的bit数组大小bloomFilter.tryInit(100000L,0.03);//将 test 插入到布隆过滤器中bloomFilter.add("test");bloomFilter.add("test2");//判断下面号码是否在布隆过滤器中System.out.println(bloomFilter.contains("test3"));//falseSystem.out.println(bloomFilter.contains("test4"));//falseSystem.out.println(bloomFilter.contains("test2"));//trueSystem.out.println(bloomFilter.count()); // 2个元素System.out.println(bloomFilter.getSize()); // 长度

yaml 配置

---
singleServerConfig:idleConnectionTimeout: 10000connectTimeout: 10000timeout: 3000retryAttempts: 3retryInterval: 1500password: nullsubscriptionsPerConnection: 5clientName: nulladdress: "redis://127.0.0.1:6379"subscriptionConnectionMinimumIdleSize: 1subscriptionConnectionPoolSize: 50connectionMinimumIdleSize: 32connectionPoolSize: 64database: 0dnsMonitoringInterval: 5000
threads: 0
nettyThreads: 0
codec: !<org.redisson.codec.JsonJacksonCodec> {}
"transportMode":"NIO"

集群配置

Config config = new Config();
config.useClusterServers().setScanInterval(2000) // 集群状态扫描间隔时间,单位是毫秒//可以用"rediss://"来启用SSL连接.addNodeAddress("redis://127.0.0.1:7000", "redis://127.0.0.1:7001").addNodeAddress("redis://127.0.0.1:7002");RedissonClient redisson = Redisson.create(config);

yaml配置

配置集群模式可以通过指定一个YAML格式的文件来实现。以下是YAML格式的配置文件样本。文件中的字段名称必须与ClusterServersConfig和Config对象里的字段名称相符。

---
clusterServersConfig:idleConnectionTimeout: 10000connectTimeout: 10000timeout: 3000retryAttempts: 3retryInterval: 1500password: nullsubscriptionsPerConnection: 5clientName: nullloadBalancer: !<org.redisson.connection.balancer.RoundRobinLoadBalancer> {}slaveSubscriptionConnectionMinimumIdleSize: 1slaveSubscriptionConnectionPoolSize: 50slaveConnectionMinimumIdleSize: 32slaveConnectionPoolSize: 64masterConnectionMinimumIdleSize: 32masterConnectionPoolSize: 64readMode: "SLAVE"nodeAddresses:- "redis://127.0.0.1:7004"- "redis://127.0.0.1:7001"- "redis://127.0.0.1:7000"scanInterval: 1000
threads: 0
nettyThreads: 0
codec: !<org.redisson.codec.JsonJacksonCodec> {}
"transportMode":"NIO"

哨兵配置

Config config = new Config();
config.useSentinelServers().setMasterName("mymaster")//可以用"rediss://"来启用SSL连接.addSentinelAddress("127.0.0.1:26389", "127.0.0.1:26379").addSentinelAddress("127.0.0.1:26319");RedissonClient redisson = Redisson.create(config);

yaml
配置哨兵模式可以通过指定一个YAML格式的文件来实现。以下是YAML格式的配置文件样本。文件中的字段名称必须与SentinelServersConfig和Config对象里的字段名称相符。

---
sentinelServersConfig:idleConnectionTimeout: 10000connectTimeout: 10000timeout: 3000retryAttempts: 3retryInterval: 1500password: nullsubscriptionsPerConnection: 5clientName: nullloadBalancer: !<org.redisson.connection.balancer.RoundRobinLoadBalancer> {}slaveSubscriptionConnectionMinimumIdleSize: 1slaveSubscriptionConnectionPoolSize: 50slaveConnectionMinimumIdleSize: 32slaveConnectionPoolSize: 64masterConnectionMinimumIdleSize: 32masterConnectionPoolSize: 64readMode: "SLAVE"sentinelAddresses:- "redis://127.0.0.1:26379"- "redis://127.0.0.1:26389"masterName: "mymaster"database: 0
threads: 0
nettyThreads: 0
codec: !<org.redisson.codec.JsonJacksonCodec> {}
"transportMode":"NIO"

主从模式

Config config = new Config();
config.useMasterSlaveServers()//可以用"rediss://"来启用SSL连接.setMasterAddress("redis://127.0.0.1:6379").addSlaveAddress("redis://127.0.0.1:6389", "redis://127.0.0.1:6332", "redis://127.0.0.1:6419").addSlaveAddress("redis://127.0.0.1:6399");RedissonClient redisson = Redisson.create(config);

yaml
配置主从模式可以通过指定一个YAML格式的文件来实现。以下是YAML格式的配置文件样本。文件中的字段名称必须与MasterSlaveServersConfig和Config对象里的字段名称相符。

---
masterSlaveServersConfig:idleConnectionTimeout: 10000connectTimeout: 10000timeout: 3000retryAttempts: 3retryInterval: 1500failedAttempts: 3password: nullsubscriptionsPerConnection: 5clientName: nullloadBalancer: !<org.redisson.connection.balancer.RoundRobinLoadBalancer> {}slaveSubscriptionConnectionMinimumIdleSize: 1slaveSubscriptionConnectionPoolSize: 50slaveConnectionMinimumIdleSize: 32slaveConnectionPoolSize: 64masterConnectionMinimumIdleSize: 32masterConnectionPoolSize: 64readMode: "SLAVE"slaveAddresses:- "redis://127.0.0.1:6381"- "redis://127.0.0.1:6380"masterAddress: "redis://127.0.0.1:6379"database: 0
threads: 0
nettyThreads: 0
codec: !<org.redisson.codec.JsonJacksonCodec> {}
"transportMode":"NIO"

详细配置 https://github.com/redisson/redisson/wiki/2.-%E9%85%8D%E7%BD%AE%E6%96%B9%E6%B3%95#241-%E9%9B%86%E7%BE%A4%E8%AE%BE%E7%BD%AE

使用Google 开源的 Guava 中自带的布隆过滤器

依赖

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

代码实例

// 创建布隆过滤器对象BloomFilter<Integer> filter = BloomFilter.create(Funnels.integerFunnel(),1000,0.01);
// 判断指定元素是否存在System.out.println(filter.mightContain(1));System.out.println(filter.mightContain(2));
// 将元素添加进布隆过滤器filter.put(1);filter.put(2);System.out.println(filter.mightContain(1));System.out.println(filter.mightContain(2));

在我们的示例中,当 mightContain() 方法返回 true 时,我们可以 99% 确定该元素在过滤器中,当过滤器返回 false 时,我们可以 100% 确定该元素不存在于过滤器中。

Guava 提供的布隆过滤器的实现还是很不错的 ,但是它是单机使用 ,如果是分布式的场景,需要用到 Redis 中的布隆过滤器了

相关文章:

Redis 布隆过滤器总结

Redis 布隆过滤器总结 适用场景 大数据判断是否存在来实现去重&#xff1a;这就可以实现出上述的去重功能&#xff0c;如果你的服务器内存足够大的话&#xff0c;那么使用 HashMap 可能是一个不错的解决方案&#xff0c;理论上时间复杂度可以达到 O(1) 的级别&#xff0c;但是…...

云基础设施安全:7个保护敏感数据的最佳实践

导语&#xff1a;云端安全防护进行时&#xff01; 您的组织可能会利用云计算的实际优势&#xff1a;灵活性、快速部署、成本效益、可扩展性和存储容量。但是&#xff0c;您是否投入了足够的精力来确保云基础设施的网络安全&#xff1f; 您应该这样做&#xff0c;因为数据泄露、…...

centos7安装nginx

1.配置环境 1).gcc yum install -y gcc2).安装第三方库 pcre-devel yum install -y pcre pcre-devel3).安装第三方库 zlib yum install -y zlib zlib-devel2.下载安装包并解压 nginx官网下载&#xff1a;http://nginx.org/en/download.html 或者 使用wget命令进行下载 wg…...

PyQt5 基础篇(一)-- 安装与环境配置

1 PyQt5 图形界面开发工具 Qt 库是跨平台的 C 库的集合&#xff0c;是最强大的 GUI 库之一&#xff0c;可以实现高级 API 来访问桌面和移动系统的各种服务。PyQt5 是一套 Python 绑定 Digia QT5 应用的框架。PyQt5 实现了一个 Python模块集&#xff0c;有 620 个类&#xff0c;…...

Java—JDK8新特性—函数式接口【内含思维导图】

目录 3.函数式接口 思维导图 3.1 什么是函数式接口 3.2 functionalinterface注解 源码分析 3.3 Lambda表达式和函数式接口关系 3.4 使用函数式接口 3.5 内置函数式接口 四大核的函数式接口区别 3.5.1 Supplier 函数式接口源码分析 3.5.2 Supplier 函数式接口使用 3.…...

【MySQL】外键约束和外键策略

一、什么是外键约束&#xff1f; 外键约束&#xff08;FOREIGN KEY&#xff0c;缩写FK&#xff09;是用来实现数据库表的参照完整性的。外键约束可以使两张表紧密的结合起来&#xff0c;特别是针对修改或者删除的级联操作时&#xff0c;会保证数据的完整性。 外键是指表…...

3. SQL底层执行原理详解

一条SQL在MySQL中是如何执行的 1. MySQL的内部组件结构1.1 Server层1.2 Store层 2. 连接器3. 分析器4. 优化器5. 执行器6. bin-log归档 本文是按照自己的理解进行笔记总结&#xff0c;如有不正确的地方&#xff0c;还望大佬多多指点纠正&#xff0c;勿喷。 1. MySQL的内部组件结…...

Bus动态刷新

Bus动态刷新全局广播配置实现 启动 EurekaMain7001ConfigcenterMain3344ConfigclientMain3355ConfigclicntMain3366 运维工程师 修改Gitee上配置文件内容&#xff0c;增加版本号发送POST请求curl -X POST "http://localhost:3344/actuator/bus-refresh" —次发送…...

逆波兰式的写法

一、什么是波兰式&#xff0c;逆波兰式和中缀表达式 6 *(37) -2 将运算数放在数值中间的运算式叫做中缀表达式 - * 6 3 7 2 将运算数放在数值前间的运算式叫做前缀表达式 6 3 7 * 2 - 将运算数放在数值后间的运算式叫做后缀表达式 二、生成逆波兰表达式 6 *(37) -2 生成…...

Linux系统日志介绍

Linux系统日志都是放在“/var/log”目录下面&#xff0c;各个日志文件的功能&#xff1a; /var/log/messages — 包括整体系统信息&#xff0c;其中也包含系统启动期间的日志。此外&#xff0c;mail&#xff0c;cron&#xff0c;daemon&#xff0c;kern和auth等内容也记录在va…...

第三十二章 React路由组件的简单使用

1、NavLink的使用 一个特殊版本的 Link&#xff0c;当它与当前 URL 匹配时&#xff0c;为其渲染元素添加样式属性 <NavLink className"list-group-item" to"/home">Home</NavLink> <NavLink className"list-group-item" to&quo…...

“裸奔”时代下,我们该如何保护网络隐私?

当我们在互联网上进行各种活动时&#xff0c;我们的个人信息和数据可能会被攻击者窃取或盗用。为了保护我们的隐私和数据安全&#xff0c;以下是一些实用的技巧和工具&#xff0c;可以帮助您应对网络攻击、数据泄露和隐私侵犯的问题&#xff1a; 使用强密码&#xff1a;使用独特…...

c#笔记-方法

方法 方法定义 方法可以将一组复杂的代码进行打包。 声明方法的语法是返回类型 方法名 括号 方法体。 void Hello1() {for (int i 0; i < 10; i){Console.WriteLine("Hello");} }调用方法 方法的主要特征就是他的括号。 调用方法的语法是方法名括号。 He…...

054、牛客网算法面试必刷TOP101--堆/栈/队列(230509)

文章目录 前言堆/栈/队列1、BM42 用两个栈实现队列2、BM43 包含min函数的栈3、BM44 有效括号序列4、BM45 滑动窗口的最大值5、BM46 最小的K个数6、BM47 寻找第K大7、BM48 数据流中的中位数8、BM49 表达式求值 其它1、se基础 前言 提示&#xff1a;这里可以添加本文要记录的大概…...

怎么让chatGTP写论文-chatGTP写论文工具

chatGTP如何写论文 ChatGPT是一个使用深度学习技术训练的自然语言处理模型&#xff0c;可以用于生成自然语言文本&#xff0c;例如对话、摘要、文章等。作为一个人工智能技术&#xff0c;ChatGPT可以帮助你处理一些文字内容&#xff0c;但并不能代替人类的创造性思考和判断。以…...

springboot 断点上传、续传、秒传实现

文章目录 前言一、实现思路二、数据库表对象二、业务入参对象三、本地上传实现三、minio上传实现总结 前言 springboot 断点上传、续传、秒传实现。 保存方式提供本地上传&#xff08;单机&#xff09;和minio上传&#xff08;可集群&#xff09; 本文主要是后端实现方案&…...

2023河南省赛vp题解

目录 A题&#xff1a; B题 C题 D题 E题 F题 G题 H题 I题 J题 K题 L题 A题&#xff1a; 1.思路&#xff1a;考虑暴力枚举和双hash&#xff0c;可以在O(n)做完。 2.代码实现&#xff1a; #include<bits/stdc.h> #define sz(x) (int) x.size() #define rep(i,z,…...

港科夜闻|香港科大与香港资管通有限公司签署校企合作备忘录,成立校企合作基金促科研成果落地...

关注并星标 每周阅读港科夜闻 建立新视野 开启新思维 1、香港科大与香港资管通有限公司签署校企合作备忘录&#xff0c;成立校企合作基金促科研成果落地。“港科资管通领航基金”28日在香港成立&#xff0c;将致力于推动高校科研成果转化&#xff0c;助力香港国际创科中心建设。…...

Neo4j 笔记

启动命令 neo4j console Cypher句法由四个不同的部分组成&#xff0c; 每一部分都有一个特殊的规则&#xff1a; start——查找图形中的起始节点。 match——匹配图形模式&#xff0c; 可以定位感兴趣数据的子图形。 where——基于某些标准过滤数据。 return——返回感兴趣的…...

数据库基础应用——概念模型

1、实体(Entity) 客观存在并可相互区别的事物称为实体。实体可以是人、物、对象、概念、事物本身、事物之间的联系。&#xff08;例如一名员工、一个部门、一辆汽车等等。&#xff09; 2、属性(Attributre) 实体所具有的每个特性称为属性。&#xff08;例如&#xff1a;员工由员…...

【学姐面试宝典】前端基础篇Ⅴ——JS深浅拷贝、箭头函数、事件监听等

前言 博主主页&#x1f449;&#x1f3fb;蜡笔雏田学代码 专栏链接&#x1f449;&#x1f3fb;【前端面试专栏】 今天继续学习前端面试题相关的知识&#xff01; 感兴趣的小伙伴一起来看看吧~&#x1f91e; 文章目录 什么是事件监听事件委托以及冒泡原理介绍一下 promise&#…...

最新研究,GPT-4暴露了缺点!无法完全理解语言歧义!

夕小瑶科技说 原创作者 |智商掉了一地、Python自然语言推理&#xff08;Natural Language Inference&#xff0c;NLI&#xff09;是自然语言处理中一项重要任务&#xff0c;其目标是根据给定的前提和假设&#xff0c;来判断假设是否可以从前提中推断出来。然而&#xff0c;由于…...

商业数据挖掘-第一章-数据探索式分析-1

数据探索最基本的步骤之一是获取对数据的基本描述,通过获取对数据的基本描述从而获得对数据的基本感觉。下面的一些方法用于帮助我们认识数据。 我们使用波士顿房价预测的数据集进行实验 DataFrame.describe():查看数据的基本分布,具体是对每列数据进行统计,统计值包含频…...

MybatisPlus是否防止SQL注入?

问 如果我希望使用mybatisplus同时也进行防SQL注入操作&#xff0c;应该怎么处理&#xff1f; 答 如果你想在使用 MyBatis-Plus 进行数据库操作的同时也进行防 SQL 注入处理&#xff0c;可以采用以下两种方式&#xff1a; 使用 #{} 占位符&#xff1a;在 QueryWrapper 或 Up…...

5月第1周榜单丨飞瓜数据B站UP主排行榜(哔哩哔哩平台)发布!

飞瓜轻数发布2023年5月1日-5月7日飞瓜数据UP主排行榜&#xff08;B站平台&#xff09;&#xff0c;通过充电数、涨粉数、成长指数三个维度来体现UP主账号成长的情况&#xff0c;为用户提供B站号综合价值的数据参考&#xff0c;根据UP主成长情况用户能够快速找到运营能力强的B站…...

数据的插入删除和更新

在之前我们就已经学过了数据的插入&#xff0c;在这里再进行一点内容的补充&#xff1a; 在insert语句中&#xff0c;value子句中参数的顺序与表中各个列的顺序是一一对应的。 mysql> insert into first_table(second_column, first_column) values(aaa, 1); Query OK, 1 r…...

C# byte[] 与 int 类型互转

本文讲述在C#中,怎样使用 BitConverter 类将字节数组转换为 int 然后又转换回字节数组的过程。 为什么需要这样呢&#xff1f;这是因为&#xff0c;比如说,在从网络读取字节之后&#xff0c;可能需要将字节转换为内置数据类型。 除了示例中的 ToInt32(Byte[], Int32) 方法之外…...

MySQL---多表联合查询(上)(多表关系、外键约束、学生成绩多表关系、交叉连接查询)

1. 多表关系 MySQL多表之间的关系可以概括为&#xff1a; 一对一&#xff1a; 比如&#xff1a;一个学生只有一张身份证&#xff1b;一张身份证只能对应一学生。 实现原则&#xff1a;在任一表中添加唯一外键&#xff0c;指向另一方主键&#xff0c;确保一对一关系。 一般一对…...

【iOS】—— RunLoop线程常驻和线程保活

文章目录 没有线程常驻会怎么样&#xff1f; 线程常驻线程保活 没有线程常驻会怎么样&#xff1f; 我们一般写一个子线程&#xff0c;子线程执行完分配的任务后就会自动销毁&#xff0c;比如下面这个情况&#xff1a; 我们先重写一下NSThread里面的dealloc方法&#xff0c;打印…...

Springcloud--docker快速入门

认识docker docker相关操作 1.初识Docker 1.1.什么是Docker 微服务虽然具备各种各样的优势&#xff0c;但服务的拆分通用给部署带来了很大的麻烦。 分布式系统中&#xff0c;依赖的组件非常多&#xff0c;不同组件之间部署时往往会产生一些冲突。在数百上千台服务中重复部署…...