Redis布隆过滤器
一:什么是布隆过滤器
布隆过滤器(Bloom Filter)是 Redis 4.0 版本提供的新功能,它被作为插件加载到 Redis 服务器中,给 Redis 提供强大的去重功能。
当布隆过滤器判定某个值存在时,这个值只是有可能存在;当它说某个值不存在时,那这个值肯定不存在。所以布隆会有一定的误判率,但是误判率极低。
二:布隆过滤器使用场景
虽然这种结构的去重率并不完全精确,但和其他结构一样都有特定的应用场景,比如当处理海量数据时,就可以使用布隆过滤器实现去重。
1、垃圾邮件过滤功能
2、爬虫url过滤
3、解决缓存穿透问题
三:布隆过滤器原理
布隆过滤器(Bloom Filter)是一个高空间利用率的概率性数据结构,由二进制向量(即位数组)和一系列随机映射函数(即哈希函数)两部分组成。其中位数组的初始状态都为 0。
当使用布隆过滤器添加 key 时,会使用不同的 hash 函数对 key 存储的元素值进行哈希计算,从而会得到多个哈希值。根据哈希值计算出一个整数索引值,将该索引值与位数组长度做取余运算,最终得到一个位数组位置,并将该位置的值变为 1。每个 hash 函数都会计算出一个不同的位置,然后把数组中与之对应的位置变为 1。通过上述过程就完成了元素添加(add)操作。
简单理解:元素添加到布隆过滤器的时候,内部会进行一系列计算,将过滤器的数组位上的对于值修改为1。
这样也就理解了为什么布隆过滤器会存在误判,假设元素1占据1,2,3号位,而元素2应该占据1,2号位。那么添加的时候计算出的位置已经被元素1占据,那么就会认为已存在。
1、布隆和哈希表比较
哈希表查询效率可以达到O(1),但是哈希存储比较消耗内存,会随着数据的增大而不但增大。
布隆过滤器在前期确定数据大小之后不会随着数据的改变而改变。
2、布隆过滤器误判率统计
m/n | k=1 | k=2 | k=3 | k=4 | k=5 | k=6 | k=7 | k=8 |
2 | 0.393 | 0.400 | ||||||
3 | 0.283 | 0.237 | 0.253 | |||||
4 | 0.221 | 0.155 | 0.147 | 0.160 | ||||
5 | 0.181 | 0.109 | 0.101 | 0.092 | 0.092 | |||
6 | 0.154 | 0.080 | 0.060 | 0.056 | 0.057 | 0.063 | ||
7 | 0.133 | 0.061 | 0.042 | 0.035 | 0.034 | 0.036 | ||
8 | 0.118 | 0.048 | 0.030 | 0.024 | 0.021 | 0.021 | 0.022 | |
9 | 0.105 | 0.039 | 0.022 | 0.016 | 0.014 | 0.013 | 0.013 | 0.014 |
10 | 0.095 | 0.032 | 0.017 | 0.011 | 0.009 | 0.008 | 0.008 | 0.008 |
11 | 0.086 | 0.027 | 0.013 | 0.008 | 0.006 | 0.005 | 0.005 | 0.005 |
12 | 0.080 | 0.023 | 0.010 | 0.006 | 0.004 | 0.003 | 0.003 | 0.003 |
3、Bit数组的大小选择
根据预估数据量n以及误判率fpp,bit数组大小的m的计算方式:
4、哈希函数的量选择
由预估数据量n以及bit数组长度m,可以得到一个hash函数的个数k:
四:布隆过滤器的使用
1、通过redis hash (setbit | getbit) + hash (多个) 函数 实现
2、安装redis 布隆插件 bloom-fifilter (https://github.com/RedisBloom/RedisBloom)
使用Redis 布隆过滤器主要就2个命令:
# bf.add 将元素添加到布隆过滤器
bf.add key value
# bf.add 判断元素是否存在布隆过滤器
bf.exists key value
上面说过布隆过滤器存在误判的情况,在 Redis 中有两个值决定布隆过滤器的准确率:
-
error_rate :允许布隆过滤器的错误率,这个值越低过滤器的位数组的大小越大,占用空间也就越大。
-
initial_size :布隆过滤器可以储存的元素个数,当实际存储的元素个数超过这个值之后,过滤器的准确率会下降。
# 设置错误率 和 存储元素个数, 当key存在时不可设置 bf.reserve key 0.01 10000
实际中如何使用呢?
先查询布隆是否存在,若不存在则直接处理数据,如果存在查询mysql再根据结果处理数据。
五:Laravel 中的调用
# 设置布隆过滤器 Redis::rawCommand('bf.reserve', 'course', '0.01', '10000'); # 查询值是否存在 Log::info(Redis::rawCommand('bf.exists', 'course', 'php')); # 加入一个值到过滤器 Redis::rawCommand('bf.add', 'course', 'php'); Log::info(Redis::rawCommand('bf.exists', 'course', 'php')); Log::info(Redis::rawCommand('bf.exists', 'course', 'java'));
六:布隆过滤器的缺点
无法删除元素
需要定期做补偿机制