想知道某一个值是不是已经在 HyperLogLog 结构里面了,它就无能为力了,它只提供了 pfadd 和 pfcount 方法,没有提供 pfcontains 这种方法。

讲个使用场景,比如我们在使用新闻客户端看新闻时,它会给我们不停地推荐新的内容,它每次推荐时要去重,去掉那些已经看过的内容。问题来了,新闻客户端推荐系统如何实现推送去重的?

布隆过滤器是什么?

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

Redis 中的布隆过滤器

Redis 官方提供的布隆过滤器到了 Redis 4.0 提供了插件功能之后才正式登场。布隆过滤器作为一个插件加载到 Redis Server 中,给 Redis 提供了强大的布隆去重功能。

下面我们来体验一下 Redis 4.0 的布隆过滤器,为了省去繁琐安装过程,我们直接用 Docker 吧。

> docker pull redislabs/rebloom  # 拉取镜像
> docker run -p6379:6379 redislabs/rebloom  # 运行容器
> redis-cli  # 连接容器中的 redis 服务

如果上面三条指令执行没有问题,下面就可以体验布隆过滤器了。

布隆过滤器基本使用

布隆过滤器有二个基本指令,bf.add 添加元素,bf.exists 查询元素是否存在,它的用法和 set 集合的 sadd 和 sismember 差不多。注意 bf.add 只能一次添加一个元素,如果想要一次添加多个,就需要用到 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

Java 客户端 Jedis-2.x 没有提供指令扩展机制,所以你无法直接使用 Jedis 来访问 Redis Module 提供的 bf.xxx 指令。RedisLabs 提供了一个单独的包 JReBloom,但是它是基于 Jedis-3.0,Jedis-3.0 这个包目前还没有进入 release,没有进入 maven 的中央仓库,需要在 Github 上下载。在使用上很不方便,如果怕麻烦,还可以使用 lettuce,它是另一个 Redis 的客户端,相比 Jedis 而言,它很早就支持了指令扩展。

public class BloomTest {public static void main(String[] args) {Client client = new Client();client.delete("codehole");for (int i = 0; i < 100000; i++) {client.add("codehole", "user" + i);boolean ret = client.exists("codehole", "user" + i);if (!ret) {System.out.println(i);break;}}client.close();}}

误判率大约 1% 多点。你也许会问这个误判率还是有点高啊,有没有办法降低一点?答案是有的。

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

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

接下来我们使用 bf.reserve 改造一下上面的脚本:

public class BloomTest {private String chars;{StringBuilder builder = new StringBuilder();for (int i = 0; i < 26; i++) {builder.append((char) ('a' + i));}chars = builder.toString();}private String randomString(int n) {StringBuilder builder = new StringBuilder();for (int i = 0; i < n; i++) {int idx = ThreadLocalRandom.current().nextInt(chars.length());builder.append(chars.charAt(idx));}return builder.toString();}private List<String> randomUsers(int n) {List<String> users = new ArrayList<>();for (int i = 0; i < 100000; i++) {users.add(randomString(64));}return users;}public static void main(String[] args) {BloomTest bloomer = new BloomTest();List<String> users = bloomer.randomUsers(100000);List<String> usersTrain = users.subList(0, users.size() / 2);List<String> usersTest = users.subList(users.size() / 2, users.size());Client client = new Client();client.delete("codehole");// 对应 bf.reserve 指令client.createFilter("codehole", 50000, 0.001);for (String user : usersTrain) {client.add("codehole", user);}int falses = 0;for (String user : usersTest) {boolean ret = client.exists("codehole", user);if (ret) {falses++;}}System.out.printf("%d %d\n", falses, usersTest.size());client.close();}}

运行一下,等待约 1 分钟,输出如下:

total users 100000
all trained
6 50000

我们看到了误判率大约 0.012%,比预计的 0.1% 低很多,不过布隆的概率是有误差的,只要不比预计误判率高太多,都是正常现象。

注意事项

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

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

布隆过滤器的原理

学会了布隆过滤器的使用,下面有必要把原理解释一下,不然读者还会继续蒙在鼓里

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

向布隆过滤器中添加 key 时,会使用多个 hash 函数对 key 进行 hash 算得一个整数索引值然后对位数组长度进行取模运算得到一个位置,每个 hash 函数都会算得一个不同的位置。再把位数组的这几个位置都置为 1 就完成了 add 操作。

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

【Redis】布隆过滤器相关推荐

  1. Google布隆过滤器与Redis布隆过滤器详解

    一.什么是布隆过滤器? 布隆过滤器可以用来判断一个元素是否在一个集合中.它的优势是只需要占用很小的内存空间以及有着高效的查询效率.对于布隆过滤器而言,它的本质是一个位数组:位数组就是数组的每个元素都只 ...

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

