海量数据问题总结

南楼画角 提交于 2019-12-08 17:57:29

本博客已弃用,当时存在一些小细节错误后期也不再修改了

欢迎来我的新博客

前言:在这个用户量爆炸、并发极高的互联网时代,对于如今的IT行业,在极大量的数据处理这一块的能力,无疑某些程度上是其技术实力的体现。例如淘宝、支付宝在双十一这天的峰值时期,每秒要处理的数据量几乎都是百亿级别,新浪微博在春晚将要处理一亿左右人的抢红包活动。海量数据处理能力是一家大公司必须要做得非常硬的一个技术块,这样才能在互联网行业的激烈竞争中站稳脚跟。因此,海量数据问题在面试中也是经常问到的,并且也是很能体现你的知识底蕴的一个方面。

下面我们通过七个问题来进行讲解。

Ps:本文需要堆、哈希、位图、布隆这些知识基础,若你不明白这些目前还是先不要看下去了,当然,没有耐心去一个一个方法看的肯定也看不下去。

正文:

海量数据的定义:内存里存不下或者会占用太多内存的数据量。(注意:我们说的是内存,因为这些数据都是要拿来直接用的)

注意:在这里讲的是方法,因为别人问你也不是要你自己能实现一个处理海量的数据的轮子,重要的是能表述清楚原理,因此无关紧要的细节各位自行忽略(比如下面的log file怎么取模,log file中的IP怎么提取之类的问题)

1)给⼀一个超过100G大小的log file, log中存着IP地址, 设计算法找到出现次数最多的IP地址。如何找到top K的IP?

        这样的问题如果出现在数据量较小的情形下,用一个map或者unordered_map都能很快的处理,但是100G这样的量级放在一个map里是绝对不可行的。因此,我们采用哈希切分。

        什么是哈希切分?

        100G文件我们无法处理,是1G文件我们能处理,100M文件我们能处理

        因此,哈希切分的第一步:映射切分(分治)

        首先把所有log file中的IP挨个提取出来,然后这个总量为100G的IP文件,我们通过对所有进行IP%1000这样一个操作,把100G的大文件切分成了1000个100M的小文件

        第二步:对每个切分小文件进行统计

        这个统计过程一般直接用unordered_map<IP,size_t>就能完成,然后在每个切分小文件里,我们很快就能找到出现次数最多的那个IP

        第三步:排序

        由第二步得到了1000个出现频率最高的IP后,由于我们在这里只用找出出现次数最多的IP,那么O(n)次内遍历一趟就能找出来,当然,如果找前k个,那就用一个容量为k的小堆就能完成。

        过程如图(后面大部分题都是这么个模式,就不再画图了)

        

        好了,思考的同学肯定会有疑问,这样做难道不会出现相同的IP没有被映射到一个文件的问题嘛?当然不会啦。因为第一步的哈希映射是等价映射,相同的IP%1000后是一定会被映射到同一小文件里的,因此不存在这种问题。同时,我们这里%1000改成%100也是同样可以的,只不过%1000比%100更能避免同一小文件里映射了太多IP的问题。

        总得来说就是:映射->统计->排序

        OK,有了对哈希切分的基础,后面的好几个问题用哈希切分就迎刃而解了,但是这些问题我们就不说哈希切分这个方法了,对于这类问题,你会发现哈希切分几乎是个万金油的方法╭(′▽`)╭(′▽`)╯

