布隆过滤器
HyperLogLog可以进行估数,非常具有价值,可以解决很多精确度要求不高的统计需求。
但是如果我们想知道某一值是不是在HyperLogLog结构内则无能为力了,因为HyperLogLog没有提供类似pfcontains这种方法。
比如,我们在使用新闻客户端看新闻时,它会不断推荐新的内容,每次推荐时都要去重,那么如何实现推送去重?
我们会想到,服务器记录了用户看过的所有历史纪录,推荐系统每次都从用户的历史纪录内筛选已经看过的记录,但用户量很大并且每个用户看过的新闻又很多时,推荐系统的去重功能在性能上不一定能跟的上。并且如果历史记录保存在关系数据库中,去重就要频繁的对数据库进行exists查询,当并发量上来时,数据库首先会扛不住压力。
再其次可能会想到缓存,但这么大量的历史纪录全部缓存下来,浪费的存储空间就太大了,并且这个存储空间是线性增长的,能撑得住一个月,不一定撑得了几年,但不用缓存,性能又跟不上,那么怎么办呢?
布隆过滤器(Bloom Filter)就是专门来解决这种问题的,它起到去重的同时,在空间上还能节省90%以上,只是存在一定的误判概率。
布隆过滤器是什么?
可理解为一个不那么精确的set结构,当使用contains
方法判断某个对象是否存在时,可能会误判,但是也不是特别不精确,只要参数设置合理,精确度可以控制得足够精准。
当布隆过滤器说某个值存在时,它可能不存在;但当它说某个值不存在时,它一定不存在。
套用在上述情景中,布隆过滤器能准确过滤掉已看过的内容,没看过的内容可能会过滤掉很小一部分,这样就能保证推荐给用户的都是无重复的。
Redis中的Bloom Filter
Redis官方提供的布隆过滤器到4.0提供插件功能后才正式登场,布隆过滤器作为一个插件加载到Redis Server中,给Redis提供了强大的布隆去重功能。
git clone git://github.com/RedisLabsModules/rebloom
cd rebloom
make # 在当前路径下生成rebloom.so文件
redis-server --loadmodule /path/to/rebloom.so # 启动redis服务器并使用前面生成的文件,或者在配置文件中添加"loadmodule /path/to/rebloom.so"
基本使用
布隆过滤器有两个基本指令,bf.add
添加元素,bf.exists
查询元素是否存在,bf.madd
一次添加多个元素,bf.mexists
一次查询多个元素。
127.0.0.1:6379> bf.add codehole user1
(integer) 1
127.0.0.1:6379> bf.add codehole user2
(integer) 1
127.0.0.1:6379> bf.add codehole user3
(integer) 1
127.0.0.1:6379> bf.exists codehole user1
(integer) 1
127.0.0.1:6379> bf.exists codehole user2
(integer) 1
127.0.0.1:6379> bf.exists codehole user3
(integer) 1
127.0.0.1:6379> bf.exists codehole user4
(integer) 0
127.0.0.1:6379> bf.madd codehole user4 user5 user6
1) (integer) 1
2) (integer) 1
3) (integer) 1
127.0.0.1:6379> bf.mexists codehole user4 user5 user6 user7
1) (integer) 1
2) (integer) 1
3) (integer) 1
4) (integer) 0
注意布隆过滤器对见过的元素肯定不会误判,只会误判没见过的元素。
布隆过滤器在第一次add的时候自动创建基于默认参数的过滤器,Redis还提供了自定义参数的布隆过滤器。
在add之前使用bf.reserve
指令显式创建,其有3个参数,key,error_rate, initial_size,错误率越低,需要的空间越大,error_rate表示预计错误率,initial_size参数表示预计放入的元素数量,当实际数量超过这个值时,误判率会上升,所以需要提前设置一个较大的数值来避免超出。默认的error_rate是0.01,initial_size是100。
注意事项
initial_size估计的过大会浪费存储空间,因此在使用前要尽可能精确估计好元素数量+冗余空间。
error_rate越小,需要的存储空间越大。
原理
每个布隆过滤器对应到Redis的数据结构中就是一个大型的位数组和几个不同的无偏hash函数,无偏表示分布均匀。
添加key时,使用多个hash函数对key进行hash运算得到一个整数索引值,对位数组长度进行取模运算得到一个位置,每个hash函数都会得到一个不同的位置,将这几个位置都置1就完成了add操作。
查询同理,只要有一位是0就表示这个key不存在,但如果都是1,则不一定存在对应的key。
空间占用估计
布隆过滤器的空间占用有一个简单的计算公式,但推导比较繁琐。布隆过滤器有两个参数,预计元素数量n,错误率f,公式得到两个输出,位数组长度L(即存储空间大小bit),hash函数的最佳数量k。
k = 0.7*(1/n)
f = 0.6185^(L/n)
- 位数组相对长度越长,错误率越低;
- 位数组相对长度越长,需要的hash函数越多;
- 当一个元素平均需要一个字节(8bit)的指纹空间时(L/n=8),错误率大约为2%。
实际元素超出时,误判率会怎样变化?
f = (1-0.5^t)^k # t为实际元素与预计元素的倍数
- 当错误率为10%时,倍数比为2时,错误率接近40%;
- 当错误率为1%,倍数比为2时,错误率15%;
- 当错误率为0.1%,倍数为2时,错误率5%。
布隆过滤器的其它应用
爬虫URL去重,NoSQL数据库领域降低数据库的IO请求数量,邮箱系统的垃圾邮件过滤。
原文地址:https://www.cnblogs.com/ikct2017/p/9499364.html