作者 | 杜如东
要想学会递归,首先学会递归
一、问题背景
不安全网页的黑名单包含 100 亿个黑名单网页,每个网页的 URL 最多占用 64 字节。现在想要实现一种网页过滤系统,可以根据网页的 URL 判断该网站是否在黑名单上,请设计该系统。要求该系统允许有万分之一以下的判断失误率,并且使用的额外空间不要超过 30G。
通常我们判断一个 URL 存在与否,可能会用 HashMap,查询效率极高,时间复杂度 O(1),但是其占用的内存空间就没那么美丽了。那么有没有一种数据结构,查询速度快、占用空间又小呢?废话,当然是存在的,它就是我们今天的主角:Bloom 过滤器。
二、 什么是Bloom 过滤器
1、基本思想
Bloom 过滤器是 1970 年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。当一个元素被加入集合时,通过 K 个散列函数将这个元素映射成一个位数组中的 K 个点,把它们置为 1。检索时,我们只要看看这些点是不是都是 1 就(大约)知道集合中有没有它了:如果这些点有任何一个 0,则被检元素一定不在;如果都是 1,则被检元素很可能在。这就是布隆过滤器的基本思想。
Bloom 过滤器其实和 HashMap 有点类似,他们都需要利用哈希函数求得输入元素的哈希值,然后对数组长度取模,只不过 Bloom 过滤器需要多个哈希函数,占用空间更小。Bloom 过滤器其实就是一个概率型的数据结构,它会告诉你,一个元素可能存在或者一定不存在。
2、举个栗子
1、插入和查询操作
2、删除操作
标准的 Bloom 过滤器并不支持删除操作,一个元素通过 k 个哈希函数映射到 bit 数组中的 k 个点,如果删除的时候,直接将这 k 个点置为 0,会影响其他元素的判断,Counting Bloom filter 可以解决这类问题,具体可以参考Counting Bloom Filter 的原理和实现。
三、如何确定散列函数的个数和bit数组的长度
根据上图可知,如果 bit 数组的长度不变,随着集合元素越来越多,bit 数组中的所有 bit 将均置为 1,任何输入元素都会被认为可能存在,误差率将越来越高。那么如何确定散列函数的个数和 bit 数组的长度呢,接下来咱们进行简单的数学推导。
如果一个随机试验所包含的单位事件是有限的,且每个单位事件发生的可能性均相等,则这个随机试验叫做拉普拉斯试验,这种条件下的概率模型就叫古典概型。
假设存在一个长度为 m 的 bit 数组,k 个散列函数,n 个待插入的元素。
设误差率为 p ,设 i 为 bit 数组中的任意一个位置。
1、误差率推导
1. 根据古典概型,我们可以假设存在一个完美的散列函数(即一个待插入元素能等概率的映射到 bit 数组中的某一个位置)。那么 bit[i] 被置为 1 的概率为 ,反之,不被置为 1 的可能性为 。
2. 那么经过 k 次散列函数后,bit[i] 为 0 的概率是 。
3. 那么在 n 个元素完全插入后,bit[i]为 0 的概率为 ,置为 1 的概率为 。
4. 那么出现误判(一个元素通过散列函数映射后 k 个位置都为 1)的概率是 。
5. 根据极限定理 , 。
2、散列函数个数推导
令 ,得到 ,我们需要求的便是 的最小值。
可以很明显的看出,在待输入元素 (n) 个数固定的情况下,随着 bit 数组长 (m) 度越来越大,误差率越来越小。
3、空间大小推导
四、如何在 Redis 中使用 Bloom 过滤器
在 Redis 中,Bloom 过滤器存在几个基本命令,分别是:
bf.add : 添加元素到 Bloom 过滤器中
bf.madd : 批量添加元素到 Blomm 过滤器中
bf.exists : 查询某个元素是否在 Bloom 过滤器中
bf.mexists : 批量查询某些元素是否在 Bloom 过滤器中
我们第一次使用 bf.add 命令时,会自动创建一个默认参数的 Bloom 过滤器,如果想创建自定义参数的 Bloom 过滤器,我们需要在使用 bf.add 命令添加元素之前,使用 bf.reserve 命令创建一个自定义的 Bloom 过滤器,该命令有三个参数,分别是:
key : 键
error_rate :误判率 (p)
capacity :初始容量,当输入元素集合大小 (n) 大于该数值时,误判率会上升
设置了 error_rate 和 capacity,redis 底层通过公式 ,可以轻易的算出 bit 数组的长度,有兴趣的童鞋可以尝试算一下。
例如:
bf.reverse filter 0.0001 10000000000
看到这里,可以回头去看本文最初的问题,聪明的你,心里应该已经有答案了。
五、总结
1、优点
Bloom 过滤器本质上是一个概率型数据结构,它可以告诉你一个元素可能存在或者肯定不存在
插入和查询效率高
2、缺点
具有一定误判率
标准 Bloom 过滤器不支持删除操作
全文完
以下文章您可能也会感兴趣:
我们正在招聘 Java 工程师,欢迎有兴趣的同学投递简历到 rd-hr@xingren.com 。