大数据处理相关

题目描述

一个网站有100亿url存在一个黑名单中,每条url平均64字节。这个黑名单要怎么存?若此时随便输入一个url,你如何快速判断该 url 是否在这个黑名单中?

题目解析

这是一道经常在面试中出现的算法题。凭借着题目极其容易描述,电面的时候也出现过。

不考虑细节的话,此题就是一个简单的查找问题。对于查找问题而言,使用散列表来处理往往是一种效率比较高的方案。

但是,如果你在面试中回答使用散列表,接下来面试官肯定会问你:然后呢?如果你不能回答个所以然,面试官就会面无表情的通知你:今天的面试到此结束,我们会在一周内给你答复。

为什么不能用散列表

100亿是一个很大的数量级,这里每条url平均64字节,全部存储的话需要640G的内存空间。又因为使用了散列表这种数据结构,而散列表是会出现散列冲突的。为了让散列表维持较小的装载因子,避免出现过多的散列冲突,需要使用链表法来处理,这里就要存储链表指针。因此最后的内存空间可能超过1000G了。

只是存储个url就需要1000G的空间,老板肯定不能忍!

位图(BitMap)

位图的基本概念是用一个位(bit)来标记某个数据的存放状态。

一、海量数据排序

从最简单的情况说起,如果要对90个小于100的不重复的正整数排序。用位图的思想就是先申请一块100bit的空间,第一遍遍历所有的数,将出现的数字在位图中对应的位置置为1;第二遍遍历位图,依次输出值为1的位对应的数字。先且不说这种情况出现的频率不是很高,就仅这种情况,还是有很多其他的排序算法有它们自己的优势(不用额外占用空间之类)。但更进一步,如果我们把数字范围放大,对1000,000,000中的900,000,000个不重复的正整数排序,由于需要的内存非常大,其他算法在不分治的情况下就很难再处理这个问题。而用位图法只需要1000000000/(8*1024*104)=119.2MB空间,对现在的计算机来说没有任何问题。但是,空间复杂度随集合内最大元素增大而线性增大。对于开头的题目而言,使用位图进行处理,实际上内存消耗也是不少的。

二、海量数据去重

看一个比较常见的面试题:在2.5亿个整数中找出不重复的整数,内存不足以放下所有的数。我们可以采用两位的位图法,为每个数分配两位,00表示没有出现,01表示出现一次,10表示出现多次,11没有意义。接下去扫描着2.5亿个整数,查看位图中相对应的位,如果是00就变为01,如果是01就变为10,其他情况保持不变。扫描完成后仍为01的整数就是需要查找的数。

三、总结

Bitmap适用于数据规模大,但数据状态少的情况。同时Bitmap在存在以下一些不足:

  • 存储离散数据利用率低 
    Bitmap申请空间时要根据最大的数来决定申请的空间大小,如果数据是离散的,那空间的利用率就会非常低。
  • 不适合多状态 
    一个bit只能表示两种状态,如果要表示更多的状态,就需要更多的状态位来实现。如果一个数字需要多个状态位来表示的话,Bitmap的优越性也会大打折扣,而且复杂度却在增加。
  • 可读性差 
    将数据抽象为bit不利于理解,尤其是用多个bit位来表示一个数时。
  • 性能一般 
    需要维护额外的逻辑,计算速度会受到一定的影响。

 

布隆过滤器

布隆过滤器(英语:BloomFilter)是1970年由Burton Bloom提出的。

布隆过滤器需要的是一个位数组(和位图类似)+ K个独立的映射函数(和Hash表类似)!

直观的说,bloom算法类似一个hash set,用来判断某个元素(key)是否在某个集合中。和一般的hash set不同的是,这个算法无需存储key的值,对于每个key,只需要k个比特位,每个存储一个标志,用来判断key是否在集合中。

它可以用来判断一个元素是否在一个集合中,它的优势是只需要占用很小的内存空间以及有着高效的查询效率。

