布隆过滤器在海量数据去重验证中应用

news2024/11/24 21:05:33

布隆过滤器在海量数据去重验证中应用

文章目录

  • 布隆过滤器在海量数据去重验证中应用
    • 引子
      • 面试结束级方案——从数据库中取
      • 新手级方案——利用redis的set数据结构
      • 专业级方案——利用布隆过滤器
    • 布隆过滤器
      • 基本概念
      • 优点
      • 缺点
      • 布隆过滤器的数据结构
      • 布隆过滤器的工作流程
      • 布隆过滤器的优缺点
    • Redis 中的布隆过滤器

引子

今天一个技术群里的小伙伴提了这样一个问题:

你爬虫的时候假如有海量的url,怎么保证不重复?

他说是之前在面试中遇到了这样的问题,大家第一时间想到的都差不多,用集合这种数据结构不久可以轻松的解决这个问题吗?

然而事情似乎没有这么简单,因为及时是存在Redis中,在海量数据的前提下,直接使用set作为数据存储来判断是否出现过,成本似乎比较高(内存条还是比较贵的。。。)

面试结束级方案——从数据库中取

爬虫每采集过一个 URL,就把这个 URL 存入数据库中,每次一个新的 URL 过来就到数据库查询下是否访问过。

select id from table where url = 'https://jaychen.cc'

但是随着爬虫爬过的 URL 越来越多,每次请求前都要访问数据库一次,并且对于这种字符串的 SQL 查询效率并不高。

新手级方案——利用redis的set数据结构

Redis 的 set 结构也可以满足这个需求,并且性能优于数据库,时间复杂度是O(1)。似乎一切都很美好,甚至还表现出了你对redis还是有一些了解的。但是 Redis 也存在一个问题:耗费过多的内存。

当这些问题中的海量,其实已经提示了,你必须想好如何应对这样的大数据。如果面试官提出了这样的问题,他显然不希望你只考虑到了,时间复杂度。

专业级方案——利用布隆过滤器

我们先看维基百科给出的解释:

布隆过滤器(英语:Bloom Filter)是1970年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。

布隆过滤器

基本概念

如果想判断一个元素是不是在一个集合里,一般想到的是将集合中所有元素保存起来,然后通过比较确定。链表、树、散列表(又叫哈希表,Hash table)等等数据结构都是这种思路。但是随着集合中元素的增加,我们需要的存储空间越来越大。同时检索速度也越来越慢,上述三种结构的检索时间复杂度分别为 O(n),O(log n),O(1)。

布隆过滤器的原理是,当一个元素被加入集合时,通过K个散列函数将这个元素映射成一个位数组中的K个点,把它们置为1。检索时,我们只要看看这些点是不是都是1就(大约)知道集合中有没有它了:如果这些点有任何一个0,则被检元素一定不在;如果都是1,则被检元素很可能在。这就是布隆过滤器的基本思想。

优点

相比于其它的数据结构,布隆过滤器在空间和时间方面都有巨大的优势。布隆过滤器存储空间和插入/查询时间都是常数O(k)。另外,散列函数相互之间没有关系,方便由硬件并行实现。布隆过滤器不需要存储元素本身,在某些对保密要求非常严格的场合有优势。

布隆过滤器可以表示全集,其它任何数据结构都不能;

k和m相同,使用同一组散列函数的两个布隆过滤器的交并[来源请求]运算可以使用位操作进行。

缺点

但是布隆过滤器的缺点和优点一样明显。误算率是其中之一。随着存入的元素数量增加,误算率随之增加。但是如果元素数量太少,则使用散列表足矣。

另外,一般情况下不能从布隆过滤器中删除元素。我们很容易想到把位数组变成整数数组,每插入一个元素相应的计数器加1, 这样删除元素时将计数器减掉就可以了。然而要保证安全地删除元素并非如此简单。首先我们必须保证删除的元素的确在布隆过滤器里面。这一点单凭这个过滤器是无法保证的。另外计数器回绕也会造成问题。

