关于布隆过滤器:布隆过滤器-与-Redis-BitMap

1. 前言在前些年的开发中就遇到几个相似的场景: 场景1场景:我以后业务表构造冗余了人员的信息字段,当人员的根本信息产生变更或删除时,会推送MQ。我以后业务监听到有人员信息变更的MQ音讯,会查数据库,看看该人员在以后表中是否存在。如果存在则更新,如果不存在则无需解决。 问题:每当监听到人员变更的的MQ音讯,就须要查问表中是否存在,给数据库带来耗费。 尝试计划:将以后表中的所有人员ID,存入redis Set汇合中。在监听到人员变更的MQ音讯时,先去Set中检查一下是否存在。如果存在再去更新数据库。 尝试计划的问题:当人员数量几十万的时候,redis Set汇合也会很大,redis key太大影响性能,检查人员是否存在也会变慢。 场景2咱们有些高频调用的查问接口,因为调用频率高,查库简单,因而曾经做了缓存。缓存的策略是:如果查问对应的缓存key存在,则从缓存获取;如果不存在,则在查库,如果查库能取得有效值,再将后果存入缓存。 带来的问题:接口对外裸露后,因为前台调用方不可控,可能总会查问不存在的key。造成缓存穿透,频繁的有效查库。 尝试计划:针对缓存穿透,当查问某个key,在库中不存在时。在缓存中仍然创立一个key,value设为null。 尝试计划的问题:设为null的key,就只有等过期工夫到了后,自行删除。有些时候上一秒查库不存在,给对应的key缓存了一个为null的value。但下一秒库中有了,走缓存拿到的仍然是null。 更好的解决方案对于下面可能遇到的问题,和尝试的解决方案,有一个更好的解决方案:布隆过滤器。 能够了解为场景1外面的那个Set,只不过容量更小,检索性能更高。那么针对场景2缓存穿透的问题,将所有的缓存key存入布隆过滤器中,如果在过滤器中的key,再通过缓存、数据库获取。 2. 布隆过滤器布隆过滤器(Bloom Filter)是 1970 年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。次要用于判断一个元素是否在一个汇合中。 通常咱们会遇到很多要判断一个元素是否在某个汇合中的业务场景,个别想到的是将汇合中所有元素保存起来,而后通过比拟确定。链表、树、散列表(又叫哈希表,Hash table)等等数据结构都是这种思路。然而随着汇合中元素的减少,咱们须要的存储空间也会出现线性增长,最终达到瓶颈。同时检索速度也越来越慢。上述三种构造的检索工夫复杂度别离为: O(n):链表O(logn):树O(1):哈希表这个时候,布隆过滤器(Bloom Filter)就应运而生。 2.1. 原理当一个元素被退出汇合时,通过N个Hash函数将这个元素进行Hash,算出一个整数索引值,而后对数组长度进行取模运算,从而失去一个地位,每个Hash函数都会失去一个不同的地位,而后再把位数组中的几个地位点都置为1。 检索时,也会把哈希的几个地位算进去,而后看看位数组中对应的几个地位是否都为1,只有有一个位为0,那么就阐明布隆过滤器里不存在这个元素。 然而,这几个地位都为1,并不能齐全阐明这个元素就肯定存在其中。因为散列函数是会有碰撞的,不同的输出,可能在哈希后为同一个地位。即有可能这些地位为1是因为其余元素的存在,这就是布隆过滤器会呈现误判的起因。 因而查问某个变量的时候咱们只有看看这些点是不是都是 1 就能够大概率晓得汇合中有没有它了 如果这些点有任何一个 0,则被查问变量肯定不在。如果都是 1,则被查问变量很可能存在。2.2. 个性与优缺点1. 不存在时肯定不存在一个元素如果判断后果为存在的时候元素不肯定存在,然而判断后果为不存在的时候则肯定不存在。 起因:布隆过滤器的误判是指多个输出通过哈希之后,在雷同的bit位的值置1了,这样就无奈判断到底是哪个输出产生的,因而误判的本源在于雷同的 bit 位被屡次映射且置 1。 2. 只增不删布隆过滤器能够增加元素,然而不能删除元素。因为删掉元素会导致误判率减少。 起因:上述起因中的状况,多个输出通过哈希之后,在雷同的bit位的值置1了,也造成了布隆过滤器的删除问题。因为布隆过滤器的每一个 bit 并不是独占的,很有可能多个元素共享了某一位。如果咱们间接删除这一位的话,会影响其余的元素。 如果须要删除一批元素,能够思考从新初始化一个布隆过滤器,替换原来的。 3. 长处占用空间极小,插入和查问速度极快;布隆过滤器能够示意选集,其它任何数据结构都不能;3. 毛病误算率随着元素的减少而减少;个别状况下无奈删除元素;2.3. 利用场景基于上述的性能,咱们大抵能够把布隆过滤器用于以下的场景之中: 1. 大数据判断是否存在来实现去重这就能够实现出上述的去重性能,如果你的服务器内存足够大的话,那么应用 HashMap 可能是一个不错的解决方案,实践上工夫复杂度能够达到 O(1) 的级别,然而当数据量起来之后,还是只能思考布隆过滤器。 2. 判断用户是否拜访过判断用户是否浏览过某视频或文章,比方抖音或头条,当然会导致肯定的误判,但不会让用户看到反复的内容。 3. 爬虫/邮箱等零碎的过滤平时不晓得你有没有留神到有一些失常的邮件也会被放进垃圾邮件目录中,这就是应用布隆过滤器误判导致的。 4. 人造适宜缓存穿透场景布隆过滤器人造就能应答缓存穿透的场景。 首先,布隆过滤器的利用策略正好和缓存是相同的: 缓存策略:缓存中不存在的,再去查db。布隆过滤器策略:过滤器中存在的,再去查缓存(db)。而后,因为它的个性:一个元素如果判断后果为存在的时候元素不肯定存在,然而判断后果为不存在的时候则肯定不存在。 这表明它的误判率并不影响它的策略: 当判断后果为存在时:不肯定存在。带来的不好的后果,顶多就是多查一次缓存。当判断后果为不存在时:肯定不存在。策略中判断不存在时,以后申请就会被拦挡,这方面是没有误判的。所以说,布隆过滤器人造适宜缓存穿透的场景,它的误判率对与该场景没有丝毫影响。 2.4. 实现有很多布隆过滤器的实现,就如同之前将限流器的实现有 guava 和 redisson,布隆过滤器的实现也一样。 ...

