按顺序读取大量小文件

时间:2009-10-13 14:18:21

标签: c# .net windows filesystems

我有这个问题:我有一个小文件的集合,每个大约2000字节(它们都是完全相同的大小),并且大约有~100,000个em,相当于大约200兆字节的空间。我需要能够实时选择这些文件中的范围。假设文件1000到1100(总共100个文件),读取它们并通过网络快速发送它们。

好处是文件将始终按顺序读取,即它始终是一个“从此文件和一百多个”的范围,而不是“此文件在这里,那个文件在那里,等等。 ”。

文件也可以在运行时添加到此集合中,因此它不是固定数量的文件。

我提出的当前方案是这样的:没有文件大于2000字节,所以不是在磁盘上分配多个文件,我将有一个包含所有其他文件的大文件,甚至是2048字节每个2048块的2个第一个字节的间隔是下一个2046字节中包含的文件的实际字节大小(文件大小在1800到1950字节之间),然后在此文件中搜索而不是打开新文件处理我需要阅读的每个文件。

因此,当我需要在X位置获取文件时,我将执行X * 2048,读取前两个字节,然后将(X * 2048)+2中的字节读取到前两个字节中包含的大小。这个200mb的大文件只会附加,所以即使序列化的输入线程/进程(还没有决定)附加更多的数据,它也是安全的。

这必须在Windows上可行,C是一个选项,但我更喜欢C#。

9 个答案:

答案 0 :(得分:3)

您是否反对将这些文件存储在数据库中?

一个简单的RDBMS可以大大加快对一堆2k文件的搜索和排序

答案 1 :(得分:2)

我认为你的想法可能是你做得体面的最佳工作。

或者您可以购买固态磁盘而不关心文件大小。

或者,如果您不依赖于保持较低的RAM使用率(也是最快的选项),您可以将整个数据预加载到内存中。

或者您可以使用数据库,但这里的开销很大。

答案 2 :(得分:2)

这听起来像是一个合理的选择。

当读取范围的数据时,我很想找到“数据块”的开头,并将整个数据读入内存(即所有文件的2048字节缓冲区)走。这将使文件IO降至最低。

一旦你获得了内存中的所有数据,你就可以解码大小并只发送真实数据的位。

将所有内容加载到内存中可能是一个好主意,但这完全取决于它的修改频率和查询频率。

问题还有什么,而不仅仅是“这是一件理智的事情吗?”

答案 3 :(得分:1)

您确定永远不想删除1200到1400之间的文件吗?完成转移后会发生什么?数据是存档的还是会不断增长?

我真的不明白为什么将所有数据附加到单个文件会提高性能。相反,它可能会给您带来更多问题。那么,你为什么要把它们结合起来呢?

要考虑的其他事项是,如果大量文件从磁盘上的坏扇区中间腐烂,会发生什么?看起来你失去了一切。将它们分开应该可以提高它们的生存能力。

你可以使用大文件而无需在内存中加载整个文件,但这并不容易,你最终还是需要进行一些低级编码才能完成。不要限制自己。此外,如果文件需要一些手动编辑怎么办?大多数程序都会强制你加载并锁定整个程序。

此外,拥有一个大文件意味着您不能让多个进程读/写数据。这限制了可扩展性。

如果您知道需要#1000到1100的文件,则可以使用内置(c#)代码来获取符合该条件的文件集合。

答案 4 :(得分:1)

你可以简单地连接一个大文件'dbase'中的所有文件而不需要任何页眉或页脚。

在另一个文件'index'中,您可以将所有小文件的位置保存在'dbase'中。这个索引文件非常小,可以完全缓存在内存中。

此方案允许您快速读取所需文件,并在收藏结束时添加新文件。

答案 5 :(得分:0)

你的计划听起来可行。似乎文件流可以执行您需要的搜索和读取。您是否遇到了具体的实施问题,或者您是否在寻找更好的方法?

是否有更好的方法可能取决于您读取文件的速度与在网络上传输文件的速度有多快。假设你可以比发送它们更快地读取大量单个文件,也许你可以设置一个有界缓冲区,在那里你将x个文件读入队列。另一个线程是从队列中读取并在网络上发送它们

答案 6 :(得分:0)

我会以一种方式修改你的方案:而不是读取前两个字节,然后使用那些来确定下一个读取的大小,我只是立即读取2KiB,然后使用前两个字节来确定多少你传输的字节数。

通过仅使用一次磁盘读取,您可能会节省更多时间,而不是将最后约150个字节从磁盘传输到内存中。

另一种可能性是将文件的数据打包在一起,并维护一个单独的索引来告诉您每个文件的起始位置。对于您的情况,这样做的好处是,您可以将任意数字组合成一个大型读取,而不是从磁盘执行大量小(2K)读取。每次阅读大约64-128K通常可以节省相当多的时间。

答案 7 :(得分:0)

您可以坚持使用一个大文件的解决方案,但使用内存映射来访问它(请参阅here例如)。这可能会更高效,因为您还可以避免分页,并且虚拟内存管理已针对传输4096字节的块进行了优化。 Afaik,没有直接支持内存映射,但是here是如何为C#包装WIN32 API调用的一些示例。

有关SO的相关问题,另请参阅here

答案 8 :(得分:0)

有趣的是,这个问题让我想起了这个旧的SO问题中的问题:

Is this an over-the-top question for Senior Java developer role?