提出一个问题

在我们细述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,分别为h1h2h3

首先遍历数据集中的元素,对每个元素分别计算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过滤器的更多相关文章

  1. 布隆(Bloom)过滤器 JAVA实现

    前言 Bloom过滤器,通过将字符串映射为信息指纹从而节省了空间.Bloom过滤器的原理为,将一个字符串通过一定算法映射为八个Hash值,将八个Hash值对应位置的Bitset位进行填充.在进行校验的 ...

  2. Bloom 过滤器

    待续... package com.ghc.mmall.concurrency.nio; import com.google.common.hash.BloomFilter; import com.g ...

  3. 硬核 | Redis 布隆(Bloom Filter)过滤器原理与实战

    在Redis 缓存击穿(失效).缓存穿透.缓存雪崩怎么解决?中我们说到可以使用布隆过滤器避免「缓存穿透」. 码哥,布隆过滤器还能在哪些场景使用呀? 比如我们使用「码哥跳动」开发的「明日头条」APP 看 ...

  4. bloom filter与dawgdic(一种trie树)

    我有一个做了一款移动浏览器的朋友. 他有这样一个需求:当用户输入一个站点的url时候.移动浏览器须要识别这个网址是否是一个恶意网址.另外.他有一个恶意网址库. 或许这种解决方法有多种. 当中一种就是把 ...

  5. 大数据量下的集合过滤—Bloom Filter

    算法背景 如果想判断一个元素是不是在一个集合里,一般想到的是将集合中所有元素保存起来,然后通过比较确定.链表.树.散列表(又叫哈希表,Hash table)等等数据结构都是这种思路,存储位置要么是磁盘 ...

  6. 协议栈处理中的conntrack HASH查找/Bloom过滤/CACHE查找/大包与小包/分层处理风格

    1.路由CACHE的优势与劣势 分级存储体系已经存在好多年了.其精髓在于"将最快的存储器最小化.将最慢的存储器最大化",这样的结果就使资源利用率的最大化.既提高了訪问效率,又节省了 ...

  7. 基于Redis扩展模块的布隆过滤器使用

    什么是布隆过滤器?它实际上是一个很长的二进制向量和一系列随机映射函数.把一个目标元素通过多个hash函数的计算,将多个随机计算出的结果映射到不同的二进制向量的位中,以此来间接标记一个元素是否存在于一个 ...

  8. Redis 布隆过滤器

    1.布隆过滤器 内容参考:https://www.jianshu.com/p/2104d11ee0a2 1.数据结构 布隆过滤器是一个BIT数组,本质上是一个数据,所以可以根据下标快速找数据 2.哈希 ...

  9. 关于布隆过滤器,手写你真的知其原理吗?让我来带你手写redis布隆过滤器。

    说到布隆过滤器不得不提到,redis, redis作为现在主流的nosql数据库,备受瞩目:它的丰富的value类型,以及它的偏向计算向数据移动属性减少IO的成本问题.备受开发人员的青睐.通常我们使用 ...

随机推荐

  1. 记录一次腾讯X5内核64位手机初始化失败

    之前一直在使用x5内核,只需要一个jar包和so文件就能让webview实现多余原生webview的水平,在32位的手机上能够正常运行,但是到了64位手机上就报如下错误: E/ERROR:: .... ...

  2. DIOCP任务队列和工作线程

    DIOCP任务队列和工作线程 涉及4个单元文件:utils_strings.pas,utils_queues.pas,utils_queueTask.pas,utils_grouptask.pas. ...

  3. 中间件 | mq消息队列解说

    消息队列 1.1 什么是消息队列 我们可以把消息队列比作是一个存放消息的容器,当我们需要使用消息的时候可以取出消息供自己使用.消息队列是分布式系统中重要的组件,使用消息队列主要是为了通过异步处理提高系 ...

  4. 安卓P2P开源项目

    https://github.com/LinYaoTian/P2PChat 一个基于局域网的 Android P2P 聊天系统 https://github.com/ddssingsong/webrt ...

  5. ISO/IEC 9899:2011 条款6.4.2——标识符

    6.4.2 标识符 6.4.2.1 通用 语法 1.identifier: identifier-nodigit identifier    identifier-nondigit identifie ...

  6. DisplayAttribute应用——根据PropertyName自动获取对应的UI显示名

    model定义,使用DisplayAttribute public class AddressSetInfo { /// <summary> /// invoiceAddress.Id / ...

  7. jmeter 测试 websocket 接口

    https://www.cnblogs.com/penghaihang/p/7724830.html(请求的亲测ok) https://www.jianshu.com/p/bb8b3e928607 h ...

  8. 123457123456#1#-----com.threeapp.qianShuiTingYX01-----潜水艇跑酷游戏01

    com.threeapp.qianShuiTingYX01-----潜水艇跑酷游戏01

  9. PAT 甲级 1054 The Dominant Color (20 分)(简单题)

    1054 The Dominant Color (20 分)   Behind the scenes in the computer's memory, color is always talked ...

  10. 数据结构与算法 Javascript描述

    数据结构与算法系列主要记录<数据结构与算法 Javascript描述>学习心得