March 6, 2023 · 2 min · jiezi

关于布隆过滤器:布隆过滤器BoomFilter学习

一、什么是布隆过滤器(BoomFilter)Bloom Filter是一种空间效率很高的随机数据结构,它利用位数组很简洁地示意一个汇合,并能判断一个元素是否属于这个汇合。它实际上是一个很长的二进制向量和一系列随机映射函数。 特点总结为如下: 空间效率高的概率型数据结构,用来查看一个元素是否在一个汇合中。对于一个元素检测是否存在的调用,BloomFilter会通知调用者两个后果之一:可能存在或者肯定不存在。二、应用场景解决缓存穿透咱们常常会把一部分数据放在Redis等缓存,比方产品详情。这样有查问申请进来,咱们能够依据产品Id间接去缓存中取数据,而不必读取数据库,这是晋升性能最简略,最广泛,也是最无效的做法。个别的查问申请流程是这样的:先查缓存,有缓存的话间接返回,如果缓存中没有,再去数据库查问,而后再把数据库取出来的数据放入缓存,所有看起来很美妙。然而如果当初有大量申请进来,而且都在申请一个不存在的产品Id,会产生什么?既然产品Id都不存在,那么必定没有缓存,没有缓存,那么大量的申请都怼到数据库,数据库的压力一下子就上来了,还有可能把数据库打死。尽管有很多方法都能够解决这问题,然而咱们的配角是“布隆过滤器”,没错,“布隆过滤器”就能够解决(缓解)缓存穿透问题。至于为什么说是“缓解”,看上来你就明确了。大量数据,判断给定的是否在其中当初有大量的数据,而这些数据的大小曾经远远超出了服务器的内存,当初再给你一个数据,如何判断给你的数据在不在其中。如果服务器的内存足够大,那么用HashMap是一个不错的解决方案,实践上的工夫复杂度能够达到O(1),然而当初数据的大小曾经远远超出了服务器的内存,所以无奈应用HashMap,这个时候就能够应用“布隆过滤器”来解决这个问题。然而还是同样的,会有肯定的“误判率”。进行垃圾邮件过滤(垃圾数据过滤)网页爬虫对 URL 的去重,防止爬取雷同的 URL 地址三、实现原理布隆过滤器的外围实现是一个超大的位数组和几个哈希函数。假如位数组的长度为 m,哈希函数的个数为 k。以上图为例,具体的操做流程:假如汇合外面有 3 个元素 {x, y, z},哈希函数的个数为 3。首先将位数组进行初始化,将外面每一个位都设置位 0。对于汇合外面的每个元素,将元素顺次通过 3 个哈希函数进行映射,每次映射都会产生一个哈希值,这个值对应位数组下面的一个点,而后将位数组对应的地位标记为 1。查问 W 元素是否存在汇合中的时候,一样的办法将 W 通过哈希映射到位数组上的 3 个点。若是 3 个点的其中有一个点不为 1,则可能判断该元素必然不存在汇合中。反之,若是 3 个点都为 1,则该元素可能存在汇合中。留神:此处不能判断该元素是否必然存在汇合中,可能存在必然的误判率。可能从图中可能看到:假如某个元素通过映射对应下标为 四、五、6 这 3 个点。尽管这 3 个点都为 1,可是很显著这 3 个点是不一样元素通过哈希取得的地位,所以这种情况阐明元素尽管不在汇合中,也可能对应的都是 1,这是误判率存在的原因。 劣势相比于其它的数据结构,布隆过滤器在空间和工夫方面都有微小的长处。布隆过滤器存储空间和插入/查问工夫都是常数。另外,Hash 函数相互之间没有关系,不便由硬件并行实现。布隆过滤器不须要存储元素本人,在某些对窃密要求很是严格的场合有长处。布隆过滤器可能示意选集,其它任何数据结构都不能。 毛病可是布隆过滤器的毛病和劣势同样显著。误算率(False Positive)是其中之一。随着存入的元素数量增长,误算率随之增长(误判补救办法是:再创立一个小的白名单,存储那些可能被误判的信息)。可是若是元素数量太少,则应用散列表足矣。另外,通常情况下不能从布隆过滤器中删除元素。咱们很容易想到把位列阵变成整数数组,每插入一个元素相应的计数器加 1, 这样删除元素时将计数器减掉就可能了。然而要保障平安的删除元素并不是如此简略。首先咱们必须保障删除的元素确实在布隆过滤器外面. 这一点单凭这个过滤器是没法保障的。另外计数器回绕也会造成问题。 四、举荐应用Guava的布隆过滤器 Maven: <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>22</version></dependency>建设 BloomFilter 对象,须要传入 Funnel 对象,预估的元素个数,误判率。 BloomFilter<String> filter = BloomFilter.create(Funnels.stringFunnel(Charset.defaultCharset()), 1000,0.01);应用 put 办法增加元素: filter.put("test");应用 mightContain 办法判断元素是否存在: ...