在降低误算率方面,有不少工作,使得出现了很多布隆过滤器的变种。

布隆过滤器的数据结构

布隆过滤器本质是一个位数组,位数组就是数组的每个元素都只占用 1 bit 。每个元素只能是 0 或者 1。这样申请一个 10000 个元素的位数组只占用 10000 / 8 = 1250 B 的空间。布隆过滤器除了一个位数组,还有 K 个哈希函数。当一个元素加入布隆过滤器中的时候,会进行如下操作:

  • 使用 K 个哈希函数对元素值进行 K 次计算,得到 K 个哈希值。
  • 根据得到的哈希值,在位数组中把对应下标的值置为 1。

假设布隆过滤器有 3 个哈希函数:f1, f2, f3 和一个位数组 arr。现在要把 https://baidu.com 插入布隆过滤器中:

  • 对值进行三次哈希计算,得到三个值 n1, n2, n3。
  • 把位数组中三个元素 arr[n1], arr[n2], arr[3] 置为 1。

当要判断一个值是否在布隆过滤器中,对元素再次进行哈希计算,得到值之后判断位数组中的每个元素是否都为 1,如果值都为 1,那么说明这个值在布隆过滤器中,如果存在一个值不为 1,说明该元素不在布隆过滤器中。

布隆过滤器的工作流程

布隆过滤器是一个 bit 向量或者说 bit 数组,长这样:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KSqWoqTq-1689517590231)(../pic/image-20230716222045874.png)]

如果我们要映射一个值到布隆过滤器中,我们需要使用多个不同的哈希函数生成**多个哈希值,**并对每个生成的哈希值指向的 bit 位置 1,例如针对值 “baidu” 和三个不同的哈希函数分别生成了哈希值 1、4、7,则上图转变为:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hHBttsCT-1689517590232)(../pic/image-20230716222324789.png)]

Ok,我们现在再存一个值 “tencent”,如果哈希函数返回 3、4、8 的话,图继续变为:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bD850QmK-1689517590232)(../pic/image-20230716222358980.png)]

值得注意的是,4 这个 bit 位由于两个值的哈希函数都返回了这个 bit 位,因此它被覆盖了。现在我们如果想查询 “dianping” 这个值是否存在,哈希函数返回了 1、5、8三个值,结果我们发现 5 这个 bit 位上的值为 0,说明没有任何一个值映射到这个 bit 位上,因此我们可以很确定地说 “dianping” 这个值不存在。而当我们需要查询 “baidu” 这个值是否存在的话,那么哈希函数必然会返回 1、4、7,然后我们检查发现这三个 bit 位上的值均为 1,那么我们可以说 “baidu” 存在了么?答案是不可以,只能是 “baidu” 这个值可能存在。

这是为什么呢?答案跟简单,因为随着增加的值越来越多,被置为 1 的 bit 位也会越来越多,这样某个值 “taobao” 即使没有被存储过,但是万一哈希函数返回的三个 bit 位都被其他值置位了 1 ,那么程序还是会判断 “taobao” 这个值存在。

布隆过滤器的优缺点

看了上面的说明,必然会提出一个问题:当插入的元素原来越多,位数组中被置为 1 的位置就越多,当一个不在布隆过滤器中的元素,经过哈希计算之后,得到的值在位数组中查询,有可能这些位置也都被置为 1。这样一个不存在布隆过滤器中的也有可能被误判成在布隆过滤器中。但是如果布隆过滤器判断说一个元素不在布隆过滤器中,那么这个值就一定不在布隆过滤器中。简单来说:

  • 布隆过滤器说某个元素在,可能会被误判。
  • 布隆过滤器说某个元素不在,那么一定不在。

这个布隆过滤器的缺陷放到上面爬虫的需求中,可能存在某些没有访问过的 URL 可能会被误判为访问过,但是如果是访问过的 URL 一定不会被误判为没访问过。

Redis 中的布隆过滤器

