如何在单台8G内存机器上实现1TB日志排序?

 

问题:

最近在面试人的时候,我经常会问一个问题,题目如下:

现在有一批日志数据,大小为1TB,数据是某一天的生产的日志数据,现在只有一台单CPU及8G内存的机器,请使用算法对此日志数据按时间的先后顺序进行排序。

 

可面试了好多人,都没有回答出这个问题

针对这个问题今天来讲讲这个问题我的解法。

 

解决方案1:桶排序

它的原理是利用分区字段的特点,将数据分别写入到多个桶文件中,再针对每个桶文件使用内存排序算法(如快速排序),将文件内容进行排序输出到有序桶文件中。最后将这些文件按分桶的顺序组合组合起来,形成最终有序的文件。

 

示例

这样说可能有点抽象,我来具体举个简单的示例吧

 

 

桶排序过程:

  1. 确定分桶的数量,像本例中就是按年龄段划分,20-29,30-39,40-49,50,这样划分出4个桶
  2. 再将数据按桶的范围装入桶中。
  3. 对桶中的数据进行一遍排序。
  4. 按照分桶的顺序将文件依次读取写入到一个文件中,就形成了最终的有序数据

 

 

再来说说桶排序的特点:

  1. 桶排序的时间复杂度为O(n),假如有N个数据,划分出M个桶。每个桶内元素(T=N/M),如果桶内排序算法采用快速排序,时间复杂度O(T*logT),M个桶的时间复杂度就是O(M*T*logT),再加上T=N/M,即可得到N*log(N/M),当M与N接近时,时间复杂度为常量级,这时候桶排序时间复杂度接近O(N)
  2. 对排序的数据要求很苛刻,数据必须很容易的划分出M个桶,桶与桶之前有着天然的大小顺序。对每个桶内的数据排序完成后,桶与桶之前无需再进行排序。
  3. 桶内的数据在各个桶内须必较平均。如果经过桶的划分。桶内的数据非常不平均,有的非常多,有的非常少,时间复杂度将不再是O(N),可能退化为(N*logN)。
  4. 桶排序比较适合外部排序。即为对磁盘文件进行排序,因为一般磁盘文件数据量较大,无法一次全部加载到内存中

 

解答问题

回到开头提到的问题,桶排序如何解决针对1TB的数据进物排序的问题呢?

  1. 划分桶,在问题中,我们需要排序的是日志中的时间,而时间是很容易划分出桶的,而时间使用哪个维度来划分桶是我们所面临的问题?如果选择分钟可得到1440个桶,每个桶平均就是0.7G左右,但像日志中数据可能有数据集中的问题,尤其是像1TB这么大的日志,高峰时间段可能是平均的10倍,分桶可能非常的不平均;这样建议再降低维度使用秒,一天可最多得到86400个分桶。平均每个文件只有12M左右,高峰也不过才120M,所以选择秒是比较合适的分桶。
  2. 再将数据按秒划分到各个桶即可。
  3. 再将各个桶内的数据进行排序。
  4. 最后按桶的先后顺序,合并到一个文件中,就得到了1TB日志的排序结果。

 

代码我就不放在这里了。有兴趣可以去我在github看看

桶排序的实现

https://github.com/kkzfl22/datastruct/blob/master/src/main/java/com/liujun/datastruct/base/sort/bigdataSort/bucketSort/BucketSort.java

 

 

 

解决方案2:归并排序

再来说说另外的一种解决办法吧,归并排序,它的核心思想:分而治之,具本来说就是将大数据分解成一个一个小数据,将小数据进行排序,然后再将这些小数据合并排序成终最终有序的数据。

示例

还是以年龄数据为例来看排序的数据吧

现有一批年龄数据20,30,50,40,22,37,45,48,使用分治思想进行排序

 

 

详细排序过程