一、算法

  1. 首先需要k个hash函数,每个函数可以把key散列成为1个整数
  2. 初始化时,需要一个长度为n比特的数组,每个比特位初始化为0
  3. 某个key加入集合时,用k个hash函数计算出k个散列值,并把数组中对应的比特位置为1
  4. 判断某个key是否在集合时,用k个hash函数计算出k个散列值,并查询数组中对应的比特位,如果所有的比特位都是1,可能在集合中。如果不全为1,则一定不在集合中。

对于布隆过滤器而言,它的本质是一个位数组:位数组就是数组的每个元素都只占用1bit,并且每个元素只能是0或者1。

一开始,布隆过滤器的位数组所有位都初始化为0。比如,数组长度为m,那么将长度为m的位数组的所有的位都初始化为0。

布隆过滤器除了一个位数组,还有K个哈希函数。当一个元素加入布隆过滤器中的时候,会进行如下操作:

1、使用K个哈希函数对元素值进行K次计算,得到K个哈希值。

2、根据得到的哈希值,在位数组中把对应下标的值置为1。

举个例子,假设布隆过滤器有3个哈希函数:f1,f2,f3和一个位数组arr。现在要把2333插入布隆过滤器中:

1、对值进行三次哈希计算,得到三个值n1,n2,n3。

2、把位数组中三个元素arr[n1],arr[n2],arr[3]都置为1。

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

很明显,数组的容量即使再大,也是有限的。那么随着元素的增加,插入的元素就会越多,位数组中被置为1的位置因此也越多,这就会造成一种情况:当一个不在布隆过滤器中的元素,经过同样规则的哈希计算之后,得到的值在位数组中查询,有可能这些位置因为之前其它元素的操作先被置为 1 了。

但是,假设某个元素通过映射对应下标为4,5,6这3个点。虽然这3个点都为1,但是很明显这3个点是不同元素经过哈希得到的位置,因此这种情况说明这个元素虽然不在集合中,也可能对应的都是1,这是误判率存在的原因。所以,有可能一个不存在布隆过滤器中的会被误判成在布隆过滤器中。

这就是布隆过滤器的一个缺陷:存在误判。

但是,如果布隆过滤器判断某个元素不在布隆过滤器中,那么这个值就一定不在布隆过滤器中。总结就是:

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

用英文说就是:False is always false. True is may be true。

二、误判率

布隆过滤器可以插入元素,但不可以删除已有元素。其中的元素越多,falsepositiverate(误报率)越大,但是falsenegative(漏报)是不可能的。因此,Bloom Filter不适合那些“零错误”的应用场合。而在能容忍低错误率的应用场合下,Bloom Filter通过极少的错误换取了存储空间的极大节省。

三、优点

不需要存储key,节省空间

四、缺点

  1. 算法判断key在集合中时,有一定的概率key其实不在集合中
  2. 无法删除

五、补救方法

布隆过滤器存在一定的误识别率。常见的补救办法是在建立白名单,存储那些可能被误判的元素。

六、使用场景

布隆过滤器的最大的用处就是,能够迅速判断一个元素是否在一个集合中。因此它有如下三个使用场景:

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

b)进行垃圾邮件过滤:反垃圾邮件,从数十亿个垃圾邮件列表中判断某邮箱是否垃圾邮箱(同理,垃圾短信)

c)有的黑客为了让服务宕机,他们会构建大量不存在于缓存中的key向服务器发起请求,在数据量足够大的情况下,频繁的数据库查询可能导致DB挂掉。布隆过滤器很好的解决了缓存击穿的问题。

七、回到问题

回到一开始的问题,如果面试官问你如何在海量数据中快速判断该url是否在黑名单中时,你应该回答使用布隆过滤器进行处理,然后说明一下为什么不使用hash和bitmap,以及布隆过滤器的基本原理,最后你再谈谈它的使用场景那就更好了。

 

摘自

https://maimai.cn/article/detail?fid=1215474004&efid=VkcCbkpUmOeN8txLzz2aWA

猜你喜欢

转载自blog.csdn.net/u012836896/article/details/89891369