前言: 啊啊啊,一直想写文来着,但是最近一直忙着看房子,摇号,一直抽不出时间来。今天又摇了一次,结果让我彻底放弃摇号热门楼盘的想法,想着不能再拖了,这星期一定要出第一篇文章。其实第一篇是想出排序算法学习的,但是时间上有点紧,这周先出一个布隆过滤器相关的文章,简单的开头,希望结局并不简单。
在讲解布隆过滤器之前,先给大家讲一个小故事
失败的绑架
一伙绑架团伙准备绑架当地有名富商正在上小学的小公主,为此,他们进行了详细的分工。老大车技最好,留车上负责接应,得手立刻跑路。老二贼眉鼠眼,腿脚伶俐,负责外围放风,有什么风吹草动及时告警。老三最傻最笨,但是力气大,于是老大便安排他去绑小公主,找到人,给夹住就跑。因此便给了他看了小公主的照片,照片上写有名字,叫侍嬛。老大再三叮嘱,目标在xx班,从xx号楼xx楼梯到3楼,到了班级直接看人在不在,在的话就找机会,只要人出来就立刻发信号并且把人抱走上车跑路,不要节外生枝。老三此时却被窗外的风景吸引了目光,只是瞟了一眼照片,便走神的点着头敷衍着。
进了学校,老三确实犯了难,只记得是在xx楼,却不记得具体位置了。没办法,只能到xx楼一个教室一个教室的去问。到了第一个教室,老三敲开教室门。正在上课的老师皱皱眉头,咦,这人怎么打扰别人上课呢?不耐道:“你是哪位?找谁?”老三讷讷道:“我找沈、、沈、、”竟忘记了目标的名字~老师一拍手里的书说到:“找错地方了吧!我们班没有姓沈的小朋友!”老三急忙道歉,扫了一眼教室便仓皇离去。
到了第二个教室,老三再次敲开教室门。正在上课的是一位温柔的女老师,虽然被打断上课,却不生气,亲切的问道:“你好,请问你是哪位小朋友的家长啊,现在还没下课是有什么急事吗?”老三顿时羞红了脸:“我找沈、、我太笨了,忘记名字了,老板走之前给我看了照片让我来接人,我从小记性就不好、、”老师轻笑着捂了捂嘴:“我们班有姓沈的同学呢,你看看有没有你要找的人,但是你找到人以后我要和家长确认才能让你带走的哦。”老三急急点头,向教室里扫去,确实没有找到,失望的离开了。
终于,找了一个又一个教室后,终于在倒数第三个班级,老三找到了目标人物小公主,可惜为时已晚,因为教室太多,耽误了不少时间,在这期间内,早有起疑的老师通知了保卫处的通知过来,盯住了他,在他正要实施时拿下了他。
故事中的布隆过滤器
诶~傻子作者,文章不是要讲布隆过滤器吗?讲的故事是要干嘛?逗我们开心吗?也不好笑啊!咦,非也非也!在我看来,这个故事是布隆过滤器的一个典型的应用。
在这个故事中,笨贼就像是我们的客户端,而小公主则是我们要查找的数据,姓氏(沈)是我用于查找的key。老师在这里则是扮演着布隆过滤器的角色,老师知道每个同学的名字的姓氏,很容易通过姓氏判断本班级有没有笨贼要找的人。如果根本没有这个姓氏,直接告诉他没有,去下一个地方看看。如果有这个姓氏,再让他看看小朋友们中,有没有他要找的人。当然,最后打电话确认的权限问题可不属于布隆过滤器的范畴哦!
故事角色 | 应用场景 |
---|---|
笨贼 | 客户端 |
姓氏 | 查找key |
小公主 | 需要查找的数据 |
老师 | 布隆过滤器 |
布隆过滤器介绍
布隆过滤器能做什么?
诶!笨贼被抓时,嘟囔着:“就怪这些老师,为什么不能直接告诉我在不在教室,耽误了好多时间、、、害我被抓!”咦,我不记得?绑架可是高度紧张的精细活,能怪我吗?莫要凭空诬我清白!
有细心的同学应该发现了,老师不能告诉笨贼小公主在教室或者不在教室。只能告诉笨贼两个结果:
- 你要找的人肯定不在教室
- 你要找的人可能在教室
而这,就是布隆过滤器能做的,您查找的数据不在这里,你去别处看看?或者是,您查找的数据可能在哦,要不您进来碰碰运气?当我们对一个庞大的数据集进行检索时,布隆过滤器能快速的帮我们定位到该数据集是否可能包含我们要找的数据。如果我们在生成数据集时,对数据的key进行分组,相似的key尽量落在一个集合中,布隆过滤器可以让我们避开大量不需要查询的数据集,提高检索速度。
我该怎么实现一个布隆过滤器?
在故事中,老师是记住了所有同学的姓氏,所以他能够根据笨贼提供的姓氏,快速的回答他在或者不在。那么,在我们的程序代码中,该怎么实现这样的一个功能呢?
首先,布隆过滤器内部应该维护一个类似于姓氏列表的东西,依赖它,布隆过滤器可以快速知道这个数据的key是否存在在我关联的数据集中。实现它需要准备以下几个东东:
-
k个hash函数,用于计算key的哈希值
-
一个长度为n的比特数组,数组元素初始化为0
有了这些东西该怎么用才能生成这样一个数据的姓氏列表呢?
当数据加入到我们的数据集中时,需要对数据的key进行k次hash,将每次hash的结果作为比特数组的下标,将对应元素值置为1。这样一来,当你插入n条记录以后,这个布隆过滤器的所拥有的比特数组中可能不均匀的分布着1的元素。在查找时,我们首先得数据的key进行k次hash,并检查每次hash的结果对应的下标在数组中是否是1,只要有一个不是1,那就说明在这个集合中必不可能存在我们要查找的数据,赶紧去下一个数据集中再找啦!但是,如果下标都是1,那能告诉我们数据一定存在吗?不能。由于算法的原因,当前这个下标的1的结果可能是由于其他key在某一次hash中算出来的,因此,可能布隆过滤器告诉我们数据可能存在,但是实际查找以后发现,数据并不存在,Oh My God!赶紧去下一个数据集继续查找吧!
布隆过滤器的优缺点
缺点
误算:正如上面所言,布隆过滤器的告诉我们数据可能存在,但是查找后,发现数据实际并不存在,白白浪费了时间。想想看,你设置的比特数组长度n非常小,几位、十几位,而数据中散列着十分不均匀的key,很快,你的比特数组中所有元素都被置为了1。任意一个key过来查找时,都是快来快来,数据可能在的。Oh My God!噩梦!这就是布隆隆过滤器的误判,本质上是由于哈希碰撞导致的,不同的数据key通过hash函数算出来的hash值由于巧合存储在相同的比特位上。那我们究竟该怎么决定比特数组的长度,才能最大程度的较少误判呢?幸运的是,布隆过滤器有一个可预测的误判率FPP:
P
f
p
=
(
1
−
e
−
k
n
m
)
k
P_fp=(1-e^{\frac{-kn}{m}})^k
Pfp=(1−em−kn)k
- k:hash次数
- n:已经添加的元素数据量
- m:布隆过滤器的长度
通过FPP误判率公式,可以得出布隆过滤器的最佳长度的推导公式是:
m
=
−
m
ln
P
f
p
(
ln
2
)
2
m=-\frac{m{\ln}P_fp}{(\ln2)^2}
m=−(ln2)2mlnPfp
具体的推导过程我就不说,哈哈,公式看的脑阔疼。
不能删除:一般情况下,是不能从布隆过滤器中删除数据的。首先,要删除该数据,势必要将比特数组中对应的位数的值置为0,否则,在下次查询时,布隆过滤器还会返回true,从而对该数据集进行检索。问题来了,前面提到,布隆过滤器的误算的原因,是由于hash碰撞导致的,那么,在删除时,怎么能保证对其他的数据key不造成影响呢?全部遍历一遍,代价太大。其次,要删除数据,布隆过滤器对应的数据里面得有这条数据啊,前面提到的,布隆过滤器只知道,数据一定没有和可能有两种情况,额,巧妇难为无米之炊啊(臣妾真的做不到啊,无辜脸)
优点
布隆过滤器有什么好?为什么不用hash表存储数据?这样可以有效避免误算,并且可以删除数据,检索数据也快
首先这个问题得考虑下试用hash表存储数据的代价,使用hash表,必然要存储数据key。当数据量不大时,问题不明显,但是,一旦存储上亿、几十亿的数据时,对内存的需求是巨大的。这就是布隆过滤器的优点之一,在亿级数据应用场景中,存储空间的消耗是常数级别的。在保证空间消耗的同时,还可以保证数据查询、插入的时间性能也是常数级别。在亿级数据应用场景中使用它真是太棒了!
其次,布隆过滤器还有一个优点,那就是不需要存储数据,在一些特点的、需要保密的场景下有优势。
布隆过滤器使用场景
介绍过布隆过滤器的优缺点以后,想必读者应该已经猜到布隆过滤器的使用场景了。没错,真相只有一个,那就是在互联网海量数据中进行检索过滤。经典应用场景有:
- 爬虫判断URL是否已经爬取
- 反垃圾邮件
- 最最熟悉的,hadoop生态圈中大名鼎鼎的Hbase等
Hbase正是基于布隆过滤器对数据进行过滤,从而提高查询性能。同时,由于布隆过滤器不能删除数据,HBase是基于数据版本来解决的,当数据删除时,数据会被标记为删除,在进行文件Compaction时对数据进行删除,同时,重新构建布隆过滤器。具体HBase相关原理,我们下次再聊~
**后记:**真是磨磨蹭蹭终于搞出第一篇,在元旦假期的第二天把这篇文章发出来,关于写博客,写文章,我也是大姑娘上花轿-头一回,问题还是很多的,包括配插图的素材,各种公式的格式,甚至包括怕写错东西,还请大家多多包涵,有什么错误的地方请一定指出。
厂弟日记,请大家多多指导