详细的来说明下过程:图中的序号与下面的过程进行对应。
1,    将一个原始数组中的数据拆分到3个数组中。
第一个小数组中的数据是:20、30、50;
第二个小数组中的数据是:40、22、37;
第三个小数组中的数据是:45、48。
2,    对每个小数组进行排序操作,使用快速排序。可得到如下结果
第一个小数组中的数据:20、30、50;
第二个小数组中的数据:22、37、40;
第三个小数组中的数据:45、48。
3,    开始进行小数组的合并操作。声明一个3(根据分拆的数组个数定义大小)个大小的合并数组,从每个小数组中取出首个元素。即可得到20、22、45,进行排序操作,结果为: 20、22、45,将20从合并数组中移出,输出到最终的数组中,最终数组现在仅一个数据20,这一个元素。
4,    从20这个所在的小数组中取出下一个元素,也就是图中的30这个数字,加入到合并数组中,可得到数据:22、45、30,进行排序,结果为:22、30、45。将22从合并数组中移出,将22输出到最终的数组中,现在最终数组中有20,22这两个元素。
5,    从22这个数字所在的小数组中取出下一个元素,也就是37这个数字,加入到合并数组中,可得到:30、45、37,进行排序,结果为:30、37、45,将30这个数字从合并数组中移出,输出到最终的数组中,现在最终的数组中有20、22、30这三个元素
6,    从30这个数字所在的小数组中取出下一个元素,也就是50这个数字,加入到合并数组中,可得到:37、45、50,进行排序,结果为:37、45、50,将37这个数字从合并数组中移出,输出到最终的数组中,现在最终的数组中有20、22、30、37这四个元素。
7,    从37这个数字所在的小数组中取出下一个元素,也就是40这个数字,加入到合并数组中,可得到:45、50、40,进行排序,结果为40、45、50,将40这个数字从合并数组中移出,输出到最终数组中,现在最终的数组中有20、22、30、37、40这五个元素。
8,    从40这个数字所在小数组中取下一个元素,但40这个数字是这个小数组中的最后一个,所以没有数据加入到合并数组中,将合并数组中的元素45、50,进行排序,结果为:45、50,将45这个数字从合并数组中移出,加入到最终数组中,现在最终的数组中有20、22、30、37、40、45,这六个元素。
9,    从45这个数字所在的小数组中取出下一个元素,也就是48这个数字。加入到合并数组中,可得到48、50,进行排序,结果为48、50,将48这个数字从合并数据中移出,加入到最终数组中,现在最终数据中有20、22、30、37、40、45、48,这七个元素。
10,    从48这个数字所在的小数组中取出下一个元素,但48是这个小数组中的最后一个,所以没有数据加入到合并数组中,现在合并数组中仅有一个数字,无需排序,直接将50加入到最终数组中,现在最终数组中有20、22、30、37、40、45、48、50这八个元素。排序完成。
 

 

 

再来说说归并排序的特点:

  1. 归并排序的时间复杂度是 O(nlogn),这个推导有点复杂,略过。而且即使是最坏情况下,时间复杂度依然是O(nlogn)
  2. 归并排序并非原地排序算法,它需要一个额外的存储空间,空间复杂变为O(n)
  3. 归并排序也适用于外部排序,相对于桶排序来说,它就没有苛刻的数据要求。通用性更强

 

解答问题

再来讲讲使用归并排序的思想来实现对1TB的数据进行排序的问题

  1. 将1TB的文件按固定大小进行切分,比如大小256M,读取时可按行读取,限制大小在256M以内,需保证完整的行,这样可以切出大约4096个文件。
  2. 将这4096个文件,使用快速排序算法对这每个文件进行排序操作,可得到4096个排序的文件
  3. 将这4096个文件进行合并输出,合并的逻辑就是从每个文件中依次获取数据,排序,输出,就像示例中的那样。就可以得到一个最终有序的文件。

 

代码我也不放在这里了。有兴趣可以去我在github看看

归并排序的实现

https://github.com/kkzfl22/datastruct/blob/master/src/main/java/com/liujun/datastruct/base/sort/bigdataSort/mergeSort/MergeSort.java

 

 

总结下

 

桶排序的性能更好,最好情况下能够在O(N)的时间复杂度内解决,但对于数据要求很苛刻,所以选择此算法,需要小心甄别数据的特点:能够很容易的划分出N个桶,最好能比较平均。

 

归并排序的通用性更好,几乎对数据无要求,但时间复杂度比桶排序高,它的场景是在比较通用的大文件排序中.

 

 

 

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值