大数据去重:Bitmap和布隆过滤器

目录

  • 1. 用户关注链问题
  • 2. Bitmap
  • 3. Bloom filter(布隆过滤器)
  • 4. 海量数据中位数问题
  • 5. Top-K问题

1. 用户关注链问题

场景描述:在某场景下有一个用户关系链,比如A关注了B,然后B关注了C,然后用户B知道自己被谁关注,也知道他关注了谁。假如说将这个信息放在统一的数据库中,然后用户查询的时候每次去遍历,那么就会对数据库造成非常大的负担,而且在一个亿级用户系统中这样的时间延迟是不可接受的。这种情况下我们可以为每个用户维护两个集合:一个是他关注的人,一个是他被谁关注了。这里也会出现一个问题:假如A关注了B,那么一次关注就要写两个数据,一个是A的关注列表,一个是B的被关注列表,如果说中间由于网络或者其他某些原因导致其中一个写操作丢失,后面要怎么排查出那个丢失的写操作?

解决方案:使用归并排序来处理,内排外排都行。假定A关注了B,那么就会产生两个数据(A,B)、(A,B),一个放在A的关注列表中,一个放在B的被关注列表中,他们都是成对出现。我们每隔一段时间就批量将所有用户的关注列表和被关注列表读取出来,然后进行归并排序,只要在归并的过程中出现了成对的数据,就把成对的数据抛弃掉,这样一直归并下去最后剩下的就是不成对的数据。最后我们根据这个不成对的数据去把它们补全即可。

2. Bitmap

场景描述:在2.5亿个整数中找出不重复的整数,注,内存不足以容纳这2.5亿个整数。

解决方案1:采用2-Bitmap(每个数分配2bit,00表示不存在,01表示出现一次,10表示多次,11无意义)进行,共需内存2^32 * 2 bit=1 GB内存,还可以接受。然后扫描这2.5亿个整数,查看Bitmap中相对应位,如果是00变01,01变10,10保持不变。所描完事后,查看bitmap,把对应位是01的整数输出即可。

解决方案2:用哈希或者取模将2.5亿个整数分到小文件中,然后在小文件中找出不重复的整数,并排序。然后再进行归并,注意去除重复的元素。

3. Bloom filter(布隆过滤器)

原理:当一个元素被加入集合时,通过K个散列函数将这个元素映射成一个位数组中的K个点,把它们置为1。检索时,我们只要看看这些点是不是都是1就(大约)知道集合中有没有它了:如果这些点有任何一个0,则被检元素一定不在;如果都是1,则被检元素很可能在。这就是布隆过滤器的基本思想。它实际上是一个很长的二进制向量和一系列随机映射函数。优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。

大数据去重:Bitmap和布隆过滤器_第1张图片
场景描述:给你A,B两个文件,各存放50亿条URL,每条URL占用64字节,内存限制是4G,让你找出A,B文件共同的URL。如果是三个乃至n个文件呢?

解决方案:如果允许有一定的错误率,可以使用Bloom filter,4G内存大概可以表示340亿bit。将其中一个文件中的url使用Bloom filter映射为这340亿bit,然后挨个读取另外一个文件的url,检查是否与Bloom filter,如果是,那么该url应该是共同的url(注意会有一定的错误率)。

在这里的话另外引入一个场景:知乎首页的推送是不重复的,那么在获取到内容之后要把用户已读文章过滤掉,如果说用所有的文章一个个去用户已读列表中查找这样的效率太低了。那么可以为每一个用户维护一个布隆过滤器,然后用布隆过滤器去对文章进行过滤。当然可能并不完美,因为布隆过滤器也会占用内存,以及需要对其进行维护更新,否则短时间内用户刷新两次就很多重复文章了,这个可能不是知乎现在的方案。

其他应用场景:

  • Google 著名的分布式数据库 Bigtable 使用了布隆过滤器来查找不存在的行或列,以减少磁盘查找的IO次数

  • 在很多Key-Value系统中也使用了布隆过滤器来加快查询过程,如 Hbase,Accumulo,Leveldb,一般而言,Value 保存在磁盘中,访问磁盘需要花费大量时间,然而使用布隆过滤器可以快速判断某个Key对应的Value是否存在,因此可以避免很多不必要的磁盘IO操作

  • 比如高并发秒杀系统中抢红包系统中判断用户是否今天已领取红包

  • 在爬虫系统中,我们需要对URL进行去重,已经爬过的网页就可以不用爬了。但是URL太多了,几千万几个亿,如果用一个集合装下这些URL地址那是非常浪费空间的。这时候就可以考虑使用布隆过滤器。它可以大幅降低去重存储消耗,只不过也会使得爬虫系统错过少量的页面。

  • 邮箱系统的垃圾邮件过滤功能也普遍用到了布隆过滤器,因为用了这个过滤器,所以平时也会遇到某些正常的邮件被放进了垃圾邮件目录中,这个就是误判所致,概率很低。

4. 海量数据中位数问题

场景描述:在5亿个int中找它们的中位数

解决方案一:首先我们将int划分为2^16个区域,然后读取数据统计落到各个区域里的数的个数,之后我们根据统计结果就可以判断中位数落到那个区域,同时知道这个区域中的第几大数刚好是中位数。然后第二次扫描我们只统计落在这个区域中的那些数就可以了。
实际上,如果不是int是int64,我们可以经过3次这样的划分即可降低到可以接受的程度。即可以先将int64分成224个区域,然后确定区域的第几大数,在将该区域分成220个子区域,然后确定是子区域的第几大数,然后子区域里的数的个数只有2^20,就可以直接利用direct addr table(第n个数存放在第n个位置)进行统计了。

解决方案二:方法同基数排序有些像,开一个大小为65536的Int数组,第一遍读取,统计Int32的高16位的情况,也就是0-65535,都算作0,65536 - 131071都算作1。就相当于用该数除以65536。Int32 除以 65536的结果不会超过65536种情况,因此开一个长度为65536的数组计数就可以。每读取一个数,数组中对应的计数+1,考虑有负数的情况,需要将结果加32768后,记录在相应的数组内。
第一遍统计之后,遍历数组,逐个累加统计,看中位数处于哪个区间,比如处于区间k,那么0- k-1的区间里数字的数量sum应该

5. Top-K问题

场景描述:100w个数中找出最大的100个数。

解决方案一:局部淘汰方法。选取前100个元素,并排序,记为序列L。然后一次扫描剩余的元素x,与排好序的100个元素中最小的元素比,如果比这个最小的要大,那么把这个最小的元素删除,并把x利用插入排序的思想,插入到序列L中。依次循环,知道扫描了所有的元素。复杂度为O(100w*100)。

解决方案二:采用快速排序的思想,每次分割之后只考虑比轴大的一部分,知道比轴大的一部分在比100多的时候,采用传统排序算法排序,取前100个。复杂度为O(100w*100)。

解决方案三:用一个含100个元素的最小堆来完成。复杂度为O(100w*lg100)

你可能感兴趣的:(大数据去重:Bitmap和布隆过滤器)