当前位置:  开发笔记 > 编程语言 > 正文

给定内存约束时,对具有大量数据的文件进行排序

如何解决《给定内存约束时,对具有大量数据的文件进行排序》经验,为你挑选了5个好方法。

要点:

我们同时处理数千个平面文件.

内存约束是一个主要问题.

我们为每个文件进程使用线程.

我们不按列排序.文件中的每一行(记录)都被视为一列.

做不到:

我们不能使用unix/linux的sort命令.

我们不能使用任何数据库系统,无论它们有多么轻盈.

现在,我们不能只加载集合中的所有内容并使用排序机制.它会占用所有内存,程序会出现堆错误.

在那种情况下,您如何对文件中的记录/行进行排序?



1> phisch..:

看起来你正在寻找的是 外部排序.

基本上,您首先对小块数据进行排序,将其写回磁盘,然后迭代这些数据以对所有数据进行排序.


从我的研究中,我理解的是,如果你在一个文件中有1000条记录并且你一次读取100条,则将其排序为100并将排序后的版本放入临时文件中,这将创建10个临时排序文件.然后按顺序读取两个文件并创建另一个已排序(现在更大)的文件,并删除刚读过的另外两个文件.继续,直到您有一个文件.当真?现在,假设您在一个文件中有1000万条记录,并且您一次读取5000条,您创建了多少临时文件以及花费多少时间来获得最终版本?

2> x4u..:

您可以读取较小部分的文件,对它们进行排序并将它们写入临时文件.然后你再次按顺序读取其中的两个并将它们合并到一个更大的临时文件中,依此类推.如果只剩下一个,则排序文件.基本上就是在外部文件上执行的Megresort算法.它可以很好地扩展到任意大文件,但会导致一些额外的文件I/O.

编辑:如果您对文件中行的可能差异有一些了解,则可以使用更有效的算法(分发排序).简化后,您将读取原始文件一次,并将每行写入临时文件,该文件仅包含具有相同第一个字符(或特定范围的第一个字符)的行.然后按升序迭代所有(现在很小的)临时文件,在内存中对它们进行排序并将它们直接附加到输出文件中.如果临时文件太大而无法在内存中进行排序,则可以根据行中的第二个字符重新为此进行相同的处理,依此类推.因此,如果您的第一个分区足够好以生成足够小的文件,那么无论文件有多大,您都只有100%的I/O开销,



3> Eduardo..:

尽管有限制,我还是会使用嵌入式数据库SQLITE3.像你一样,我每周工作10-15百万个平面文件行,导入和生成排序数据非常非常快,而且你只需要一点免费的可执行文件(sqlite3.exe).例如:下载.exe文件后,在命令提示符下可以执行以下操作:

C:> sqlite3.exe dbLines.db
sqlite> create table tabLines(line varchar(5000));
sqlite> create index idx1 on tabLines(line);
sqlite> .separator '\r\n'
sqlite> .import 'FileToImport' TabLines

然后:

sqlite> select * from tabLines order by line;

or save to a file:
sqlite> .output out.txt
sqlite> select * from tabLines order by line;
sqlite> .output stdout



4> danben..:

我会启动一个EC2集群并运行Hadoop的MergeSort.

编辑:不确定您想要多少细节,或者什么.EC2是亚马逊的弹性计算云 - 它允许您以低成本按小时租用虚拟服务器.这是他们的网站.

Hadoop是一个开源MapReduce框架,专为大型数据集的并行处理而设计.作业是MapReduce的一个很好的候选者,它可以被分割成可以单独处理然后合并在一起的子集,通常是通过对键进行排序(即分而治之的策略).这是它的网站.

正如其他海报所提到的,外部排序也是一个很好的策略.我认为我在两者之间决定的方式取决于数据的大小和速度要求.一台机器可能会被限制为一次处理一个文件(因为您将耗尽可用内存).因此,只有在需要以更快的速度处理文件时,才能查看类似EC2的内容.



5> KLE..:

正如其他提到的,您可以按步骤处理.
我想用我自己的话解释这一点(第3点不同):

    按顺序读取文件,在内存中一次处理N条记录(N是任意的,具体取决于您的内存约束和您想要的临时文件的数量T.).

    对内存中的N条记录进行排序,将它们写入临时文件.循环在T上,直到你完成.

    同时打开所有T temp文件,但每个文件只读一条记录.(当然,有缓冲区).对于这些T记录中的每一个,找到较小的记录,将其写入最终文件,并仅在该文件中前进.


好处:

内存消耗,你想要的低.

与内存中的所有内容策略相比,您只能执行双倍的磁盘访问.不错!:-)


例如:

    原始文件有100万条记录.

    选择有100个临时文件,因此一次读取和排序10 000条记录,并将它们放在自己的临时文件中.

    一次打开100个临时文件,读取内存中的第一条记录.

    比较第一个记录,写下较小的并提前该临时文件.

    循环在第5步,一百万次.


EDITED

你提到了一个多线程应用程序,所以我想知道......

正如我们从这些关于这种需求的讨论中看到的那样,使用更少的内存会降低性能,在这种情况下具有显着的影响.所以我也建议一次使用一个线程来处理一种,而不是多线程应用程序.

如果你处理十个线程,每个线程有十分之一的可用内存,你的性能将会很糟糕,远远低于初始时间的十分之一.如果您只使用一个线程,并将其他9个需求排队并依次处理它们,那么您的全局性能会更好,您将更快地完成十个任务.


阅读此响应之后: 在给定内存约束的情况下对具有大量数据的文件进行排序 我建议您考虑这种分发排序.在你的背景下,这可能是巨大的收获.

我的提议的改进是你不需要一次打开所有临时文件,只打开其中一个.它可以节省您的一天!:-)

推荐阅读
linjiabin43
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有