February 18, 2022 · 1 min · jiezi

关于布隆过滤器:如何抗住亿级流量之布隆过滤器

前言很久没写文章了,明天来学习下赫赫有名的 布隆过滤器 。 注释什么是布隆过滤器这个牛轰轰的神器是布隆这位大牛在 1970 年创造的,是一个二进制向量数据结构,过后专门解决数据查问问题。能够用来通知你 某样货色肯定不存在或者可能存在。 相比于传统的 List、Set、Map 等数据结构,它更高效、占用空间更少,然而毛病是其返回的后果是概率性的,而不是确切的。 布隆过滤器有什么用说起布隆过滤器的作用能够大家可能首先想到的就是Redis缓存穿透,那咱们先来说说什么是缓存穿透? 你必定上过京东、淘宝这样的购物网站,不知有没有留神过:在咱们日常开发中,其实每一个页面的 URL 网址是和具体的商品对应的。 比如说,以后我标红的 857,就是咱们商品的 sku 编号,你能够了解为是这个商品型号的惟一编码。这里商品编号为 857,那显示的页面天然也是对应的内容。 比方在 618 当天,数亿网友 shopping 剁手时,商城利用同时收到海量申请,要求拜访、下单、领取,这时机器如何顶得住呢?这就波及咱们零碎的架构了,来看一下。 首先作为商城的用户,他发动一个申请,比方这里还是要查看 857 号的商品。 这时作为商城应用程序,它会向后盾的 Redis 缓存服务器进行查问。 如果缓存数据库中没有 857 号的商品数据,咱们程序就须要在后盾的数据库服务器中进行查问,并且填充至 Redis 服务器,这是一个失常的操作流程。 那么在长时间积攒后,咱们的缓存服务器外面的数据可能就是这个样子的。为了好了解,这里我假如商城有 1000 件商品,编号从 1~1000。 此时此刻作为商城用户,如果查问 857 号商品时,商城利用就不再须要从 MySQL 数据库中进行数据提取,间接从 Redis 服务器中将数据提取并返回就能够了。因为 Redis 它是基于内存的,无论是从吞吐量还是处理速度来说,都要比传统的 MySQL 数据库快很多倍。 随着工夫一直积攒,那么在 Redis 的服务器中应该会存储编号从 1~1000 的所有商品数据缓存。 Redis 面临的安全隐患:缓存穿透 大家请留神以后缓存中只有 1~1000 号数据。假如如果是同行歹意竞争,或者由第三方公司研发了爬虫机器人,在短时间内批量的进行数据查问,而这些查问的编号则是之前数据库中不存在的,比方当初看到的 8888、8889、8890 这些都是不存在的。 此时咱们零碎就会遇到一个重大的安全隐患:因为商城利用在向后盾 Redis 查问时,因为缓存中没有这条数据,它就进而到了数据库服务器中进行查问。 【有效申请超高并发,会导致解体】 要晓得数据库服务器对于刹时超高并发的拜访承载能力并不强。所以在短时间内,由爬虫机器人或者流量攻打机器人发来的这些有效的申请,都会霎时的灌入到数据库服务器中,对咱们的零碎的性能造成极大的影响,甚至会产生零碎解体。 而这种绕过 Redis 服务器,间接进入后盾数据库查问的攻击方式,咱们就称之为缓存穿透。 ...