redis 在 4.0 的版本中加入了 module 功能,布隆过滤器可以通过 module 的形式添加到 redis 中,所以使用 redis 4.0 以上的版本可以通过加载 module 来使用 redis 中的布隆过滤器。但是这不是最简单的方式,使用 docker 可以直接在 redis 中体验布隆过滤器。

$ docker run -d -p 6379:6379 --name bloomfilter redislabs/rebloom
$ docker exec -it bloomfilter redis-cli

redis 布隆过滤器主要就两个命令:

  • bf.add 添加元素到布隆过滤器中:bf.add urls https://jaychen.cc
  • bf.exists 判断某个元素是否在过滤器中:bf.exists urls https://jaychen.cc

上面说过布隆过滤器存在误判的情况,在 redis 中有两个值决定布隆过滤器的准确率:

  • error_rate:允许布隆过滤器的错误率,这个值越低过滤器的位数组的大小越大,占用空间也就越大。
  • initial_size:布隆过滤器可以储存的元素个数,当实际存储的元素个数超过这个值之后,过滤器的准确率会下降。

redis 中有一个命令可以来设置这两个值:

bf.reserve urls 0.01 100

三个参数的含义:

  • 第一个值是过滤器的名字。
  • 第二个值为 error_rate 的值。
  • 第三个值为 initial_size 的值。

使用这个命令要注意一点:执行这个命令之前过滤器的名字应该不存在,如果执行之前就存在会报错:(error) ERR item exists

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/760816.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

位1的个数:一种高效且优雅的解法

本篇博客会讲解力扣“191. 位1的个数”的解题思路,这是题目链接。 要想求解二进制位中1的个数,首先要了解一个神奇的表达式:n & n - 1。这个表达式的含义是:把n的二进制中最低位的1变成0,也就是消除n的二进制中最低…

day5-环形链表II

142.环形链表II 力扣题目链接 给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。 如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环&am…

Prometheus实现钉钉报警

1、Prometheus实现钉钉报警 1.1 Prometheus环境 # my global config global:scrape_interval: 15s # Set the scrape interval to every 15 seconds. Default is every 1 minute.evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute…

位运算专题

异或:相同为0,相异为1,无进位相加 约定:给定的所有数从左到右依次是从低位到高位,下标从0开始 一)给定一个数,判断它的二进制位表示中的第X位是0还是1 1)(N>>X)&1,先将x位右移动到最低位,然后再将…

warp框架教程5-Filter系统中各个模块

any 模块 any 模块只有一个方法,就是 any 方法,它可以匹配任何路由的过滤器。我们可以使用 any 方法将一些可克隆的资源转换成一个过滤器,从而允许轻松地将它与其他 Filter 结合在一起。当然也可以使用 any 方法创建适用于多个 Filter 的末尾…

Android Hook技术实战详解

前些天发现了一个蛮有意思的人工智能学习网站,8个字形容一下"通俗易懂,风趣幽默",感觉非常有意思,忍不住分享一下给大家。 👉点击跳转到教程 前言: 什么是Android Hook技术? Android Hook技术是指在Android…

Sentinel流量规则模块(新增)

系统并发能力有限,比如系统A的QPS支持1个请求,如果太多请求过来,那么系统A就应该进行流量控制了,比如其他请求直接拒绝 新增流控规则介绍:新增流控规则窗口 1.资源名:默认请求路径。 2.针对来源:Se…

WebRTC基础

有用的网址: https://webrtc.org/ WebRTC API - Web API 接口参考 | MDN Browser APIs and Protocols: WebRTC - High Performance Browser Networking(OReilly) 浏览器中查看webrtc运行的实时信息: Chrome浏览器:chrome://webrtc-inter…

Linux(Ubuntu2004)实现kitti数据集的点云投影记录

1、新建终端打开roscore roscore 2、在“下载”路径里面播放example_new的bag rosbag play example_new.bag -l 3、在catkin_ws里面运行下列命令 rosrun ros_detection_tracking projector 如果不能运行,可以采用tab的方式查看是否错误 4、新建一个终端运行r…

