一篇吃透布隆过滤器(Bloom Filter)及其使用场景

news2024/11/23 21:20:27

目录

1、什么是布隆过滤器

2、布隆过滤器的原理

2.1 布隆过滤器的数据结构

2.2 布隆过滤器的检索和插入原理

2.3 布隆过滤器元素的修改和删除

3、布隆过滤器的使用场景

3.1 Redis通过布隆过滤器防止缓存穿透

3.2 RocketMQ通过布隆过滤器防止消息重复消费

4、布隆过滤器优缺点

4.1 优点:

4.2 缺点:


1、什么是布隆过滤器

以下定义来自百度百科:

布隆过滤器(Bloom Filter)是1970年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都比一般的算法要好的多,缺点是有一定的误识别率和删除困难。

从上述定义我们可以得到以下关键信息:

  1. 布隆过滤器是由很长的二进制向量(即可以理解成很长的0、1数组)与一系列随机映射函数(Hash函数)构成。
  2. 布隆过滤器的作用是检索一个元素是否存在我们的集合之中。
  3. 优点是空间效率和查询时间都比一般的算法要好的多;缺点是有一定的误识别率和删除困难。

(*^▽^*)OK,那我们就根据这个定义得到的信息来详细的讲解我们的布隆过滤器。请大家耐心观看~

注意!接下来的原理我们都会基于定义来逐句解释和讲解布隆过滤器,请大家记住上述三点关键信息。

2、布隆过滤器的原理

2.1 布隆过滤器的数据结构

我们从定义总结的关键信息可知:布隆过滤器是由很长的二进制向量(即可以理解成很长的0、1数组)与一系列随机映射函数(Hash函数)构成。因此我们可以将布隆过滤器理解成下图这种很长的一个二进制数组:

正是由于布隆过滤器的数据结构仅需要存储“0”或“1”,因此所占用内存极少,这也是布隆过滤器的一大优点。 

2.2 布隆过滤器的检索和插入原理

从上图布隆过滤器的数据结构图和定义的关键信息我们可以知道:布隆过滤器实际上是个很长的二进制数组,作用是检索一个元素是否存在我们的集合之中。那么布隆过滤器是如何通过上述数据结构判断一个元素是否存在我们的集合之中的呢?

这里就需要用到我们定义中提到的“一系列随机映射函数(Hash函数)”了。

首先我们需要知道什么是Hash函数?这里我们给出Hash函数的一个简要的说明:

简单来说Hash函数就是把输入值通过特定方式(hash函数) 处理后 生成一个值,这个值等同于存放数据的地址。

比如我们当前的Hash函数是 y=x²&(len-1),这里y是指最终在布隆过滤器的数据结构(二进制数组)中存放的下标位置,x指我们传入的值,len指数组的长度。那么如果当数组长度为100(举个例子,实际上数组长度是很长的),传入的值为5,则我们通过Hash函数得到的下标为25。那么此时我们便将下标25的值从0标为1。这就是插入(增加)数据的原理。

插入(增加)数据流程图如下:

那么,当我们下次再输入这个值的时候,我们会得到当前数组对应下标的值为1,说明我们有这个数字!这是不是就是检索的原理了!

检索流程图如下:

当然,这里有基础的同学肯定会发现我们上述说的过程虽然很简单,但是存在很大的问题:

①存在Hash冲突导致误判:

首先我们先对Hash冲突作一个简要说明:

根据key(键值)即经过一个Hash函数F(key)得到的结果的作为地址去存放当前的value值,但是却发现算出来的地址上已经被占用了,这就是所谓的hash冲突。

我们基于上述例子继续讲,在经过上述流程后,我们数组下标为25的值是1。此时我们传入一个值25,那么通过我们上述举例的Hash算法计算后得到的数组下标值也为25,那么此时布隆过滤器是不是就会认为值25是存在的!但是实际上是因为5和25经过Hash映射后得到同一个地址,导致了误判

当然,这么简单的问题伟大的“布隆先生”肯定不会犯如此“低级”的错误,因此解决方法就和我们上述定义中的关键字“一系列Hash函数”有关了。

我们通过“一系列的Hash函数”,比如Hash函数①y=x²&(len-1)②y=(2*x)&(len-1) ③y=(x+x²)&(len-1) 这三个Hash函数一起来决定某个元素是否存在我们的集合中。

也就是检索流程变为:将key值传入一系列Hash函数得到对应的一系列数组地址(索引下标),注意这里一般来说有几个Hash函数就会得到几个地址,然后去判断这几个索引下标对应的值是否均为1,是的话则说明存在,否则不存在。

上述才是布隆过滤器检索元素是否存在的真正流程,检索元素流程图因此对应变化如下: 

插入元素流程变为:根据一系列Hash函数得到一系列地址,将对应地址下标值改为1,流程图如下:

当然,我们从布隆过滤器定义中提到的缺点可以知道:布隆过滤器会有一定误判率。说明即使是在一系列Hash函数下,依然会有巧合:“一个不存在的元素,对应的一系列映射后的地址的值为1,即出现误判”。这也是无法避免的事情,毕竟如果数据量很大的话,很难防止有一定量的、不存在的“幸运儿”能通过布隆过滤器的筛选。

