深入Redis布隆过滤器

Posted ikct2017

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深入Redis布隆过滤器相关的知识,希望对你有一定的参考价值。

布隆过滤器

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)
  1. 位数组相对长度越长,错误率越低;
  2. 位数组相对长度越长,需要的hash函数越多;
  3. 当一个元素平均需要一个字节(8bit)的指纹空间时(L/n=8),错误率大约为2%。

实际元素超出时,误判率会怎样变化?

f = (1-0.5^t)^k  # t为实际元素与预计元素的倍数
  1. 当错误率为10%时,倍数比为2时,错误率接近40%;
  2. 当错误率为1%,倍数比为2时,错误率15%;
  3. 当错误率为0.1%,倍数为2时,错误率5%。

布隆过滤器的其它应用

爬虫URL去重,NoSQL数据库领域降低数据库的IO请求数量,邮箱系统的垃圾邮件过滤。


以上是关于深入Redis布隆过滤器的主要内容,如果未能解决你的问题,请参考以下文章

布隆过滤器实现代码php+redis

Redis详解(十三)------ Redis布隆过滤器

Redis详解(十三)------ Redis布隆过滤器

布隆过滤器 - Redis 布隆过滤器,Guava 布隆过滤器 BloomFilter

redis中布隆过滤器使用详解

redis 十五. 缓存穿透 与布隆过滤器原理