在面试软件开发工程师时,常常会遇到海量数据排序和去重的面试题,特别是大数据岗位。html
例1:给定a、b两个文件,各存放50亿个url,每一个url各占64字节,内存限制是4G,找出a、b文件共同的url?面试
针对上述问题,咱们分治算法的思想。算法
遍历文件a,对每一个url求取hash(url)%1000,而后根据所取得的值将url分别存储到1000个小文件(记为a0,a1,...,a999,每一个小文件约300M),为何是1000?主要根据内存大小和要分治的文件大小来计算,咱们就大体能够把320G大小分为1000份,每份大约300M。数组
遍历文件b,采起和a相同的方式将url分别存储到1000个小文件(记为b0,b1,...,b999)。性能优化
文件a的hash映射和文件b的hash映射函数要保持一致,这样的话相同的url就会保存在对应的小文件中。好比,若是a中有一个url记录data1被hash到了a99文件中,那么若是b中也有相同url,则必定被hash到了b99中。微信
因此如今问题转换成了:找出1000对小文件中每一对相同的url(不对应的小文件不可能有相同的url)数据结构
由于每一个小文件大约300M,因此咱们再能够采用上面解答中的想法。架构
常见的高效解答思路有:堆排序法、分治策略和BitMap(位图法)。函数
堆排序是4种平均时间复杂度为nlogn的排序方法之一,其优势在于当求M个数中的前n个最大数,和最小数的时候性能极好。因此当从海量数据中要找出前m个最大值或最小值,而对其余值没有要求时,使用堆排序法效果很好。微服务
从1亿个整数里找出100个最大的数
这里采用堆排序将空间复杂度讲得很低,要排序1亿个数,但一次性只需读取100个数字,或者设置其余基数,不须要一次性读完全部数据,下降对内存要求。
整体思想:分而治之。经过分治将大数据变成小数据进行处理,再合并。
首先区份内部排序和外部排序:
步骤:
其次要注意待排序数据的特色。若是待排序数据具备某些特色,每每可以有更加有效的方法解决。 同时,这种思想也更加贴近大数据应用的思惟方式。
32位机器上,一个整形,好比int a; 在内存中占32bit位,能够用对应的32bit位对应十进制的0-31个数,BitMap算法利用这种思想处理大量数据的排序与查询.
其优势是运算效率高,不准进行比较和移位,且占用内存少,好比N=10000000;只需占用内存为N/8=1250000Byte=1.25M。
例2:在特定的场合下:
对10亿个不重复的整数进行排序。
找出10亿个数字中重复的数字。
如上的题目通常会限制内存。
咱们换一个与上面示例类似的题目进行演示解答过程。
例3:一台主机,2G内存,40亿个不重复的没排过序的unsigned int的整数的文件,而后再给一个整数,如何快速判断这个整数是否在那40亿个数当中?
咱们能够有几种方法解答如上的题目。
若是内存足够将40亿个数所有放到内存中,逐个遍历,此时时间复杂度为O(N)。但是如今在内存不足,须要分批读一部分数据到内存而后在作判断,加上I/O操做的时间,时间复杂度远远大于O(N)。
这时,性能问题主要集中在I/O操做,和遍历数组上。那么有没有下降时间复杂度的方法呢?答案是确定的,若是咱们假定内存是足够的,只去优化时间,能够获得下面的方法。
申请一个4G超大数组char a[0~2^32-1],将文件中出现的数字置为1,没有出现的置为0。 例如文件存在一个整数1000022,就将a[100002211]=1。
a | 0 | 1 | 2 | ... | 2^32-1 |
---|---|---|---|---|---|
value | 1 | 0 | 1 | 0 | 0 |
这时时间复杂度为O(1),但是空间问题尚未解决。分析下咱们的算法,以所需判断的整数为数组下标,用0/1来区分整数是否在。一共用了一个字节来做为标记位,而事实上1Bit就足够标记了。若是能把这部分空间优化掉,4G/8 < 2G 那么就能够解决问题了。看下面的方法。
在前面两篇文章中,咱们讲过BitMap的概念和应用。
将整数映射到bit上,例如整数10,10/8=1,10%8=2,那么就将a[1]的b[2]置为1。这样时间复杂度便是O(1),内存也获得了压缩。
a | 0 | 1 | 2 | ... | 2^32/8-1 |
---|---|---|---|---|---|
bit | 0 1 2 3 4 5 6 7 | 0 1 2 3 4 5 6 7 | 0 1 2 3 4 5 6 7 | 0 1 2 3 4 5 6 7 | 0 1 2 3 4 5 6 7 |
flag | 0 0 0 0 0 0 0 0 | 0 0 1 0 0 0 0 0 | 0 0 0 0 0 0 0 0 | 0 0 0 0 0 0 0 0 | 0 0 0 0 0 0 0 0 |
unsinged int只有接近43亿(unsigned int最大值为2^32-1=4294967295,最大不超过43亿),咱们还能够用某种方式存储没有出现过的3亿个数(使用数组{大小为3亿中最大的数/8 bytes}存储),若是出如今3亿个数里面,说明不在40亿里面。3亿个数存储空间通常小于40亿个。ps:存储4294967296(不到43亿)须要512MB, 而存储294967296只须要35.16MB。
这里须要注意的是,BitMap排序须要的时间复杂度和空间复杂度依赖于数据中最大的数字。当数据相似(1,1000,10万)只有3个数据的时候,用BitMap时间复杂度和空间复杂度至关大,只有当数据比较密集时才有优点。
在处理海量数据时,咱们会想到这些数据的存储结构。在全部具备性能优化的数据结构中,你们使用最多的就是hash表,是的,在具备定位查找上具备O(1)的常量时间,多么的简洁优美。可是数据量大了,内存就不够了。固然也可使用相似外排序来解决问题的,因为要走IO因此时间上又不行。BitMap基于位的映射,用一个Bit位来标记某个元素对应的Value, 而Key便是该元素。因为采用了Bit为单位来存储数据,所以BitMap在存储空间方面,能够大大节省。
本文总结了几种经常使用的海量数据处理方法,咱们能够根据实际的题意(空间、时间限制)进行灵活应用。了解散列表和BitMap能够参见前面两篇文章。
最后,欢迎购买笔者的新书《Spring Cloud微服务架构进阶》。
最后,留一个思考题给你们,和上面的解答过程相似,有兴趣的能够在文章下面留言讨论。
例4:现有3G的数据量,数据类型为整型,找出其中重复的数据。
数据:每一个数据不大于20亿,且每一个数据最多重复一次。
内存:最多用300M的内存进行操做。