August 12, 2021 · 2 min · jiezi

关于布隆过滤器:布隆过滤器你值得拥有的开发利器

在程序的世界中,布隆过滤器是程序员的一把利器,利用它能够疾速地解决我的项目中一些比拟辣手的问题。如网页 URL 去重、垃圾邮件辨认、大汇合中反复元素的判断和缓存穿透等问题。 布隆过滤器(Bloom Filter)是 1970 年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器能够用于检索一个元素是否在一个汇合中。它的长处是空间效率和查问工夫都比个别的算法要好的多,毛病是有肯定的误识别率和删除艰难。 浏览更多对于 Angular、TypeScript、Node.js/Java 、Spring 等技术文章,欢送拜访我的集体博客 ——全栈修仙之路一、布隆过滤器简介当你往简略数组或列表中插入新数据时,将不会依据插入项的值来确定该插入项的索引值。这意味着新插入项的索引值与数据值之间没有间接关系。这样的话,当你须要在数组或列表中搜寻相应值的时候,你必须遍历已有的汇合。若汇合中存在大量的数据,就会影响数据查找的效率。 针对这个问题,你能够思考应用哈希表。利用哈希表你能够通过对 “值” 进行哈希解决来取得该值对应的键或索引值,而后把该值寄存到列表中对应的索引地位。这意味着索引值是由插入项的值所确定的,当你须要判断列表中是否存在该值时,只须要对值进行哈希解决并在相应的索引地位进行搜寻即可,这时的搜寻速度是十分快的。 bf-array-vs-hashtable.jpg 依据定义,布隆过滤器能够查看值是 “可能在汇合中” 还是 “相对不在汇合中”。“可能” 示意有肯定的概率,也就是说可能存在肯定为误判率。那为什么会存在误判呢?上面咱们来剖析一下具体的起因。 布隆过滤器(Bloom Filter)实质上是由长度为 m 的位向量或位列表(仅蕴含 0 或 1 位值的列表)组成,最后所有的值均设置为 0,如下图所示。 bf-bit-vector.jpg 为了将数据项增加到布隆过滤器中,咱们会提供 K 个不同的哈希函数,并将后果地位上对应位的值置为 “1”。在后面所提到的哈希表中,咱们应用的是单个哈希函数,因而只能输入单个索引值。而对于布隆过滤器来说,咱们将应用多个哈希函数,这将会产生多个索引值。 bf-input-hash.jpg 如上图所示,当输出 “semlinker” 时,预设的 3 个哈希函数将输入 2、4、6,咱们把相应地位 1。假如另一个输出 ”kakuqo“,哈希函数输入 3、4 和 7。你可能曾经留神到,索引位 4 曾经被先前的 “semlinker” 标记了。此时,咱们曾经应用 “semlinker” 和 ”kakuqo“ 两个输出值,填充了位向量。以后位向量的标记状态为: bf-input-hash-1.jpg 当对值进行搜寻时,与哈希表相似,咱们将应用 3 个哈希函数对 ”搜寻的值“ 进行哈希运算,并查看其生成的索引值。假如,当咱们搜寻 ”fullstack“ 时,3 个哈希函数输入的 3 个索引值别离是 2、3 和 7: ...

