面试题-海量数据去重,如何实现?

这是一道非常经典的面试题 类似的问法比如
40亿QQ号如何设计算法去重,相同的QQ号码仅保留一个,内存限制为1个G? 或者40亿个QQ号,限制1G内存,如何去重?
这种问题的本质:其实就是一个海量数据去重的问题, 但是有一个受限条件, 内存限制为1个G。
解决方案有很多,但是主流的方案有两种
方案1: 使用BitMap进行海量数据去重
方案2: 使用布隆过滤器进行海量数据去重

方式1: 使用BitMap进行海量数据去重

首先我们需要知道,什么是BitMap?BitMap的使用场景
位图(BitMap)其实就是一个bit数组即每一个位置都是一个bit,其中的取值可以是0或者1
位图(BitMap)思想:就是用一个bit来标记元素,bit是计算机中最小的单位,也就是我们常说的计算机中的0和1,这种就是用一个位来表示的。
对于0,2,4,6 这四个数,如果存在的话,则可以这样表示:
面试题-海量数据去重,如何实现?_第1张图片
如果不用位图的话,我们想要记录0, 2,4,6 这四个整型的话,怎么办?
就需要用四个unsigned int,已知每个unsigned int占4个字节,那么就是 44 = 16 个字节,一个字节
有8 bit,那么就是 16
8 = 128 个bit。
结论是:位图最大的好处就是节省空间。这里节省了 16倍

那么如何使用BitMap进行40亿个QQ号去重?

回到问题:40亿个QQ号,限制1G内存,如何去重?
前面分析过:一个qq号码,就是一个unsigned int。
40亿个QQ号,就是40亿个 unsigned int,一个 unsigned int占用4个字节。假如,40亿个 unsigned int直接用内存存储的话,需要多少内存呢?
简单计算一下:
4000000000*4 /1024/1024/1024 = 14.9G
所以,如果直接把 40亿个QQ号放入内存,需要15个G,1G的空间也是不够用的。
那么,怎么办呢?
qq号是数字,刚好可以使用bitmap。
比如要把一个QQ号"12345678"放到Bitmap中,就需要找到第12345678这个位置,然后把他设置成1就可以了。
面试题-海量数据去重,如何实现?_第2张图片
这样,把40亿个数字都放到Bitmap之后,位置上是1的表示存在,不为1的表示不存在。
相同的QQ号只需要设置一次1就可以了,那么,最终就把所有是1的数字遍历出来就行了。
使用位图的话,一个数字只需要占用1个bit,那么40亿个数字也就是:
4000000000 * 1 /8/1024/1024 = 476M
相比于之前的14.9G来说,大大的节省了很多空间。
大约节省了 30倍的空间。

BitMap位图的优势和不足是什么呢?

其实位图也有着一定的限制,很明显那就是他只能表示0和1,无法存储其他的数字。
所以BitMap只适合这种能表示true or false的场景
其次,BitMap 只适用于值域比较小的集合,因为如果值域过大,BitMap 的存储空间也会过大,这时候 使用布隆过滤器可能更为合适。
最后,BitMap 不支持删除操作,因为删除一个元素需要将对应的比特位设置为 0,这可能会影响到其他元素的状态。

方式2: 使用布隆过滤器进行海量数据去重

刚才说了 如果值域过大,BitMap 的存储空间也会过大,这个时候,需要使用布隆过滤器,进一步进行空间的压缩。

什么是布隆过滤器,实现原理是什么?

布隆过滤器是一种数据结构,用于快速检索一个元素是否可能存在于一个集合(bit 数组)中
它的基本原理是利用多个哈希函数,将一个元素映射成多个位,然后将这些位设置为 1。本质上:布隆过滤器内部包含一个bit数组和多个哈希函数,每个哈希函数都会生成一个index 索引值
它由两个部分组成:
一个bit数组, 存储数据
多个哈希函数, 计算key的 index 索引
如下图所示 这里有三个key ==》x,y,z
面试题-海量数据去重,如何实现?_第3张图片
那么问题来了:我们如何做 exist(key)这种存在性的判定呢?
答案:当查询一个元素时,如果这些位都被设置为 1,则认为元素可能存在于集合中,否则肯定不存在
比如说exist(“x”) 的结果为 true
但是: 布隆过滤器只能准确的判断一个元素是否一定不存在。注意,是判断一定不存在。为啥呢? 因为哈希冲突的存在。

布隆过滤器的工作过程又是怎么样的?

下面是布隆过滤器的工作过程:
1、初始化布隆过滤器:在初始化布隆过滤器时,需要指定集合的大小和误判率。
2、添加元素到布隆过滤器:要将一个元素添加到布隆过滤器中,首先需要将该元素通过多个哈希函数生成多个索引值,然后将这些
索引值对应的位设置为 1。如果这些索引值已经被设置为 1,则不需要再次设置。
3、查询元素是否存在于布隆过滤器中:要查询一个元素是否存在于布隆过滤器中,需要将该元素通过多个哈希函数生成多个索引值,并判断这些索引值对应的位是否都被设置为 1。如果这些位都被设置为 1,则认为元素可能存在于集合中,否则肯定不存在。

布隆过滤器的主要优点是可以快速判断一个元素是否属于某个集合,并且可以在空间和时间上实现较高的效率。
但是,它也存在一些缺点,例如:
(1).布隆过滤器在判断元素是否存在时,有一定的误判率
(2).布隆过滤器删除元素比较困难,因为删除一个元素需要将其对应的多个位设置为 0,但这些位可能被其他元素共享

如何实现实现布隆过滤器?

我们可以通过Guava实现,或者通过Redisson,以及原生jedis都可以实现布隆过滤器

布隆过滤器应用场景

布隆过滤器因为他的效率非常高,所以被广泛的使用,比较典型的场景有以下几个:
1、网页爬虫:爬虫程序可以使用布隆过滤器来过滤掉已经爬取过的网页,避免重复爬取和浪费资源。
2、缓存系统:缓存系统可以使用布隆过滤器来判断一个查询是否可能存在于缓存中,从而减少查询缓
存的次数,提高查询效率。布隆过滤器也经常用来解决缓存穿透的问题。
3、分布式系统:在分布式系统中,可以使用布隆过滤器来判断一个元素是否存在于分布式缓存中,避免在所有节点上进行查询,减少网络负载。
4、垃圾邮件过滤:布隆过滤器可以用于判断一个邮件地址是否在垃圾邮件列表中,从而过滤掉垃圾邮
件。
5、黑名单过滤:布隆过滤器可以用于判断一个IP地址或手机号码是否在黑名单中,从而阻止恶意请求。

3 海量数据去重场景:布隆过滤器和位图如何选择?

布隆过滤器的空间效率更高,但存在一定的误判概率 因此不适合那些“零错误”的应用场合;
而位图的空间效率较低,但不存在误判。因此,在实际应用中,需要根据具体的场景选择合适的数据结构

你可能感兴趣的:(面试,分布式,#,分布式缓存,java,redis)