当然,我们在使用布隆过滤器的时候能通过设置两个参数:①预期数据量 ②误判率期望值。我们可以通过设置“误判率期望值”来达到我们能接受的误判率。

当然!大家别异想天开:“哎呀,那我设置0不就行了吗?”这肯定是不可能的,而且设置的误判率越低,数据量越大,占用内存则越大,运行时间则越慢!这也很好理解:数据量越大肯定占用越多内存空间,误判率越低则说明要越多的Hash函数来进行运算,则运行时间越慢,一个key对应的地址也多了,肯定占用越多内存空间。

2.3 布隆过滤器元素的修改和删除

我们从定义可以知道:我们想要修改或删除一个元素时,同时去保证布隆过滤器不受影响是几乎不可能的。

为什么这样说呢,由于我们在插入元素时,不同的值可能经过一系列Hash函数后得到的一系列地址,总有可能两个或多个值经过某个Hash函数映射后得到其中一个地址会一样,此时数组中对应的下标肯定为1,当我们删除或修改某个元素后,我们如果想将其原来对应地址的值从1改为0后,无法确定这个地址是否也对应其他值,如果贸然修改,可能会导致其他原本存在的值在检索时返回不存在的情况!这种情况是极其危险的,可能会导致数据的“逻辑丢失”。

因此我们这里不讨论修改和删除的情况。因为布隆过滤器对元素的删除不太支持,目前有一些变形的特定布隆过滤器支持元素的删除。

3、布隆过滤器的使用场景

3.1 Redis通过布隆过滤器防止缓存穿透

首先我们需要知道什么是缓存穿透,这里我们给出缓存穿透的定义。当然大家也可以去我主页看我关于Redis缓存写的文章:点我!!!传送门!!!

Redis缓存穿透指访问一个缓存和数据库中都不存在的key,由于这个key在缓存中不存在,则会到数据库中查询,数据库中也不存在该key,无法将数据添加到缓存中,所以每次都会访问数据库导致数据库压力增大。

我们可以在访问Redis之前使用布隆过滤器来对请求的key进行过滤, 可以大大减少那些恶意攻击。当然,会存在一定误判率,但是使用布隆过滤器后,“不法分子”肯定对我们服务器就没那么容易进行恶意攻击了。

3.2 RocketMQ通过布隆过滤器防止消息重复消费

为了防止RocketMQ消息重复消费,我们发送消息时可以对每个消息设置唯一的key,然后在消费者处利用布隆过滤器对消息的key检索,如果存在则说明消息已经消费过,不消费。不存在则进行消费,然后插入布隆过滤器。

当然,上面两个例子仅仅是举的例子,布隆过滤器能使用的地方很多,只要但凡涉及“数据过滤”均可以考虑使用“布隆过滤器”来实现。

4、布隆过滤器优缺点

4.1 优点:

  • 时间复杂度低,增加和查询元素的时间复杂为O(N),(N为哈希函数的个数,通常情况比较小)
  • 保密性强,布隆过滤器不存储元素本身
  • 存储空间小,如果允许存在一定的误判,布隆过滤器是非常节省空间的(相比其他数据结构如Set、Map集合)

4.2 缺点:

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

以上是布隆过滤器的介绍,大家一键三连就是对我最大的支持!谢谢大家!

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

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

相关文章

DAPP开发(三)——智能合约开发

智能合约 Remix IDE 是开发以太坊智能合约的在线IDE工具,部署简单的智能合约非常方便。 http://remix.ethereum.org truffle 一个世界级的智能合约开发框架,专为智能合约而生。 管理智能合约的生命周期自动化合约测试可编程,可部署&…

linux安装jupyter notebook

目录 使用miniconda的conda安装 切换conda镜像源有两种方法: 设置密码: 修改配置文件: 启动 关闭进程: 使用miniconda的conda安装 conda install jupyter 如果镜像不好用则切换conda镜像源 切换conda镜像源有两种方法: 1. [shuqiqshuqiq bin]$ ./conda config --add…

MyBatis - 基础使用Ⅰ

这篇文章将讲解MyBatis的基础使用,MyBatis的学习是非常重要的,在前面学习servlet的时候,我们就能感受到将数据持久化存储的重要性,当时在使用JDBC的时候非常繁琐麻烦,但是在Spring里,提供了一种框架可以轻松…

真题详解(传引用)-软件设计(七十五)

真题详解(补码转换)-软件设计(七十四)https://blog.csdn.net/ke1ying/article/details/130674214 分治算法技术设计______。 答案:1、问题划分 2、递归求解 3、合并解 虚拟存储体系_____两级构成。 解析:主存 和 辅…

vue项目打包成桌面应用并修改图标

