将许多二进制文件传输到SQL Server数据库的有效方法

时间:2013-03-29 22:26:31

标签: c# sql-server nhibernate memory filesystems

我们要求Winforms应用程序从本地文件系统(或网络位置)读取数千个文件并将它们存储在数据库中。

我想知道加载文件最有效的方法是什么?总共可能有数GB的数据。

当前使用了

File.ReadAllBytes但是当计算机的内存用完时,应用程序最终会锁定。

当前代码循环遍历包含文件路径的表,用于读取二进制数据:

protected CustomFile ConvertFile(string path)
{
    try
    {
        byte[] file = File.ReadAllBytes(path);
        return new CustomFile { FileValue = file };
    }
    catch
    {
        return null;
    }
}

然后使用NHibernate作为ORM将数据保存到数据库(SQL Server 2008 R2或2012)。

2 个答案:

答案 0 :(得分:1)

首先,请允许我说明我的知识是在.NET 4.0之前,所以这些信息可能已经过时,因为我知道他们将在这方面做出改进。

不要使用File.ReadAllBytes来读取大文件(大于85kb),特别是当您按顺序对多个文件执行操作时。我再说一遍,不要。

使用类似流和BinaryReader.Read的内容来缓冲您的阅读。即使这听起来效率不高,因为你不会通过单个缓冲区爆破CPU,如果用ReadAllBytes进行,它就不会像你发现的那样工作。

原因是因为ReadAllBytes读取字节数组中的整个内容。如果mem中的那个字节数组是> 85Kb(还​​有像数组元素这样的其他注意事项),它会进入大对象堆,这很好,但是,LOH不会移动内存,也不会对释放的空间进行碎片整理,所以,简化,这可能发生:

  • 读取1GB文件,LOH中有1GB块,保存文件。 (无GC循环)
  • 读取1.5GB文件,你要求一块1.5GB的内存,它会进入LOH的末尾,但是你说你得到一个GC周期,所以你之前使用的1GB块会被清除,但现在你有一大块2.5GB内存,第一个1GB免费。
  • 读取1.6GB文件,开头的1GB空闲块不起作用,因此分配器结束。现在你有一块4.1GB的内存。
  • 重复。

你的内存不足,但你肯定没有真正使用它,碎片可能会让你失望。如果文件非常大(我认为Windows 32位的进程空间是2GB?),你实际上可以实现真正的OOM情况。)

如果文件没有被排序或相互依赖,那么通过使用BinaryReader缓冲读取它们的几个线程就可以完成工作。

参考文献:

http://www.red-gate.com/products/dotnet-development/ants-memory-profiler/learning-memory-management/memory-management-fundamentals

https://www.simple-talk.com/dotnet/.net-framework/the-dangers-of-the-large-object-heap/

答案 1 :(得分:0)

如果你有很多文件,你应该逐一阅读。

如果你有大文件,并且数据库允许它,你应该逐块读取它们到缓冲区并逐块写入数据库。如果使用File.ReadAllBytes,当文件太大而无法放入运行时的内存时,可能会得到OutOfMemoryException。上限小于2 GiB,当应用程序运行一段时间内存碎片时,上限更小。