Redis深度历险-Redis 布隆过滤器

本文大部分内容引自《Redis深度历险:核心原理和应用实践》,感谢作者!!!

布隆过滤器作用

1、布隆过滤器 (Bloom Filter)是专门用来解决去重问题的(新闻中已经看过的新闻去除,用户行为历史记录,邮箱系统的垃圾邮件过滤,爬虫URL去重;HBase、Cassandra 还有 LevelDB、RocksDB 内部都有布隆过滤器结构,布隆过滤器可以显著降低数据库的 IO 请求数量。当用户来查询某个 row 时,可以先通过内存中的布隆过滤器过滤掉大量不存在的 row 请求,然后再去磁盘进行查询)。在起到去重的同时,在空间上还能节省 90% 以上(因为存储的是元素指纹),只是稍微有那么点不精确,也就是有一定的误判概率

2、布隆过滤器可以理解为一个不怎么精确的 set 结构,当你使用它的 contains 方法判断某个对象是否存在时,它可能会误判。但是布隆过滤器也不是特别不精确,只要参数设置的合理,它的精确度可以控制的相对足够精确,只会有小小的误判概率(当布隆过滤器说某个值存在时,这个值可能不存在;当它说不存在时,那就肯定不存在)

3、布隆过滤器有二个基本指令,bf.add 添加元素,bf.exists查询元素是否存在,它的用法和set集合的sadd和sismember差不多。注意bf.add只能一次添加一个元素,如果想要一次添加多个,就需要用到bf.madd指令。同样如果需要一次查询多个元素是否存在,就需要用到bf.mexists指令

4、布隆过滤器对于已经见过的元素肯定不会误判,它只会误判那些没见过的元素

5、布隆过滤器的initial_size估计的过大会浪费存储空间,估计的过小就会影响准确率,用户在使用之前一定要尽可能地精确估计好元素数量,还需要加上一定的冗余空间以避免实际元素可能会意外高出估计值很多

6、布隆过滤器的error_rate越小,需要的存储空间就越大,对于不需要过于精确的场合,error_rate设置稍大一点也无伤大雅。比如在新闻去重上而言,误判率高一点只会让小部分文章不能让合适的人看到,文章的整体阅读量不会因为这点误判率就带来巨大的改变

布隆过滤器基本使用

bf.add [key] [value] #向key中添加value
bf.exists [key] [value] #检测value是否存在
bf.madd [key] [value0 ... value] #向key中添加多个value
bf.mexists [key] [value0 ... value] #检查value0 ... value是否存在
bf.reserve [key] [error_rate] [initial_size] #创建一个错误率为error_rate,容量为initial_size的布隆过滤器

 

自定义参数使用布隆过滤器

1、Redis提供了自定义参数的布隆过滤器,需要我们在add之前使用bf.reserve指令显式创建。如果对应的key已经存在,bf.reserve会报错。bf.reserve有三个参数,分别是 key,error_rate和initial_size。错误率越低,需要的空间越大。initial_size参数表示预计放入的元素数量,当实际数量超出这个数值时,误判率会上升

2、布隆过滤器需要提前设置一个较大的数值避免超出导致误判率升高。如果不使用 bf.reserve,默认的error_rate是0.01,默认的initial_size是100

 

布隆过滤器防止缓存击穿和缓存雪崩:https://blog.csdn.net/u011277123/article/details/88757861

 

 

布隆过滤器原理

Redis深度历险-Redis 布隆过滤器_第1张图片

1、每个布隆过滤器对应到Redis的数据结构里面就是一个大型的位数组和几个不一样的无偏hash函数。所谓无偏就是能够把元素的hash值算得比较均匀

2、向布隆过滤器中添加key时,会使用多个hash函数对key进行hash算得一个整数索引值然后对位数组长度进行取模运算得到一个位置,每个hash函数都会算得一个不同的位置。再把位数组的这几个位置都置为1就完成了add操作(这里可以解释为什么容量超出的时候误判率会增加,因为大多数位都为1)

3、向布隆过滤器询问 key 是否存在时,跟add一样,也会把hash的几个位置都算出来,看看位数组中这几个位置是否都位 1,只要有一个位为 0,那么说明布隆过滤器中这个key不存在。如果都是 1,这并不能说明这个key就一定存在,只是极有可能存在,因为这些位被置为1可能是因为其它的key存在所致。如果这个位数组比较稀疏,这个概率就会很大,如果这个位数组比较拥挤,这个概率就会降低

4、使用时不要让实际元素远大于初始化大小,当实际元素开始超出初始化大小时,应该对布隆过滤器进行重建,重新分配一个size更大的过滤器,再将所有的历史元素批量add进去 (这就要求我们在其它的存储器中记录所有的历史元素)。因为error_rate不会因为数量超出就急剧增加,这就给我们重建过滤器提供了较为宽松的时间

 

布隆过滤器占用空间计算

    预计元素数量n,错误率f,数组位(bit)数l(英文字母小写L),hash函数的最佳数量k

    布隆过滤器有两个参数,第一个是预计元素的数量n,第二个是错误率f。公式根据这两个输入得到两个输出,第一个输出是位数组的长度 l,也就是需要的存储空间大小 (bit),第二个输出是hash函数的最佳数量k。hash函数的数量也会直接影响到错误率,最佳的数量会有最低的错误率

k=0.7*(l/n) # 约等于
f=0.6185^(l/n) # ^ 表示次方计算,也就是 math.pow

 

从公式中可以看出

    1、位数组相对越长 (l/n),错误率f越低,这个和直观上理解是一致的

    2、位数组相对越长 (l/n),hash函数需要的最佳数量也越多,影响计算效率

    3、当一个元素平均需要1个字节 (8bit)的指纹空间时(l/n=8),错误率大约为2%

    4、错误率为10%,一个元素需要的平均指纹空间为4.792个bit,大约为5bit

    5、错误率为1%,一个元素需要的平均指纹空间为9.585个bit,大约为10bit

    6、错误率为0.1%,一个元素需要的平均指纹空间为14.377个bit,大约为15bit

    你也许会想,如果一个元素需要占据 15 个 bit,那相对 set 集合的空间优势是不是就没有那么明显了?这里需要明确的是,set 中会存储每个元素的内容,而布隆过滤器仅仅存储元素的指纹。元素的内容大小就是字符串的长度,它一般会有多个字节,甚至是几十个上百个字节,每个元素本身还需要一个指针被 set 集合来引用,这个指针又会占去 4 个字节或 8 个字节,取决于系统是 32bit 还是 64bit。而指纹空间只有接近 2 个字节,所以布隆过滤器的空间优势还是非常明显的

 

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

当实际元素超出预计元素时,错误率会有多大变化,它会急剧上升么,还是平缓地上升,这就需要另外一个公式,引入参数t表示实际元素和预计元素的倍数t

f=(1-0.5^t)^k # 极限近似,k 是 hash 函数的最佳数量

 

Redis深度历险-Redis 布隆过滤器_第2张图片

从这个图中可以看出曲线还是比较陡峭的

1、错误率为10%时,倍数比为2时,错误率就会升至接近40%,这个就比较危险了

2、错误率为1% 时,倍数比为2时,错误率升至15%,也挺可怕的

3、错误率为0.1%,倍数比为2时,错误率升至5%,也比较悬了

 

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(Redis,Redis)