布隆过滤器 Bloom Filter
之前的一版笔记 点此跳转
1. 什么是布隆过滤器
本质上布隆过滤器是一种数据结构,比较巧妙的概率型数据结构(probabilistic data structure),特点是高效地插入和查询,可以用来告诉你 “某样东西一定不存在或者可能存在”。
相比于传统的 List、Set、Map 等数据结构,它更高效、占用空间更少,但是缺点是其返回的结果是概率性的,而不是确切的。
解决目标:在海量数据的场景当中用来快速地判断某个元素在不在一个庞大的集合当中。
2. 背景
比如在爬虫场景当中,我们需要记录下之前爬过的网站。我们要将之前的网址全部都存储在容器里,然后在遇到新网站的时候去判断是否已经爬过了。在这个问题当中,我们并不关心之前爬过的网站有哪些,我们只关心现在的网站有没有在之前出现过。也就是说之前出现过什么不重要,现在的有没有出现过才重要。
我们利用平衡树或者是Trie或者是AC自动机等数据结构和算法可以实现高效的查找,但是都离不开存储下所有的字符串。想象一下,一个网址大概上百个字符,大约0.1KB,如果是一亿个网址,就需要10GB了,如果是一百亿一千亿呢?显然这么大的规模就很麻烦了,今天要介绍的布隆过滤器就可以解决这个问题,而且不需要存储下原值,这是一个非常巧妙的做法,让我们一起来看下它的原理。
3. 算法
- 需要 k 个 hash 函数,依次传入关键字,返回值对应了 k 个不同的 hash 值
- 初始化一个长度为 m 的比特 bit 数组,所有值都置为0
- 通过 k 个不同的 hash 值对长度 m 取模,可以得到 n 个不同的下标值(n <= k 有可能存在hash值不同,但是取模最后相同的情况),将数组中对应下标的值都由0置为1
- 拿到一个新的关键字,计算出它的 k 个 hash 值,得到 n 个下标,查询数组中对应下标的值是否是1,分两种情况
- 如果有一个不是1,那么可以认为这个关键字一定没有存在过,并将其对应的下标都置为1
- 如果都是1,只能认为可能存在。因为可能存在两个关键字不同,但是hash值取模后的下标是一样的情况
4. 优缺点
优点
- 不需要存储key,节省空间
缺点
- 可能存在,导致命中失败
- 无法删除,会导致结果越来越大,最终需要重建数组
5. 代码
1 | # 插入元素 |
6. 应用
利用布隆过滤器减少磁盘 IO 或者网络请求,因为一旦一个值必定不存在的话,就可以不用进行后续昂贵的查询请求,比如
- Google 著名的分布式数据库 Bigtable 使用了布隆过滤器来查找不存在的行或列,以减少磁盘查找的IO次数
- Squid 网页代理缓存服务器在 cache digests 中使用了也布隆过滤器
- Venti 文档存储系统也采用布隆过滤器来检测先前存储的数据
- SPIN 模型检测器也使用布隆过滤器在大规模验证问题时跟踪可达状态空间
- Google Chrome浏览器使用了布隆过滤器加速安全浏览服务
7. 改进后的布隆过滤器
由于传统布隆过滤器的每一位都只有0和1两个状态,并不支持删除操作,原因如下:
假设 A 和 B 两个关键字对应的下标分别为 1、5 和 2、5,删除 A 后将下标 1、5 都置为0,那么查询 B 的时候由于 5 被置为0,其实是存在的,也会返回不存在。
于是有了一个改进版布隆过滤器——Counting Bloom Filter。改进的地方是:数组每一位变成了计数器,插入时就让计数器 +1,删除时 -1。但是容易引起很大的资源浪费,同时有可能某一位多次删除操作后变成负数。