布隆过滤器
1、布隆过滤器概述
布隆过滤器(Bloom Filter)是一种概率型数据结构,它高效且占用空间小,常用于判断某个元素是否在一个集合中。不过,这个结果并不是完全准确的,只是一个具有概率性的结果,只要正确使用,在足够大体量的数据中这个概率的不完全准确几乎就可以忽略不计了。
布隆过滤器用于解决缓存穿透的问题,比如故意搜索一些缓存和数据库中没有的数据、恶意穿透缓存形成对数据库的攻击。本章主要讲解它的安装和原理,后续我们会介绍它的用途。
2、环境安装
为了方便管理Redis的模块,这里在Redis目录下新建module目录并将刚才编译的文件复制到该目录下,然后安装。
1)进入RedisBloom源码包进行编译,得到文件redisbloom.so。
bash
wget https:// github.com/RedisBloom/RedisBloom/archive/v2.2.3.tar.gz
tar -zxvf RedisBloom-2.2.3.tar.gz -C .
cd RedisBloom-2.2.3
make
2)把得到的文件复制到安装的Redis模块中。
bash
/home/redis/redis-6.0.9/src
// 创建模块目录
mkdir /home/redis/redis-6.0.9/module
// 将redisbloom.so文件移动到创建的模块目录中
mv redisbloom.so /home/redis/redis-6.0.9/module/
// 修改配置文件redis.conf,加上下面这行代码
Loadmodule /home/redis/redis-6.0.9/module/redisbloom.so
// 重启Redis服务器
systemctl restart redis
2.2、Docker安装
命令如下:
bash
docker run -p 6379:6379 -d --name redis-redisbloom redislabs/rebloom:latest
验证:
bash
docker exec -it redis-redisbloom bash
redis-cli
127.0.0.1:6379>
通过添加新项目来创建新的布隆过滤器:
bash
# 127.0.0.1:6379> bf.add newFilter foo
(integer) 1
找出过滤器中是否存在一项:
bash
# 127.0.0.1:6379> bf.exists newFilter foo
(integer) 1
1表示在newFilter集合中存在foo,需要注意的是,使用布隆过滤器可能会出现误报。
bash
1表示在newFilter集合中存在foo,需要注意的是,使用布隆过滤器可能会出现误报。
0表示bar不在集合中。启动容器,然后创建布隆过滤器进行键的判断操作,如图所示。在返回结果中,1代表存在,0代表不存在。

3、布隆过滤器的原理
每一个布隆过滤器对应到Redis中就是一个位数组和几个不同的哈希函数,当我们向布隆过滤器中添加新元素时会调用这几个哈希函数将算出来的几个整型索引值对应到位数组中的内容,将那几个位置的值标为1,初始时默认该位数组中所有值都是0。在判断元素是否在该集合中时,也是调用这几个哈希函数将算出来的几个整型索引值对应到位数组中的内容,判断这几个位置的数组内容是否全部为1,如果有不为1的值,那么说明该元素一定不在这个集合内,如果全部为1,则该元素可能存在于该集合内。
三个不同的键存储在位数组的示意图如图11-2所示。随着键值越来越多,可能会让整个位数组的内容全部标记为1,这样就可能存在误判,比如key11经过三个哈希函数计算出的结果是14、8、10。

当根据位数组的内容去进行判断时,发现这几个索引值对应的位数组的内容都是1,就会判断key11在集合中,但是key11实际上是不在集合中的。误判的失败率与哈希算法计算的次数以及位数组的长度有关,数组越长、哈希算法计算次数越多、插入元素越少,误判率就越低。
计算公式如下:
h≈0.7*(m/n)p≈0.6185^(m/n)
其中,n表示元素的数量;M代表位数组的长度,位数组越长,占用空间越大;h表示最近的哈希函数的数量;p代表误判率。
通过上面的公式可知:位数组m越长,误判率p越低;位数组m越长,哈希函数最佳的数量h越多;哈希函数越多,则计算花费的时间越长。
实际上,通过上面的公式可以得出:
p=1%时,每个元素才占9.6 bit。p=0.0001%时,每个元素才占28.8 bit。
也就是说,误判率p=0.0001%时,每个元素只占用28.8bit,就算存储100万个元素,也不过消耗27.5MB的存储空间。布隆过滤器的空间效率非常高、占用率非常低,无论是查询速度还是存储方式都相当不错。
使用如下:
bash
// 创建一个误判率0.1%、可存储10000个元素的布隆过滤器
bf.reserve myfilter 0.001 10000
bf.info myfilter # 查看布隆过滤器
bf.add myfilter key1 # 添加新元素
bf.exists myfilter key1 # 判断元素是否在集合内
结果如图所示。

4、应用场景
- 避免爬虫爬到重复内容
可以把爬虫爬过的URL放入布隆过滤器中,爬虫每次爬取之前先判断目标URL是否在布隆过滤器中,如果在就不需要重复爬取;如果不在就执行爬取操作。 - 垃圾邮件过滤黑白名单可以把黑名单用户信息存储在布隆过滤器中。当收到邮件之后,判断发件人是否在布隆过滤器中,如果在,就将此邮件过滤掉。白名单也是如此。
- 避免消息推送给相同的人将发送过消息的用户名单存放在布隆过滤器中,下次发送此消息时若用户名在布隆过滤器中则不需要重新发送。
- 解决缓存穿透等缓存问题