2)给定100亿个整数,设计算法找到只出现一次的整数

        这个问题如果出现在数据量比较小的情况下,把位图(BitSet)进行扩展无疑是最节省空间与时间的方法。

        把位图每个标记位由普通位图的一个变为两个,因为普通位图只能标记在或不在,而我们的要求是判断是否只出现一次,因此我们用两个位来标记就好啦,因为两个位可以表示:0次、1次、2次、3次,但这是100亿个整数诶!

        我们先做个计算:假定我们的位图用两个位为来标记一个数字,一个字节就能标记4个正数,而我们有100亿个,那么就需要100/4 = 25亿字节,10亿字节约等于1g,也就是说我们的位图也至少要开2.5个g左右的内存空间。2.5g内存对如今的计算机似乎也不大,但如果我就是要你不用超过1g内存呢 ◔ ‸◔?

        机灵的你一定想到了哈希切分!没错,我们先哈希切分,然后每次用位图统计只出现一次的正数,即使我们切10份,每次用位图也就需要250M空间,这样就能把内存占用控制在250M左右,这样问题就解决了。

 

 3)给两个文件,分别有100亿个整数,我们只有1G内存,如何找到两个文件交集 

        有了前面两题的基础,这个问题是不是就迎刃而解了,这道题在上题的基础上,我们只要把位图改为用普通位图,然后该切分还是切分,我们再来计算一下:现在一个字节可以标记八个整数,那么我们对于每个文件需要  100/8 = 12.5亿字节,约等于1.25g左右,因为有两个文件,我们需要两个位图,那么我们要就要2.5g左右空间。

        同样,我们对两个文件分别分10份,每次使用两个位图,这两个位图按位与得到的结果就记录好了交集,每次我们又把交集里出现的正数统计好,记录下来,这样内存也可以很好的控制在1g以下。

4)1个文件有100亿个int,1G内存,设计算法找到出现次数不超过2次的所有整数

        这个问题非常简单了,就是把第二题中要找的只出现一次的整数,改成找出现一次、出现两次的整数,其他的就不符合哦我们要求啦,方法仍然就是把普通位图进行扩展。

5)给两个文件,分别有100亿个query,我们只有1G内存,如何找到两个文件交集?分别给出精确算法和近似算法

        为了方便讲解,避免不必要的讨论,我们假定这个query就是字符串类型。

        首先,对于字符串类型,直接用位图肯定是没法用了,那我们就用布隆过滤器(BloomFilter)

        布隆的特点:存在不一定是准确的(虽然一般几率较小),不存在是一定准确的

        因此,对于近似算法,我们用普通的布隆过滤器能做到的就是近似准确的,毕竟HasnFunc对于字符串的映射到整型是无限集到有限集的映射,是有可能出现不同字符串映射到同一个位的,即使几率很小。

        同样,都不用我说你也知道,先哈希切分,再布隆,每次把两个布隆按位与的结果记录下来即可。

        对于精确算法,我们是这样实现的,把布隆进行扩展,原来用一个HashFunc映射一次来映射一个位,那么我们用N个字符串HashFunc来映射N个位,(N的取值是有具体理论的,这里不进行赘述),占用的空间回事原来的N倍,但是这样出现误判的几率就几乎是不可能有了。做了这个处理后,后面的步骤是相同的。

6)如何扩展BloomFilter使得它支持删除元素的操作?如何扩展BloomFilter使得它支持计数操作?

        在这道题里说的布隆当然是说的精确的布隆过滤器,它是不支持删除的,因为删除的时候会产生对其他数据的连锁效应,比两个字符串经过三个映射后,其可能有一个映射位是相同的,这个时候删除这两个字符串中的哪一个字符串都不太好处理。这两个问题其实问的是同一件事,只要支持了计数,那么就必然是置产删除的。   

        那么怎么使它支持计数呢?我们原来是每个HashFunc会映射到一个位,现在每个HashFunc映射到n个位来计数(类似位图的扩展),比如n为2,那么就能表示这个映射到的位被标记了0、1、2、3次,每次Set操作,该位+1,每次ReSet操作,该位-1,这样就能做到计数与删除了,当然,对空间的消耗也是成倍的,并且,这个计数位到底应该有几个对于要处理的问题是合适的也是一个要考虑的问题,比如n为2最多才能被标记三次。

7)与(1)题条件相同,如何直接用Linux系统命令实现找到top K的IP?

        第一步要做的仍然是哈希切分,然后每一组文件里要用到awk sort uniq  具体要怎么用还得看Log file 中每一行的格式。

        

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!