June 12, 2021 · 3 min · jiezi

关于布隆过滤器:你永远可以相信布隆

明天的文章和大家一起来学习大数据畛域一个常常用到的算法——布隆过滤器。如果看过《数学之美》的同学对它应该并不生疏,它常常用在汇合的判断上,在海量数据的场景当中用来疾速地判断某个元素在不在一个宏大的汇合当中。它的原理不难,然而设计十分奇妙,。 欢送各位进群973961276一起聊聊技术吹吹牛,每周都会有几次抽奖送专业书籍的流动,奖品虽不甚值钱,但也算个彩头不是 原理在我之前的了解当中,如果想要判断某个元素在不在汇合当中,经典的构造应该是均衡树和hash table。然而无论是哪一种办法,都逃不开一点,都须要存储原值。 比方在爬虫场景当中,咱们须要记录下之前爬过的网站。咱们要将之前的网址全副都存储在容器里,而后在遇到新网站的时候去判断是否曾经爬过了。在这个问题当中,咱们并不关怀之前爬过的网站有哪些,咱们只关怀当初的网站有没有在之前呈现过。也就是说之前呈现过什么不重要,当初的有没有呈现过才重要。 咱们利用均衡树或者是Trie或者是AC自动机等数据结构和算法能够实现高效的查找,然而都离不开存储下所有的字符串。设想一下,一个网址大略上百个字符,大概0.1KB,如果是一亿个网址,就须要10GB了,如果是一百亿一千亿呢?显然这么大的规模就很麻烦了,明天要介绍的布隆过滤器就能够解决这个问题,而且不须要存储下原值,这是一个十分奇妙的做法,让咱们一起来看下它的原理。 布隆过滤器自身的构造非常简单,就是一个一维的bool型的数组,也就是说每一位只有0或者1,是一个bit,这个数组的长度是m。对于每个新增的项,咱们应用K种不同的hash算法对它计算hash值。所以咱们能够失去K个hash值,咱们用hash值对m取模,假如是x。刚开始的时候数组内全部都是0,咱们把所有x对应的地位标记为1。 举个例子,假如咱们一开始m是10,K是3。咱们遇到第一个插入的值是”线性代数“,咱们对它hash之后失去1,3,5,那么咱们将对应的地位标记成1. 而后咱们又遇到了一个值是”高等数学“,hash之后失去1,8,9,咱们还是将对应地位赋值成1,会发现1这个地位对应的值曾经是1了,咱们疏忽就好。 如果这个时候咱们想要判断”概率统计”有没有呈现过,怎么办?很简略,咱们对“概率统计”再计算hash值。假如失去1,4,5,咱们去遍历一下对应的地位,发现4这个地位是0,阐明之前没有增加过“概率统计”,显然“概率统计”没有呈现过。 然而如果“概率统计”hash之后的后果是1,3,8呢?咱们判断它呈现过就错了,答案很简略,因为尽管1,3,8这个hash组合之前没有呈现过,然而对应的地位都在其余元素中呈现过了,这样就呈现误差了。所以咱们能够晓得,布隆过滤器对于不存在的判断是精确的,然而对于存在的判断是有可能有谬误的。 零根底和大三大四的敌人看这里>>c/c++ 企业级我的项目实战曾经工作了想持续自我晋升跳槽涨薪的工程师看这里>>c/c++ linux服务器高级架构师学习 代码布隆过滤器的原理很简略,明确了之后,咱们很容易写出代码: # 插入元素def BloomFilter(filter, value, hash_functions): m = len(filter) for func in hash_functions: idx = func(value) % m filter[idx] = True return filter # 判断元素def MemberInFilter(filter, value, hash_functions): m = len(filter) for func in hash_functions: idx = func(value) % m if not filter[idx]: return False return True 错误率计算之前的例子当中应该展现得很明确了,布隆过滤器尽管好用,然而会存在bad case,也就是判断谬误的状况。那么,这种错误判断产生的概率有多大呢? 这个概率的计算也不难:因为数组长度是mm,所以插入一个bit它被置为1的概率是1m1m,插入一个元素须要插入k个hash值,所以插入一个元素,某一位没有被置为1的概率是(1−1m)k(1−1m)k。插入n个元素之后,某一位仍旧为0的概率是(1−1m)nk(1−1m)nk,它变成1的概率是1−(1−1m)nk1−(1−1m)nk。 ...

November 20, 2020 · 1 min · jiezi