腾讯 | 布隆过滤器原理与应用

导语 | 开通微信时,系统如何判断你输入的手机号没被注册?如何使用更少的存储空间、更快的速度解决这个问题?对于这个问题,腾讯微信支付数据开发工程师杭天梦带来了她利用Bloom过滤器解决此类问题的思考,向大家分享。本文分享的主要内容为Bloom过滤器的简介、原理、应用和结论等。

图片

“开通微信时,系统如何判断你输入的手机号没被注册?如何使用更少的存储空间、更快的速度解决这个问题?”

对于这个问题,最暴力的方法为:
通过遍历来判断是否被注册。那么时间复杂度为O(n),空间复杂度也是O(n)。

稍微学过算法的同学会说:
使用HashMap,时间复杂度瞬间降到O(1)。

精通算法的同学可能会说:
使用Bitmap,因为Bitmap只需要存储数据的状态信息(0和1),这样空间复杂度为O(max_value)。

下面问题又来了:

如果n=10亿,将会占据多少内存?

假设整数为64bit=8Byte,

Hashmap:10亿整数需要8G的内存

Bitmap:图片

虽然速度提上去了,内存占有量无法想象的…大!

那如何既保证查询效率,又保证低内存占用?

下面我们的主角闪亮登场——布隆过滤器。

一、Bloom过滤器的简介

Bloom过滤器(Bloom Filter)是由布隆(Burton Howard Bloom)在1970年提出的。

它实际上是由一个很长的二进制向量和一系列随机映射函数组成。

它的目标是——占用更小的空间的前提下,检索一个元素是否在一个集合中。

二、原理介绍

下面我从三个方面来介绍布隆过滤器:构造、检索、效果。

1.构造

构造主要包括以下三个步骤:

  • 选择k个哈希函数
  • 将待检索字符串分别做Hash映射
  • 每个映射的值对应的bit数组置为“1”

我举一个简单的例子:
假设我们有3个哈希函数,有两个待检索字符串"jimboooo"和"luckyyyyy"。

对于字符串"jimboooo",经过三个哈希函数映射后,将1,4,8的位置置为“1”。

图片

同理,对于字符串“luckyyyyy",我们经过哈希函数映射后,将位置2,4,7置为“1”。

图片

那么,我们的布隆过滤器已经构造完毕了。

2.检索

  • 将待检索的字符串通过k个哈希函数映射;
  • 查看映射的整数对应的位置是否1,如果都为1,说明待检索字符串是存在的。

下图是我们构造过程得到的数组,如果要检索"jimbooo",哈希映射后是2,4,7,这三个位置都为1,说明此字符串是存在的。如果要检索"fukuoka",映射后是1,3,4,因为3的位置为0,很明显它是不存在的。

图片

3.效果

布隆过滤器的原理已介绍完毕,看起来十分简单。那可能会出现什么问题呢?

如果要检索的字符串(原本不存在)映射后数组每个位置恰好都为1,那就出现了误判!

图片

我们来通过公式了解下它的误判率、布隆过滤器长度以及哈希函数个数之间的关系吧。

先上公式(推理见附录):
k 为哈希函数个数,m 为布隆过滤器长度,n 为插入的元素个数(待检索元素总数),p 为误报率,
当且仅当:

图片

误报率p取得最优解:



图片

根据公式就可以得到布隆过滤器的长度、误识率、待插入元素个数(待检索元素总数)的关系了。

如下图所示,x轴为m/n,含义为每个元素占有的bit数,y轴为误识率。

图片

得出的结论是,对于一个拥有最优k值且误判率在1%的布隆过滤器,每个元素只需要9.6bits(与元素的大小无关)。若给每个元素增加4.8bits左右,误判率将会减少十倍。

三、应用

可应用于很多方面:

1.网页爬虫对URL的去重

避免爬取相同的URL地址。

2.缓存击穿

将已存在的缓存放到布隆中,当黑客访问不存在的缓存时迅速返回避免缓存及DB挂掉。

3.HTTP缓存服务器

当本地局域网中的PC发起一条HTTP请求时,缓存服务器会先查看一下这个URL是否已经存在于缓存之中,如果存在的话就没有必要去原始的服务器拉取数据了(为了简单起见,我们假设数据没有发生变化),这样既能节省流量,还能加快访问速度,以提高用户体验。

4.黑/白名单

类似反垃圾邮件。

四、结论

布隆过滤器用于判断一个元素是否在一个集合中,不会有假负例(将在集合中的元素误判不在集合中),但会有一定的误识率(将不在集合中的元素误判为在集合中)。

方案对比结论:

图片

五、附录

1.公式推导

(1)k次哈希函数某一bit(长度为m)未被置为1的概率为:

(2)插入n个元素后依旧为 0 的概率和为 1 的概率分别是:

图片

图片

(3)k个位置均被设为1的概率:

图片

2.如何让误识率降到最低?

图片

3.哈希函数怎么设计?

要点:

  • Independent 相互独立
  • Uniformly distributed.离散型均匀分布(概率相同)
  • They should also be as fast as possible. 要快

案例分享:

Chromium uses HashMix
python-bloomfilter uses cryptographic hashes(cryptographic hashes)
Plan9 uses a simple hash as proposed in Mitzenmacher 2005
Sdroege Bloom filter uses fnv1a (included just because I wanted to show one that uses fnv.)Squid uses MD5

4.参考文章

(1)布隆过滤器应用:

https://www.pdai.tech/md/algorithm/alg-domain-bigdata-bloom-filter.html

(2)Hash函数设计:

https://llimllib.github.io/bloomfilter-tutorial/

(3)函数推导参考:

https://blog.csdn.net/quiet_girl/article/details/88523974

作者简介

image.png


本文地址:https://www.6aiq.com/article/1627092207876
本文版权归作者和AIQ共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出