Bloom过滤器
提出一个问题
在我们细述Bloom过滤器之前,我们先抛出一个问题:给你一个巨大的数据集(百万级、亿级......),怎么判断一个元素是否在此数据集中?或者怎么判断一个元素不在此数据集中?
思考这个问题的时候,最先想到的可能是哈希表,在数据集规模较小的时候,这个方法是可行的,当然,数据集巨大的时候也可以采用分布式哈希表的方式。当数据集规模较大时,尤其是应用中只需要判断一个元素不在此数据集中的情况时,我们可以借鉴哈希表的思路,使用Bloom过滤器解决这个问题。既然我们只关心元素在不在,不关心元素值是什么,只要把元素映射为一个布尔值表示在不在就足够了。下面细述Bloom过滤器数据结构的设计。
Bloom过滤器数据结构
Bloom filter(布鲁姆过滤器)是用于测试元素成员资格的空间高效概率数据结构。数据结构以牺牲规定的假阳性率为代价实现了巨大的数据压缩。一个Bloom过滤器作为一个m
位的数组全部设置为0
。选择一组k
个随机散列函数,在Bloom过滤器中添加元素时,元素将分别进行哈希散列,而对于每个k
个输出,该索引处的相应的Bloom过滤器位将被设置为1
。通过使用与之前相同的散列函数来完成Bloom过滤器的查询。如果在bloom过滤器中访问的所有k
个比特被设置为1
,则这很可能表明该元素位于该集合中。删除元素只能通过废除Bloom过滤器并从头重新创建来完成,这个问题后面会讨论到。
Bloom过滤器是由底层数组和哈希函数组合在一起工作的,根据对误报率要求的不同,可以选择一个哈希函数,也可以选2个、3个,一般情况下选3个。与哈希表不同,为节省空间,Bloom过滤器的底层数组的每一位是一个比特,1
表示有映射,0
表示无映射。数组的长度与问题规模、哈希函数、误报率等因素有关,根据数据集规模的不同,可选用适当的哈希函数与适合的数组大小。因为具体问题的不同,很难说那种实现是最好的。下面举例说明Bloom过滤器的工作过程。
数据集s={a,b,c}
,底层数组长度m=28
,初始值全部为0
,哈希函数个数k=3
,分别为h1
、h2
和h3
。
首先遍历数据集中的元素,对每个元素分别计算3组哈希,将数组中对应位置置为1
。计算过程中,有可能会发生哈希碰撞(即,不同元素的哈希值映射到数组中同一个位置),此时置1
即可,与哈希表不同,无需做冲突处理。所有元素插入后,Bloom过滤器构造完成。
构造好Bloom过滤器后,我们开始判断一个元素是否在数据集中。我们对要判读的元素做3次哈希,在数组中找对应位置的映射值,如果发现有一个映射值为0
,那么可以判断该元素一定不在数据集中,如果数组中对应的哈希映射值全部是1
,则可以判断大概率在数据集中,但也有可能不在数据集中。后面会给出原因。例如图中的元素e
,经过哈希映射后,发现有一个映射值为0
,则,元素e
不在数据集中;而元素d
,虽然哈希映射值全部为1
,但只是大概率在数据集中,实际上却不在数据集中,当然这种误判发生的概率很低。
可以看到,Bloom过滤器的工作过程是非常简单的。
Bloom过滤器的相关问题
为什么Bloom过滤器不能100%确定元素在数据集中呢?
很明显,Bloom过滤器是可以100%确定一个元素不在数据集中的,但是判断一个元素在数据集中只能说很大概率在。
因为哈希碰撞的原因,底层数组对应映射值为1
,有可能是其他元素与要查找的元素发生碰撞,实际上,该元素并不存在在数据集中。所以Bloom过滤器存在误报率。
Bloom过滤器能否有删除元素的操作
可以看到,Bloom过滤器,只有插入、查找操作,没有删除操作,为什么呢?
因为哈希碰撞的原因,有可能2个元素发生哈希碰撞,此时删掉其中一个元素,对应底层数组的值置为0
,则等于把另一个元素也删掉了,所以是没有删除操作的。那单从数据集中删掉一个元素,对应的底层数组的值不变,这样可以吗?逻辑上,是可以的,并不会引发错误,但实际上,这样做会大幅增加误报率,如果不断的删除插入,最后会发现,整个底层数组都快被填满了,失去了Bloom过滤器快速判断元素是否在数据集中的意义了。当然,如果删除操作很少的话,这样解决也是可以的,但是要在误报率允许范围内,定期重建Bloom过滤器(当数据集非常大时,不断重建的过程代价是很大的)。
当然,现实需求中,很可能是有删除元素的操作需求的,哪怎么办呢?可能的一个思路是在Bloom过滤器的基础上,做改进,类似引用计数的思路,底层数组中的值不再是布尔值,而是一个整型,每当发生一次碰撞,对应值递增一次,当删除一个元素时,递减一次。当然仅仅做到这样,还是不够的,可能还会有其他的问题,具体怎么实现,还有待各位大神去解决,这里不再做更深的思考。
还有大神提出了Cuckoo Filter,参考论文Cuckoo Filter: Practically Better Than Bloom。
误报率的计算
误报率与多个因素相关,数组的长度,元素的个数,哈希函数本身,哈希函数的个数等等。对于误报率如何计算,可参考Bloom filter,这里不再细述。
参考文档:布隆过滤器
除了理论上的误报率计算,程序也可以实际感受到误报率的变化,需要的话可以在程序中对每一次误报做统计,误报的次数/总的次数
。
Bloom过滤器的应用
经过上面的学习,我们可以回答文章最开始的问题了,只需要将原始数据集全部映射到Bloom过滤器底层数组中,所需要的空间开销要比哈希表等其他方式小的多,因为它不存储原始数据集数据,只存储象征数据是否存在的一个布尔值。判断一个元素时,计算哈希,查找对应映射值即可判断。
下图说明的是在一个KV存储系统中,使用Bloom提高查询响应速度。
可以看到,针对key1
这种查询请求,就无需再访问KV存储系统了,可返回访问结果:数据不存在;针对key2
这种请求,则继续访问KV存储系统返回结果;针对key3
这种是属于误报,很少发生。如果实际的应用需求中,有大量的系统并未实际存储的数据查询请求,这种方式能够显著降低对KV存储系统的访问,提高响应效率。
欢迎关注微信公众号,推送数据结构、分布式、区块链、后端开发等更多内容!
Bloom过滤器的更多相关文章
- 布隆(Bloom)过滤器 JAVA实现
前言 Bloom过滤器,通过将字符串映射为信息指纹从而节省了空间.Bloom过滤器的原理为,将一个字符串通过一定算法映射为八个Hash值,将八个Hash值对应位置的Bitset位进行填充.在进行校验的 ...
- Bloom 过滤器
待续... package com.ghc.mmall.concurrency.nio; import com.google.common.hash.BloomFilter; import com.g ...
- 硬核 | Redis 布隆(Bloom Filter)过滤器原理与实战
在Redis 缓存击穿(失效).缓存穿透.缓存雪崩怎么解决?中我们说到可以使用布隆过滤器避免「缓存穿透」. 码哥,布隆过滤器还能在哪些场景使用呀? 比如我们使用「码哥跳动」开发的「明日头条」APP 看 ...
- bloom filter与dawgdic(一种trie树)
我有一个做了一款移动浏览器的朋友. 他有这样一个需求:当用户输入一个站点的url时候.移动浏览器须要识别这个网址是否是一个恶意网址.另外.他有一个恶意网址库. 或许这种解决方法有多种. 当中一种就是把 ...
- 大数据量下的集合过滤—Bloom Filter
算法背景 如果想判断一个元素是不是在一个集合里,一般想到的是将集合中所有元素保存起来,然后通过比较确定.链表.树.散列表(又叫哈希表,Hash table)等等数据结构都是这种思路,存储位置要么是磁盘 ...
- 协议栈处理中的conntrack HASH查找/Bloom过滤/CACHE查找/大包与小包/分层处理风格
1.路由CACHE的优势与劣势 分级存储体系已经存在好多年了.其精髓在于"将最快的存储器最小化.将最慢的存储器最大化",这样的结果就使资源利用率的最大化.既提高了訪问效率,又节省了 ...
- 基于Redis扩展模块的布隆过滤器使用
什么是布隆过滤器?它实际上是一个很长的二进制向量和一系列随机映射函数.把一个目标元素通过多个hash函数的计算,将多个随机计算出的结果映射到不同的二进制向量的位中,以此来间接标记一个元素是否存在于一个 ...
- Redis 布隆过滤器
1.布隆过滤器 内容参考:https://www.jianshu.com/p/2104d11ee0a2 1.数据结构 布隆过滤器是一个BIT数组,本质上是一个数据,所以可以根据下标快速找数据 2.哈希 ...
- 关于布隆过滤器,手写你真的知其原理吗?让我来带你手写redis布隆过滤器。
说到布隆过滤器不得不提到,redis, redis作为现在主流的nosql数据库,备受瞩目:它的丰富的value类型,以及它的偏向计算向数据移动属性减少IO的成本问题.备受开发人员的青睐.通常我们使用 ...
随机推荐
- Hive和Hadoop
我最近研究了hive的相关技术,有点心得,这里和大家分享下. 首先我们要知道hive到底是做什么的.下面这几段文字很好的描述了hive的特性: 1.hive是基于Hadoop的一个数据仓库工具,可以将 ...
- little difference
把一个数字分解成有限个相差不超过1的因子: 这里如果是2的n次幂就不可以,因为比如4,可以拆成 2,2,或者2,2,1,或者2,2,1,1,...所有这个不可以,没想到这个 数据是1E18,一开始想觉 ...
- Flutter移动电商实战 --(39)路由_Fluro的路由配置和静态化
handler只是单个路由的配置,这节课我们要学习路由的整体配置 整体配置 新建routers.dart文件来做整体配置 detailsHandler就是我们在router_handler里面定义的d ...
- npm如何更新安装包?
方法一手动跟新:修改package.json中依赖包版本,执行npm install --force 方法二使用第三方插件:npm install -g npm-check-updatesncu // ...
- Flutter布局基本情况总结:
1.一行内容,发布两边: 效果: Flex( direction: Axis.horizontal, children: <Widget>[ Expanded( flex: , child ...
- 阶段5 3.微服务项目【学成在线】_day07 课程管理实战_03-新增课程-课程分类查询
2 新增课程 2.1 需求分析 用户操作流程如下: 1.用户进入“我的课程”页面,点击“新增课程”,进入新增课程页面 2.填写课程信息,选择课程分类.课程等级.学习模式等. 3.信息填写完毕,点击“提 ...
- 华为OpenStack开源团队人才招募中
职位要求: 1. 三年以上软件开发经验,编程技能良好. 2. 熟练使用Python.Java.Go或其他语言开发. 3. 有OpenStack经验或者存储经验优先考虑. 4. 良好的学习和沟通能力,责 ...
- 利用Python获取cookie的方法,相比java代码简便不少
1.通过urllib库,是python的标准库,不需要另外引入,直接看代码,注意代码的缩进: # coding=UTF-8import cookielibimport urllib2 class Ry ...
- Redis高级功能 - 慢查询日志
转自 https://segmentfault.com/a/1190000009915519
- CSS3 @font-face详细用法
@font-face是CSS3中的一个模块,他主要是把自己定义的Web字体嵌入到你的网页中,随着@font-face模块的出现,我们在Web的开发中使用字体就不用再为只能使用Web安全字体烦恼了!肯定 ...