zoukankan      html  css  js  c++  java
  • 布隆过滤器(Bloom Filter)

    布隆过滤器(Bloom Filter)

    1、布隆过滤器要解决的问题?

         想判断一个元素是不是在一个集合里?

         思路:

         1) 一般我们最先想到的是将集合中所有元素保存起来,然后通过比较确定。链表(增删改容易,查询困难)、树、散列表(又叫哈希表,Hash table)等等数据结构都是这种思路。

         2) 但是随着集合中元素的增加(比如说是20亿个url),我们需要的存储空间越来越大。同时检索速度也越来越慢,上述三种结构的检索时间复杂度分别为O(n),O(log n),O(1)。

         3) 可能很多人首先想到的会是使用 HashSet,因为 HashSet基于HashMap,理论上时间复杂度为:O(1)。达到了快速的目的,但是空间复杂度呢?字符串通过Hash得到一个Integer的值,Integer占4个字节,那20亿个url字符串理论上需要:20亿*4/1024/1024/1024=7.45G的内存,造成庞大的空间开销,显然这种解决方案是不合理的。

         针对这种问题我们该如何解决呢?这就引出这篇文章的主题:布隆过滤器。在解释布隆过滤器之前我们有必要了解什么是哈希函数,它是实现哈希表和布隆过滤器的基础

    2、哈希函数

         为什么要引出hash函数呢?因为要解决空间开销大的问题。

         1)哈希函数的概念

         将任意大小的数据转换成特定大小的数据的函数,转换后的数据称为哈希值或哈希编码。下面是一幅示意图:

     

       可以明显的看到,原始数据经过哈希函数的映射后成为了一个个的哈希编码,数据得到压缩

        2)重复的问题(hash碰撞)

        不知道你们有没有发现:这里面可能会存在哈希值重复的问题,比如John Smith和Sandra Dee对应的哈希值都是02,但是明明这是两个不同的key,这样会造成误判的啊!

        那要如何解决或者说降低这个误识别率呢?那就再增加一个hash fucntion,但是还是有可能会得到重复的哈希值。那就再增加一个hash fucntion ...如果还是有哈希值重复...增加更多的hash function...

    3、什么是布隆过滤器?

    1)维基百科定义

         布隆过滤器(Bloom Filter)是1970年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。

         它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率删除困难

    2)通俗易懂的描述  

         布隆过滤器(Bloom Filter)的核心实现是一个超大的位数组和几个哈希函数。这个超大的位数组(bit Array)在没有元素被加入集合的时候,全都初始化为0

         布隆过滤器的原理是:当一个元素被加入集合时,通过K个散列函数将这个元素映射成一个位数组中的K个点,把它们置为1。检索时,我们只要看看这些点是不是都是1就(大约)知道集合中有没有它了:

         如果这些点有任何一个0,则被检元素一定不在;如果都是1,则被检元素很可能在。这就是布隆过滤器的基本思想。

     

     

    4、举个栗子

        例如:现在给定一个url字符串,需要从20亿个url字符串的集合中,判断这个url字符串是否存在?

         为了能将整个算法过程描述的更加通俗易懂,我们加上一个前提条件:此处的hash算法使用的hash值是integer类型的,由于Integer的最大值为2147483647,意思也就是任何一个URL的哈希值都会在0-2147483647之间。

         1) 首先,我们可以定义2147483647长度的bit数组命名为source,用来存储hash之后产生的所有可能值。存储这个bit数组系统只需要:2147483647/8/1024/1024=256M。(1byte =8 bit)

         2) 接着,我们分别通过n个hash函数对这20亿个URL字符串进行处理得到n个不同的hash值,然后用这个hash值作为位数组的下标,将位数组中相应的值置为1.

         整个过程的处理逻辑如下图(这个图比较形象,是借用别人的,原文已经在参考链接中贴出)所示:

     

        

        检查结果分为2种:一定不在集合中、可能在集合中

        分析:url字符串只要经过其中某一个哈希函数后得到的哈希值,作为下标在位数组中对应的值如果是0,则说明该url字符串一定不在集合中。而经过所有hash函数得到的哈希值,作为下标在位数组中对应的值如果都是1,则说明该url字符串可能在集合中。这里面就涉及到一个假正例概率(False Positive)的推导。

    5、False Positive 概率推导

    假设 Hash 函数以等概率条件选择并设置 Bit Array 中的某一位,m 是该位数组的大小,k 是 Hash 函数的个数,那么位数组中某一特定的位在进行元素插入时的 Hash 操作中没有被置位的概率是:

     

    那么在所有 k 次 Hash 操作后该位都没有被置 "1" 的概率是:

     

    如果我们插入了 n 个元素,那么某一位仍然为 "0" 的概率是:

     

    因而该位为 "1"的概率是:

     

    现在检测某一元素是否在该集合中。标明某个元素是否在集合中所需的 k 个位置都按照如上的方法设置为 "1",但是该方法可能会使算法错误的认为某一原本不在集合中的元素却被检测为在该集合中(False Positives),该概率由以下公式确定:

      

    其实上述结果是在假定由每个 Hash 计算出需要设置的位(bit) 的位置是相互独立为前提计算出来的,不难看出,随着 m (位数组大小)的增加,假正例(False Positives)的概率会下降,同时随着插入元素个数 n 的增加,False Positives的概率又会上升,对于给定的m,n,如何选择Hash函数个数 k 由以下公式确定:

     

    此时False Positives的概率为:

     

    而对于给定的False Positives概率 p,如何选择最优的位数组大小m呢?

     

    上式表明,位数组的大小最好与插入元素的个数成线性关系,对于给定的 m,n,k,假正例概率最大为:

     

    下图是布隆过滤器假正例概率 p 与位数组大小 m 和集合中插入元素个数 n 的关系图,假定 Hash 函数个数选取最优数目:

      

     

        上图中,横轴代表插入的元素个数n,纵轴代表假正例的概率,从左到右的区县表示的是当m的值等于2的8次方,2的12次方,2的16次方。。。的时候n和p的关系

        我们拿最左边那条红色曲线为例,当位数组的大小是256,插入的元素个数为8个的时候,假正例的概率(p)是10的-10次方,这个时候几乎就是p最小的时候,也就是最理想的状态,当n的个数增加,p也随之增加,当n增加到接近100的时候,p接近为1,这就是非常糟糕的时候。后面的曲线也可以按照这个逻辑来分析。

        最后,我们看下最右边那条紫色曲线,当m为2的36次方,显然这是个超大位数组,这个时候,当n为10的9次方左右的时候,p是最理想的,随着n的增加,我们看到这条曲线,p达到的最高值也不到10的-6的次方,可见这个位数组足够大,随着插入元素的增加,它的假正例概率都是比较小的。

    6、布隆过滤器的优缺点

    优点:

    1)相比于其它的数据结构,布隆过滤器在空间和时间方面都有巨大的优势。布隆过滤器存储空间和插入/查询时间都是常数O(k)

    2)散列函数相互之间没有关系,方便由硬件并行实现。布隆过滤器不需要存储元素本身,在某些对保密要求非常严格的场合有优势。

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

    缺点:

    1)不方便扩展,如果要变更位数组,就要重新调整哈希函数,相当于原来的位数组弃用,重新在计算机内存中开辟一块空间,来存放新的位数组

    2)有一定的误算率,随着存入的元素数量增加,误算率随之增加。但是如果元素数量太少,则使用散列表就足够了。

    3)删除困难:

        一般情况下不能从布隆过滤器中删除元素。我们很容易想到把位列阵变成整数数组,每插入一个元素相应的计数器加1, 这样删除元素时将计数器减掉就可以了。然而要保证安全的删除元素并非如此简单。

        首先我们必须保证删除的元素的确在布隆过滤器里面。这一点单凭这个过滤器是无法保证的。另外计数器回绕也会造成问题。

    7、布隆过滤器的常见应用场景

         1)字处理软件中,需要检查一个英语单词是否拼写正确

         2)在 FBI,一个嫌疑人的名字是否已经在嫌疑名单上

         3)在网络爬虫里,一个网址是否被访问过

         4)yahoo, gmail等邮箱垃圾邮件过滤功能

         5)Key-Value缓存系统的Key校验

    8、 布隆过滤器如何运用

    1)解决缓存穿透的问题

         一般情况下,先查询缓存是否有该条数据,缓存中没有时,再查询数据库。当数据库也不存在该条数据时,每次查询都要访问数据库,这就是缓存穿透 缓存穿透带来的问题是,当有大量请求查询数据库不存在的数据时,就会给数据库带来压力,甚至会拖垮数据库。关于redis的缓存问题在我的这篇《Redis的雪崩、击穿、穿透》 中有详细介绍。或者可以参考别人的这篇解读缓存穿透和缓存雪崩》文章。

         可以使用布隆过滤器解决缓存穿透的问题,把已存在数据的key存在布隆过滤器中。当有新的请求时,先到布隆过滤器中查询是否存在,如果不存在该条数据直接返回;如果存在该条数据再查询缓存查询数据库。

    2)黑名单校验

          发现存在黑名单中的,就执行特定操作。比如:识别垃圾邮件,只要是邮箱在黑名单中的邮件,就识别为垃圾邮件。假设黑名单的数量是数以亿计的,存放起来就是非常耗费存储空间的,布隆过滤器则是一个较好的解决方案。把所有黑名单都放在布隆过滤器中,再收到邮件时,判断邮件地址是否在布隆过滤器中即可。

    9、 安装 

          Redis4.0开始自带了布隆过滤器这个功能。较低版本可以使用第三方扩展,实现布隆过滤器的功能,第三方扩展在空间占用上做的不是很好

    10、后续

         后面有时间再来研究下Bloom Filter的具体使用,包括通过docker安装、使用之类的。到时候再来记录~~~~

     

     

     

     

    参考链接:

    https://blog.csdn.net/Startapi/article/details/101161081

    https://www.cnblogs.com/liyulong1982/p/6013002.html

    https://www.cnblogs.com/cpselvis/p/6265825.html

    https://baijiahao.baidu.com/s?id=1655304940308056733&wfr=spider&for=pc

  • 相关阅读:
    Javascript 面向对象编程
    搜狗2013面试题——求页面中所有单词的个数
    jQuery的ready函数需要注意的细节
    js冒泡排序
    jQuery基本框架解析
    数据结构与算法汇总
    唯美的图片网站
    JavaScript中的setInterval用法
    DOCTYPE
    js实现快速排列
  • 原文地址:https://www.cnblogs.com/hld123/p/14113575.html
Copyright © 2011-2022 走看看