某一个值是否是已经在 HyperLogLog 结构里面了,它就无能为力了,它只提供了 pfadd 和 pfcount 方法,没有提供 pfcontains 这种方法git
使用场景: github
1. 好比咱们在使用新闻客户端看新闻时,它会给咱们不停地推荐新的内容,它每次推荐时要去重,去掉那些已经看过的内容。问题来了,新闻客户端推荐系统如何实现推送去重的?redis
2. 缓存击穿数组
布隆过滤器(redis 4.0)能够理解为一个不怎么精确的 set 结构,当你使用它的 contains 方法判断某个对象是否存在时,它可能会误判。可是布隆过滤器也不是特别不精确,只要参数设置的合理,它的精确度能够控制的相对足够精确,只会有小小的误判几率。缓存
当布隆过滤器说某个值存在时,这个值可能不存在;当它说不存在时,那就确定不存在。打个比方,当它说不认识你时,确定就不认识;当它说见过你时,可能根本就没见过面,不过由于你的脸跟它认识的人中某脸比较类似 (某些熟脸的系数组合),因此误判之前见过你。数据结构
原理:函数
每一个布隆过滤器对应到 Redis 的数据结构里面就是一个大型的位数组和几个不同的无偏 hash 函数。所谓无偏就是可以把元素的 hash 值算得比较均匀。网站
向布隆过滤器中添加 key 时,会使用多个 hash 函数对 key 进行 hash 算得一个整数索引值而后对位数组长度进行取模运算获得一个位置,每一个 hash 函数都会算得一个不一样的位置。再把位数组的这几个位置都置为 1 就完成了 add 操做。(多个hash函数是为了减小碰撞,视为状况而定,hash太多,消耗CPU)spa
向布隆过滤器询问 key 是否存在时,跟 add 同样,也会把 hash 的几个位置都算出来,看看位数组中这几个位置是否都为 1,只要有一个位为 0,那么说明布隆过滤器中这个 key 不存在。若是都是 1,这并不能说明这个 key 就必定存在,只是极有可能存在,由于这些位被置为 1 多是由于其它的 key 存在所致。若是这个位数组比较稀疏,判断正确的几率就会很大,若是这个位数组比较拥挤,判断正确的几率就会下降。具体的几率计算公式比较复杂,感兴趣能够阅读扩展阅读,很是烧脑,不建议读者细看。指针
使用时不要让实际元素远大于初始化大小,当实际元素开始超出初始化大小时,应该对布隆过滤器进行重建,从新分配一个 size 更大的过滤器,再将全部的历史元素批量 add 进去 (这就要求咱们在其它的存储器中记录全部的历史元素)。由于 error_rate 不会由于数量超出就急剧增长,这就给咱们重建过滤器提供了较为宽松的时间。
布隆过滤器的空间占用有一个简单的计算公式,可是推导比较繁琐,这里就省去推导过程了,直接引出计算公式,感兴趣的读者能够点击「扩展阅读」深刻理解公式的推导过程。
布隆过滤器有两个参数,第一个是预计元素的数量 n,第二个是错误率 f。公式根据这两个输入获得两个输出,第一个输出是位数组的长度 l,也就是须要的存储空间大小 (bit),第二个输出是 hash 函数的最佳数量 k。hash 函数的数量也会直接影响到错误率,最佳的数量会有最低的错误率。
k=0.7*(l/n) # 约等于 f=0.6185^(l/n) # ^ 表示次方计算,也就是 math.pow
从公式中能够看出
你也许会想,若是一个元素须要占据 15 个 bit,那相对 set 集合的空间优点是否是就没有那么明显了?这里须要明确的是,set 中会存储每一个元素的内容,而布隆过滤器仅仅存储元素的指纹。元素的内容大小就是字符串的长度,它通常会有多个字节,甚至是几十个上百个字节,每一个元素自己还须要一个指针被 set 集合来引用,这个指针又会占去 4 个字节或 8 个字节,取决于系统是 32bit 仍是 64bit。而指纹空间只有接近 2 个字节,因此布隆过滤器的空间优点仍是很是明显的。
若是读者以为公式计算起来太麻烦,也没有关系,有不少现成的网站已经支持计算空间占用的功能了,咱们只要把参数输进去,就能够直接看到结果,好比 布隆计算器。