    文章目录 布隆过滤器 - Redis 布隆过滤器,Guava 布隆过滤器 BloomFilter 1.布隆过滤器的起源,用途 2.布隆过滤器的概念 3.布隆过滤器的优缺点 1.优点 2.缺点 4.应用 ...

  3. 服务器环境部署:Redis布隆过滤器使用

    老早就想在项目中用起来这个优秀的东西.只是成熟的项目又有很多私有客户部署,redis版本可能存在差异,为避免不必要的版本兼容或迁移,就没有大幅度的在成熟项目上使用.现新项目刚好有相关使用需求,所以理所 ...

  4. ubuntu16.04安装,使用redis布隆过滤器示例

    简言 1. 环境:ubuntu16.04,redis版本:5.0.7,布隆过滤器实现版本:RedisBloom1.1.1 2. 默认情况,下载安装redis时是不带布隆过滤器功能的,它是以插件的形式提 ...

  5. Redis布隆过滤器

    正文 场景 在项目开发中,我们经常会遇到去重问题.比如:判断一个人有没有浏览过一篇文章,判断一个人当天是否登录过某个系统,判断一个ip是否发过一个请求,等等. 比较容易想到的是使用set来实现这个功能 ...

  6. redis布隆过滤器PHP,Redis 中的布隆过滤器

    什么是『布隆过滤器』 布隆过滤器是一个神奇的数据结构,可以用来判断一个元素是否在一个集合中.很常用的一个功能是用来去重.在爬虫中常见的一个需求:目标网站 URL 千千万,怎么判断某个 URL 爬虫是否 ...

  7. 深入详解Redis布隆过滤器

    前面学习HyperLogLog数据类型来进行估算,还是非常有意义的,能解决很多精度要求不高的统计问题. 但是对于某一个值是否存在于HyperLogLog结构里面,就变现的无能为力,因为它只提供了 pf ...

  8. Redis布隆过滤器与布谷鸟过滤器

    -     目录    - 大家都知道,在计算机中,IO一直是一个瓶颈,很多框架以及技术甚至硬件都是为了降低IO操作而生,今天聊一聊过滤器,先说一个场景: 我们业务后端涉及数据库,当请求消息查询某些信 ...

  9. 【342期】SpringBoot + Redis 布隆过滤器防恶意流量击穿缓存的正确姿势!

    什么是恶意流量穿透 假设我们的Redis里存有一组用户的注册email,以email作为Key存在,同时它对应着DB里的User表的部分字段. 一般来说,一个合理的请求过来我们会先在Redis里判断这 ...

  10. Redis 布隆过滤器

    什么是布隆过滤器 本质上布隆过滤器是一种数据结构,比较巧妙的概率型数据结构(probabilistic data structure),特点是高效地插入和查询,可以用来告诉你 "某样东西一定 ...

最新文章

  1. C/C++基础问题归集
  2. 记mac电脑下pycharm配置qt-creator开发环境
  3. BugKu:cookies 欺骗
  4. oracle 定时任务 job 调用存储过程有回到输出参数(含out参数)
  5. pyqt5讲解8:容器QTabWidget,QStackedWidget,QDockWidget
  6. java 的xml_详解Java解析XML的四种方法
  7. deepin安装卡死在蓝色背景_求大神帮助~安装DEEPIN系统卡在蓝色背景图什么原因...
  8. linux强制移除pdf密码,分享|如何在 Linux 中从一个 PDF 文件中移除密码
  9. 使用Eclipse编写Processing小程序
  10. poj 匈牙利二分匹配算法2239 Selecting Courses
  11. python运维是什么_什么是python自动化运维?
  12. 示波器的使用和二极管充放电过程
  13. jquery API参考手册
  14. Paxos 算法详解
  15. 软件测试好书推荐《自动化测试实践》30个项目测试案例分析
  16. html邮件模板美化,设计利器:定制你的炫酷邮件模板
  17. 学计算机的男孩情商高吗,男孩情商高的特征,家长快来看看
  18. 用友财务帐套升级:T3升级U8
  19. 定义一个电话簿,用人名查电话
  20. 显示地图不出来的问题解决

热门文章

  1. DINO: 让目标检测拥抱Transformer!霸榜COCO!
  2. Docker容器技术与应用(项目1 Docker容器简介)
  3. spring系统学习之控制反转 ioc
  4. matlab实现多元高斯分布概率密度计算
  5. 【YOLOV5-5.x 源码解读】google_utils.py
  6. npm WARN read-shrinkwrap This version of npm is compatible with lockfileVersion@1, but package-lock
  7. thrift编写规则,及常见问题
  8. 最新章节 第238章 超级计算机的安排,第238章 黄花大闺女
  9. linux运行pycharm显示Already running
  10. Python学习笔记 第四天