【问题标题】:How do I avoid excessive Network File I/O when appending to a large file with .NET?使用 .NET 附加到大文件时,如何避免过多的网络文件 I/O?
【发布时间】:2010-06-29 22:54:24
【问题描述】:

我有一个程序可以打开一个大的二进制文件,向其中添加少量数据,然后关闭该文件。

FileStream fs = File.Open( "\\\\s1\\temp\\test.tmp", FileMode.Append, FileAccess.Write, FileShare.None );
fs.Write( data, 0, data.Length );
fs.Close();

如果在本程序运行前test.tmp为5MB,数据数组为100字节,本程序将导致超过5MB的数据通过网络传输。我本来希望文件中已经存在的数据不会通过网络传输,因为我没有读取或写入它。有什么办法可以避免这种行为?这使得附加到非常大的文件变得非常缓慢。

【问题讨论】:

  • 您是否尝试过 FileStream.Seek 方法?
  • 是否有激活的防病毒扫描程序进行访问检查?
  • 您可能会添加一些关于您的最终目标的信息(即日志系统或数据报告工具),因为这里的用户可能会提出一些您可能没有考虑过的建议。
  • @ChrisW: FileMode.Append 在内部调用 Seek。
  • 从我使用 598MB 文件的测试来看,我相信您还有其他问题。问题不在于您发布的代码。第一次运行时,在清除所有系统缓存后,写入前 100 个字节最多可能需要 7 秒钟,而读取的内容不超过 256KB(这可能是流的缓冲区填充)。随后的写入需要 80 毫秒、24 毫秒,最后是 3 毫秒。无论我指定什么 FileMode 和 FileAccess 选项。

标签: c# .net file-io networking


【解决方案1】:

0xA3 在上面的评论中提供了答案。性能不佳是由于访问时病毒扫描造成的。每次我的程序打开文件时,病毒扫描程序都会读取文件的全部内容以检查病毒,即使我的程序没有读取任何现有内容。禁用按访问病毒扫描消除了过多的网络 I/O 和较差的性能。

感谢大家的建议。

【讨论】:

    【解决方案2】:

    我找到了this on MSDN(内部调用了CreateFile):

    当应用程序通过网络创建文件时,最好使用 GENERIC_READ | GENERIC_WRITE 用于 dwDesiredAccess,而不是单独使用 GENERIC_WRITE。生成的代码更快,因为重定向器可以使用缓存管理器并发送更少的 SMB 和更多的数据。这种组合还避免了通过网络写入文件偶尔会返回 ERROR_ACCESS_DENIED 的问题。

    使用 Reflector,FileAccess 映射到 dwDesiredAccess,因此它似乎建议使用 FileAccess.ReadWrite 而不仅仅是 FileAccess.Write

    我不知道这是否会有所帮助:)

    【讨论】:

      【解决方案3】:

      您可以将数据缓存到本地缓冲区并定期(比现在少得多)附加到大文件中。这将节省大量网络传输,但是...这也会增加丢失缓存(和您的数据)的风险,以防您的应用崩溃。

      这种类型的日志记录(如果是这样的话)通常存储在数据库中。使用体面的 RDBMS 可以让您以最小的开销非常频繁地发布 100 字节的数据。需要注意的是 RDBMS 的维护。

      【讨论】:

        【解决方案4】:

        如果您有系统访问权限,或者可能是实际托管文件的机器的友好管理员,您可以制作一个位于另一端的小型侦听器程序。

        您调用它只传递要写入的数据,然后它在本地进行写入,避免了额外的网络流量。

        【讨论】:

          【解决方案5】:

          .NET 中的 File 对象有很多静态方法来处理这类事情。我建议尝试:

          File file = File.AppendAllText("FilePath", "What to append", Encoding.UTF8);
          

          当你反映这个方法时,原来它正在使用:

            using (StreamWriter writer = new StreamWriter(path, true, encoding))
          {
              writer.Write(contents);
          }
          

          这个 StreamWriter 方法应该允许您简单地在末尾附加一些东西(至少这是我在迄今为止遇到的每个日志记录实例中都使用过的方法)。

          【讨论】:

          【解决方案6】:

          将数据写入单独的文件,然后仅在必要时加入它们(如果可能,在主机上进行)。

          【讨论】:

          【解决方案7】:

          我做了一些谷歌搜索,正在研究如何快速读取过大的文件,并找到了这个链接http://www.4guysfromrolla.com/webtech/010401-1.shtml

          最有趣的部分是关于字节读取的部分: 除了更常用的 ReadAll 和 ReadLine 方法外,TextStream 对象还支持 Read(n) 方法,其中 n 是相关文件/文本流中的字节数。通过实例化一个附加对象(一个文件对象),我们可以获得要读取的文件的大小,然后使用 Read(n) 方法来遍历我们的文件。事实证明,相比之下,“读取字节”方法非常快:

          const ForReading = 1
          const TristateFalse = 0
          dim strSearchThis
          dim objFS
          dim objFile
          dim objTS
          set objFS = Server.CreateObject("Scripting.FileSystemObject")
          set objFile = objFS.GetFile(Server.MapPath("myfile.txt"))
          set objTS = objFile.OpenAsTextStream(ForReading, TristateFalse)
          
          strSearchThis = objTS.Read(objFile.Size)
          
          if instr(strSearchThis, "keyword") > 0 then
          Response.Write "Found it!"
          end if
          

          然后您可以使用此方法转到文件末尾并手动附加它,而不是使用文件流以附加模式加载整个文件。

          【讨论】:

          • -1:您是否注意到这是 2001 年的一篇文章,针对 VBScript,而不是针对 .NET?
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-02-27
          • 2010-12-25
          • 2013-04-14
          • 2022-01-03
          • 1970-01-01
          • 2022-01-02
          相关资源
          最近更新 更多