Kafka 入门到起飞 - 生产者发送消息流程解析

生产者通过send()方法发送消息消息会经过拦截器->序列化器->分区器 进行加工然后将消息存在缓冲区当缓冲区中消息达到条件会按批次发送到broker对应分区上broker将接收到的消息进行刷盘持久化消息处理broker会返回给producer响应落盘成功返回元数据…

孟德尔随机化推断暴露因素与健康结局的因果关系

学习视频: 应用孟德尔随机化方法推断暴露因素与健康结局的因果关系 王友信教授 梅斯医学_哔哩哔哩_bilibili http://chinaepi.icdc.cn/zhlxbx/ch/reader/create_pdf.aspx?file_no20170427&flag1&journal_idzhlxbx&year_id2017 1. 孟德尔随机化方法 传…

详细介绍matlab使用支持向量机(SVM)预测股票市场趋势的实例

简介: 股票市场的趋势预测一直是投资者和交易员关注的重要问题之一。支持向量机(SVM)作为一种强大的机器学习算法,被广泛应用于股票市场趋势预测。本实例将介绍如何使用SVM来预测股票市场的涨跌趋势,并提供一个MATLAB代码示例。 数据准备: 首先,我们需要获取股票市场的历…

《面试1v1》Kafka优缺点别用错

🍅 作者简介:王哥,CSDN2022博客总榜Top100🏆、博客专家💪 🍅 技术交流:定期更新Java硬核干货,不定期送书活动 🍅 王哥多年工作总结:Java学习路线总结&#xf…

常用在线AI人工智能效率工具站点导航

详情请前往:常用在线AI人工智能工具站点导航

stm32(HAL库)使用printf函数打印到串口

目录 1、简介 2.1 基础配置 2.1.1 SYS配置 2.1.2 RCC配置 2.2 串口外设配置 2.3 项目生成 3、KEIL端程序整合 4、效果测试 1、简介 在HAL库中,常用的printf函数是无法使用的。本文通过重映射实现在HAL库中进行printf函数。 2.1 基础配置 2.1.1 SYS配置 2.1.2 …

quartus工具篇——modelsim的使用

文章目录 quartus工具篇——modelsim的使用1、modelsim简介二、使用教程1、新建工程添加文件 2、生成编写仿真文件3、设置仿真4、开启功能仿真5、开启时序仿真6、总结参考视频: quartus工具篇——modelsim的使用 1、modelsim简介 Quartus Prime是一款由英特尔开发…

【JavaEE初阶】博客系统的前端页面设计

文案分享~ 许多人终其一生都想从别人身上找寻爱, 以为爱是自然界的第二个太阳。 却忘了自己才是那道照耀全世界的光。 文章目录 1.博客主要组成页面2.博客列表页3.博客详情页4.博客登录页5.博客编辑页 1.博客主要组成页面 博客主要是由四个页面组成.分别是: 博…

3.3 Bootstrap 按钮组

文章目录 Bootstrap 按钮组基本的按钮组按钮工具栏按钮的大小嵌套垂直的按钮组 Bootstrap 按钮组 按钮组允许多个按钮被堆叠在同一行上。当你想要把按钮对齐在一起时,这就显得非常有用。您可以通过 Bootstrap 按钮(Button) 插件 添加可选的 J…

数据结构(2.1)——时间复杂度和空间复杂度计算

前言 (1)因为上一篇博客:数据结构(2)—算法对于时间复杂度和空间复杂度计算的讲解太少。所以我在次增加多个案例讲解。 (2)上一篇已经详细介绍了,为什么我们的算法要使用复杂度这一个…

Docker部署jar包运行

目录 场景 方法 运行 场景 springboot项目,打包成jar包,想要放到docker中运行 这种方式就是运行一个jdk的容器,然后挂载其中的目录到宿主机,然后运行之后,就可以将需要运行的jar放在宿主机的挂载目录下,然后每次…