海量数据处理面试题

总结了一些面试中常见的海量数据(数据量远大于内存容量)处理的问题。该blog参考“研究者July”的csdn博客,感谢作者分享。

1.给定a、b两个文件,各存放50亿个url,每个url各占64字节,内存限制是4G,让你找出a、b文件共同的url?

考虑到每个文件大小为:50*10^8*64B = 5G*64B = 320GB,内存4G放不下,需要将数据至少拆分为8份才可以,考虑需要同时存放a、b文件部分内容,需要至少16份。
将a、b文件中url进行hash(url)%1000分散到1000个小文件a0、a1...a1000,b0、b1...b1000中,这样具有相同url的文件ax、bx是具有相同的序号x,然后加载ax、bx文件中的url到内存中进行比较即可。

PS:

  • 1k >= 10^3, 1M >= 10^6, 1G >= 10^9,可以用1G大概表示10^9
  • 文件分散1000份之后,每份大约300M,如果文件很大超过1.5G则继续对该文件进行分散,比如a0大小为2G,将a0、b0中的url进行hash(url)%10的操作将文件分散为a00、a01...a09、b00、b01...b09,再进行比较。

 2.有10个文件,每个文件1G,每个文件的每一行存放的都是用户的query,每个文件的query都可能重复。要求你按照query的频度排序。

方案1:顺序读取10个文件,按照hash(query)%10的结果将query写入到另外10个文件中(b0、b1...b9)。这样新生成的文件每个的大小大约也1G(假设hash函数是随机的)。找一台内存在2G左右的机器,依次对用tri树或hash_map(query, query_count)来统计每个query出现的次数。利用快速/堆/归并排序按照出现次数进行排序。将排序好的query和对应的query_cout输出到文件中。这样得到了10个排好序的文件(b0、b1...b9)。对这10个文件进行归并排序(内排序与外排序相结合)。

PS:

  • 使用trie树或hashmap(query, query_count)统计的时候,对10个文件进行的操作是重复的,可以采用分布式架构并行处理(比如采用MapReduce)

方案2:一般query的总量是有限的,只是重复的次数比较多而已,可能对于所有的query,一次性就可以加入到内存了。这样,我们就可以采用trie树/hash_map等直接来统计每个query出现的次数,然后按出现次数做快速/堆/归并排序就可以了。

3.在2.5亿个整数中找出不重复的整数,内存不足以容纳这2.5亿个整数。

内存1M,文件大小为1G,至少需要分成1000份进行处理,这里使用hash(word)%5000的方法将文件分割为5000个小文件,采用2-Bitmap(00表示不存在,01表示出现一次,10表示多次)进行,共需内存2^32\*2bit=1GB内存,还可以接受。然后扫描这2.5亿个整数,查看Bitmap中相对应位,如果是00变01,01变10,10保持不变。所描完事后,查看bitmap,把对应位是01的整数输出即可。
也可采用上题类似的方法,进行划分小文件的方法。然后在小文件中找出不重复的整数,并排序。然后再进行归并,注意去除重复的元素。

PS:

  • Bitmap是hash或者map的一种,跟普通的HashMap相比局限在于只能标记两种状态,优点在于可以节省大量空间。2-BitMap可以标记4种状态,有固定几种状态的映射需求可以考虑使用BitMap/2-BitMap/3-BitMap/...。

4.一个文本文件,大约有一万行,每行一个词,要求统计出其中最频繁出现的前10个词,请给出思想,给出时间复杂度分析。

这题是考虑时间效率。用trie树统计每个词出现的次数,时间复杂度是O(n*le)(le表示单词的平准长度)。然后是找出出现最频繁的前10个词,可以用堆来实现,时间复杂度是O(n*lg10)。所以总的时间复杂度,是O(n*le)与O(n*lg10)中较大的哪一个。

5. 搜索引擎会把用户所有检索串都记录下来,每个查询串的长度为1-255字节。假设目前有一千万个记录,去除重复不超过3百万个。请你统计最热门的10个查询串,要求使用的内存不能超过1G。

1G/255B = 2^30/2^8 = 2^22,约等于400万,所以可以使用Trie树统计300万个字符串。统计出所有串的次数之后使用大小为10的最小堆得到次数即可。

6. 一共有N个机器,每个机器上有N个数。每个机器最多存O(N)个数并对它们操作。如何找到N^2个数中的中数?

方案1:先大体估计一下这些数的范围,比如这里假设这些数都是32位无符号整数(共有2^32个)。我们把0到2^32-1的整数划分为N个范围段,每个段包含2^32/N个整数。比如,第一个段位0到(2^32/N)-1,第二段为2^32/N到(2^32*2/N)-1,…,第N个段为2^32(N-1)/N到2^32-1。然后,扫描每个机器上的N个数,把属于第一个区段的数放到第一个机器上,属于第二个区段的数放到第二个机器上,…,属于第N个区段的数放到第N个机器上。注意这个过程每个机器上存储的数应该是O(N)的。下面我们依次统计每个机器上数的个数,一次累加,直到找到第k个机器,在该机器上累加的数大于或等于N^2/2,而在第k-1个机器上的累加数小于N^2/2,并把这个数记为x。那么我们要找的中位数在第k个机器中,排在第(N^2/2-x)位。然后我们对第k个机器的数排序,并找出第N^2/2-x个数,即为所求的中位数的,复杂度是O(N^2)。
方案2:先对每台机器上的数进行排序。排好序后,我们采用归并排序的思想,将这N个机器上的数归并起来得到最终的排序。找到第N^2/2个便是所求,复杂度是O(N^2*lgN^2)。

海量数据问题总结

1. 找最大数、找最大的100个数

内存中维护一个大小为100的最大堆,按照内存大小每次顺序读取大文件的一部分到内存中,将这些数据插入堆中,最后留在堆中的数据就是最大的100个数。(不需要保存中间结果,顺序处理即可)

2. 排序

按照内存大小每次顺序读取大文件的一部分到内存中,将这些数据排序后写入一个小文件中,最后对所有小文件进行归并排序。生成小文件的操作叫做生成“初始顺串”,可以使用置换选择排序使生成的初始顺串尽可能大,减少后续归并的次数。归并时,二路归并效率比较低,实际应用中用赢者树或败者树实现多路归并。

PS:

  • 需要保存中间结果,但是不需要聚合相同数据。
3. 统计频率、去重、找频率最高、找频率最高100个、按频率排序

将大文件按照hash(data)%num分成num个小文件,保证内存可以存的下,存不下的文件继续分割。将每份数据加载到内存,使用trie树或hashmap统计频率,将结果写到多个小文件中,然后对多个文件进行归并处理。

PS:

  • 需要保存中间结果,且需要聚合相同数据
  • 使用hash取模进行分割的目的是为了聚合相同的数据到同一个文件中,聚合相同数据的原因是减少内存一次需要处理的数据量,因为数据的种类可能很多,内存放不下。如果内存可以放下所有数据种类,不进行分割也可以,这样只在内存中维护一个大的trie树或hashmap,顺序读取大文件,统计所有数据频率即可。

 

posted @ 2017-12-27 14:29  爱读书的小明  阅读(866)  评论(0编辑  收藏  举报