目录 1. 打包为桌面应用 2.修改图标 1. 打包为桌面应用 1.在vux项目的终端执行打包 npm run build 2.会在项目文件夹里面出现一个dist文件夹 里面有这几个文件组成 3.在这里需要添加一个 package.json 文件 package.json 内容 {"name": "鼠标放图标上面的提…

环形链表解释约瑟夫问题

环形链表解释约瑟夫问题 来自尚硅谷开放课程的迁移学习,致敬尚硅谷的各位老师,受益匪浅!!! 单向链表,双向链表,环形链表对比介绍 单向链表、双向链表和环形链表都是常见的链表数据结构&#…

介绍如何在 MySQL 中创建新用户并授予权限?

MySQL 是一个开源的关系型数据库管理系统,常用于存储和管理大量的结构化数据。在使用 MySQL 进行数据管理时,为了安全和方便管理,通常需要创建新用户并授予相应的权限。本文将介绍如何在 MySQL 中创建新用户并授予权限的方法。 创建新用户 在…

第十一章结构性模式—组合模式

文章目录 组合模式解决的问题概念结构 实例组合模式的分类优点使用场景 结构型模式描述如何将类或对象按某种布局组成更大的结构,有以下两种: 类结构型模式:采用继承机制来组织接口和类。 对象结构型模式:釆用组合或聚合来组合对…

shell脚本之“awk“命令

文章目录 1.awk工作原理2.awk命令演示操作部分2.1 按行输出文本演示操作2.2 BEGIN模式演示操作2.3 按字段输出文本演示操作2.4 通过管道、双引号调用Shell命令2.5 date命令演示操作2.6 getline参数详解2.7 awk命令的数组操作 3. 总结 1.awk工作原理 逐行读取文本,默…

位域和字节对齐

结构体中的位域 位域是一种特殊的结构体成员,它允许将一个字节或多个字节中的每个位作为一个独立的成员来使用。位域的语法形式为: struct {type [member_name] : width; }; 其中,type 表示位域成员的类型,可以是 int、unsigne…

网络编程——TCP编程

TCP编程 流程服务器客户端 函数接口1、socket2、bind3、listen4、accept5、recv6、send7、connet 实现双工通信server.ccelient.c优化代码 流程 在C语言中进行TCP编程的一般步骤如下: (1)包含头文件: 在代码中包含必要的头文件&a…

面对象QgsPolygon

几何对象中的面用QgsPolygon进行封装,也称为多边形简单的多边形是由一串点连接而成,并首尾闭合多边形的结构更复杂,除了有一个外部轮廓,还可能包括内部多个轮廓 创建面对象 QgsPolygon() #创建一个空的面 使用setExteriorRing设…

Spring AOP 实践指南

Spring AOP 实践指南 文章目录 Spring AOP 实践指南一、概述1、简介2、官方资料3、本文档说明 二、基本使用1、引入依赖2、定义切面3、定义切点4、创建 HelloController5、启动项目,访问测试 三、通知1、概述五种通知通知的顺序 2、通知方法接受的参数3、前置通知代…

Thread线程学习(1) 了解线程的基本知识——什么是线程

本专栏将记录有关线程方面的知识 在计算机科学领域中,线程(Thread)是一种执行计算机程序的基本单元。对于初学者来说,理解线程是学习并发编程的关键一步。本文将带你了解线程的基础知识,包括线程的定义、线程与进程的关…

GPT神奇应用:给孩子做每日安排

正文共 1163 字,阅读大约需要 4 分钟 家长必备技巧,您将在4分钟后获得以下超能力: 快速生成每日安排计划 Beezy评级 :B级 *经过简单的寻找, 大部分人能立刻掌握。主要节省时间。 推荐人 | Kim 编辑者 | Linda ●图片…

JavaScript实现打印倒金字塔的代码

以下为实现打印倒金字塔的程序代码和运行截图 目录 前言 一、实现打印倒金字塔 1.1 运行流程及思想 1.2 代码段 1.3 JavaScript语句代码 1.4 运行截图 前言 1.若有选择,您可以在目录里进行快速查找; 2.本博文代码可以根据题目要求实现相关使用功…

动态规划专题

动态规划专题 最长递增子序列LeetCode 300. 最长递增子序列解题思路代码实现 LeetCode 354. 俄罗斯套娃信封问题解题思路代码实现 总结 不要纠结,干就完事了,熟练度很重要!!!多练习,多总结!&…

【Redis】Redis中bitmap的原理和使用

文章目录 一、原理二、BitMap 相关命令三、BitMap 空间计算四、使用场景1. 用户签到2. 统计活跃用户(用户登陆情况)3. 统计用户在线状态4. 实现布隆过滤器 五、总结 一、原理 先声明一下:Redis 有5种数据类型,而 BitMap 在 Redis…

【k8s】Ubuntu22.04离线部署k8s集群:搭建软件仓库和镜像仓库(repo节点)

上两篇主要记录了在CentOS 7环境中离线部署k8s的方案,本篇继续介绍方案二在Ubuntu 22.04的实现。(当然,整体思路还是跟上篇基本相似) 目录 Ubuntu22.04离线部署k8s集群:搭建软件仓库和镜像仓库(repo节点&am…

总结852

学习目标: 月目标:5月(张宇强化前10讲,背诵15篇短文,熟词僻义300词基础词) 周目标:张宇强化前5讲并完成相应的习题并记录,英语背3篇文章并回诵 每日必复习(5分钟&#…