布隆过滤器——学习笔记

  • 什么是布隆过滤器?

布隆过滤器(BloomFilter)是布隆在1970年提出的,它实际上是一个很长的二进制向量和一系列的随机映射函数。布隆过滤器用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都比一般的算法要好得多,缺陷是必然有一定概率的误判和删除困难

  • 名词解释

二进制向量:

  二进制向量,在计算机领域又叫做位向量、比特向量,因为计算机中的 “位” 都是二进制位,也就是比特(bit)。
  它本质还是向量,所以就可以表示为有向线段或坐标.而顾名思义,描述这种向量的各维坐标的数值应该也是二进制数.可如果只是把普通向量中的十进制数,改写为二进制数,那没什么意义——不同进制改变的只是数的表示方法(可能还有计算方法),数本身的意义却没变。
  真正二进制向量与普通向量的区别在于:各个维度的取值范围,从普通向量的连续区间(甚至是整个数轴),改变为 “两个离散的数值”.这就意味着,同一向量空间中的所有二进制向量,所用到的数值不会超过 2 个.所以,用二进制中的 0 和 1 表示这两个不同而又相关的数值再合适不过了。

  • 问题引入:假设你需要判断一个元素是否在指定集合中,你会用什么结构? 

 如果没有任何限制,数组、链表、数、哈希表等结构都能实现这样的查询功能,比如你选择了HashMap,HashMap可以在时间复杂度为O(1)内返回查询结果,效率很高。但是HashMap也是有缺陷的——当数据量很大的时候,HashMap的存储容量占比高,再考虑到负载因子,那么使用HashMap来存储这些大量的数据,是很占内存的。

为了解决内存问题,布隆过滤器就派上用场了!

  • 布隆过滤器简介

当一个元素被加入集合时,通过K个散列函数(哈希函数)将这个元素映射成一个位数组中的K个点,并且将这些点置为1。当我们需要检索某个元素是否在这个集合里面的时候,只需要将这个元素也通过相同的K个哈希函数得到K个点,再看这k个点是否全部被置为1了,如果是,则集合中有可能存在这个元素,否则,只要有一个位不为1,集合中就绝对不存在这个元素,这就是布隆过滤器的基本思想。

本质上,布隆过滤器是一种数据结构,这个结构很巧妙的实现了高效的插入和查询,如果业务上只涉及插入数据和查询数据,并且能够允许一定程度的误判(实际上误判概率很低的,下面会介绍计算公式),那么使用布隆过滤器能极大的节省空间。

  • 布隆过滤器原理剖析

根据上面的定义,布隆过滤器可以检查一个元素“可能在集合中”或者“绝对不在集合中”,为什么是可能存在呢?下面来看一下过滤器的实现原理,就能知道原因了!

1.布隆过滤器本质上是长为m的位向量或位列表(仅包含0或1位值的列表)组成,最初所有的初始值都为0,如下图所示。

 2.为了将元素添加到布隆过滤器中,我们会提供K个不同的哈希函数,元素经过K个哈希函数,能够计算出K个映射值,这K个映射值就对应过滤器中的位,将这个K个位 置为1,元素就被添加进了布隆过滤器中。

3.可以这样假设,加入的数据量足够大,而列表的长度m比较小,那么很有可能列表每个位上都是1。那么当你查询某一个元素是否加入布隆过滤器时,即使没有加过,那么也会发生误判。但是这个误判率是可以预测的,下面是预测公式:

 参数:

  • n 是已经添加元素的数量
  • k 哈希函数的个数
  • m 布隆过滤器的长度(如比特数组的大小)

  极端情况下,当布隆过滤器没有空闲空间时(就是列表上所有位都为1,),每次查询都会返回true,这就意味着m的取值取决于预计要加入的元素数量n,并且要保证m远远大于n。

  实际情况下,布隆过滤器的长度m可以根据给定的误判率(FFP)和期望添加的元素数量n来计算,公式如下:

 那么哈希函数的个数该如何选择呢?

从上面的公式可以看出:哈希函数个数过少和过多都会引起误报率的攀升,所以在实际情况中,如果要求了误报率和输入的数据量,可以计算出哈希函数个数K。

  • 布隆过滤器应用场景 

1.网页爬虫对URL去重,避免爬取相同的地址

2.反垃圾邮件,从数十亿个垃圾邮件列表中判断某个邮箱是否是垃圾邮箱

3.Google Chrome使用布隆过滤器识别恶意URL

4.Medium使用布隆过滤器避免推荐给用户已经读过的文章

5.Google BigTable,Apache HBbase和Apache Cassandra使用布隆过滤器减少对不存在的行和列的查找。

6.除了上述应用场景之外,布隆过滤器还有一个应用场景就是解决缓存穿透的问题。所谓的缓存穿透就是服务调用方每次都查询不在缓存中的数据,这样每次服务调用都会到数据库中查询,如果这类请求比较多的话,就会导致数据库压力增大,这样缓存就失去了意义。我们可以利用布隆过滤器预先把数据查询的主键,比如用户的ID或文章的ID缓存到过滤器中。当根据ID进行数据查询的时候,我们应该先判断该ID是否存在,若存在的话,则进行下一步处理。若不存在的话,直接返回,这样就不会触发后续的数据库查询。需要注意的是缓存穿透不能完全解决,我们只能将其控制在一个可以容忍的范围内。

猜你喜欢

转载自blog.csdn.net/qq_56919740/article/details/128771618
今日推荐