【问题标题】:How to ensure all data has been physically written to disk?如何确保所有数据都已物理写入磁盘?
【发布时间】:2010-09-27 20:29:20
【问题描述】:

我了解 .NET FileStream 的 Flush 方法只是将当前缓冲区写入磁盘,但依赖于 Windows 的磁盘驱动程序和硬盘固件,这并不能保证数据实际上是物理写入磁盘的。

有没有 .NET 或 Win32 方法可以给我这个保证?所以如果在调用这个方法回来后一纳秒掉电,我仍然可以确定一切正常吗?

【问题讨论】:

    标签: c# .net filestream flush


    【解决方案1】:

    Stefan S. 说:

    我了解.NET FileStream 的 Flush 方法只将当前缓冲区写入磁盘

    不,.NET FileStream 的 Flush 仅将 .NET 缓冲区写入操作系统缓存,它不会将操作系统缓存刷新到磁盘。遗憾的是,这门课上的 MSDN 文档并没有这么说。对于 .NET

    using System.Runtime.InteropServices;
    . . .
    
    // start of class:
    [DllImport("kernel32", SetLastError=true)]
    private static extern bool FlushFileBuffers(IntPtr handle);
    . . .
    
    stream.Flush();     // Flush .NET buffers to OS file cache.
    #pragma warning disable 618,612 // disable stream.Handle deprecation warning.
    if (!FlushFileBuffers(stream.Handle))   // Flush OS file cache to disk.
    #pragma warning restore 618,612
    {
      Int32 err = Marshal.GetLastWin32Error();
      throw new Win32Exception(err, "Win32 FlushFileBuffers returned error for " + stream.Name);
    }
    

    对于 .NET 4.0,您可以改用新的 flush(true) 方法。 2012 年 9 月 11 日更新:MS 错误报告 here 说它已损坏,然后修复,但没有说明修复的版本或服务包!听起来错误是如果内部 .NET FileStream 缓冲区为空,则 Flush(true) 什么也没做??

    【讨论】:

      【解决方案2】:

      在 Windows 下,查看FlushFileBuffers(Win32 API)。

      【讨论】:

      • 谢谢 :) 我很快完成了性能测试,FileStream.Flush() 太快了,难以置信。 FileStream 的 SafeFileHandle 上的 FlushFileBuffers 和我预期的一样慢(在我的测试中比 Flush() 慢 100 倍)
      • 我发现调用 FlushFileBuffers 会导致异常(stackoverflow.com/q/9195807/4540)。在 .NET 4 下,按照@jimvfr 的建议 (stackoverflow.com/a/3992428/4540) 调用 FileStream.Flush(true) 会更容易、更安全。
      • 缓存在文件系统缓存中的要写入磁盘的文件数据。该数据通常是延迟写入的,基于磁盘写入头的位置。拥有千兆字节的缓存数据在技术上是可行的,因此可能需要相当长的时间。如果这对您很重要,请考虑使用 FileOptions.WriteThrough 选项。
      • 有没有办法将此逻辑应用到注册表中?
      【解决方案3】:

      嗯,你可以关闭文件......这可能会做到这一点。实际上,随着 HAL 抽象化、虚拟化和磁盘硬件现在拥有比 计算机 几年前更多的处理能力和缓存内存,您将不得不忍受希望磁盘完成它的工作.

      事务文件系统从未真正实现过;-p 当然,您也许可以考虑使用数据库作为后端,并使用它的事务系统?

      除此之外:请注意,并非所有流甚至都保证Flush() - 例如,GZipStream 等即使在刷新后仍保留未提交数据的工作缓冲区 - 使其刷新的唯一方法 一切 em> 是Close()它。

      【讨论】:

      • 从技术上讲,通过写入数据库,无法保证断电或其他灾难性故障不会丢失写入或以某种方式损坏数据库,尽管它比简单的文件系统更有可能存活写。
      • 是的,但是您可以将“标记为已完成”和“结果如下”包装在同一个事务中
      • @cletus 如果数据库系统不能保证,那么它要么损坏(至少我认为 DBMS 没有 ACID 损坏),要么在损坏的系统(操作系统、硬件等)上运行。
      【解决方案4】:

      我注意到 .NET 4 #Flush(true) 实际上并没有写入磁盘。我们遇到了数据损坏的奇怪问题,我在 MS 网站上找到了这个bug report

      错误报告的详细信息选项卡有一个您可以运行的测试程序来显示问题;

      1. 将一堆数据写入磁盘
      2. fs.Flush(true)。这不需要时间(比可能写入磁盘要快得多)。
      3. 使用 win32 API FlushFileBuffers。这需要很长时间。

      我正在切换到 win32 FlushFileBuffers 调用...

      【讨论】:

      • fs.Flush(true) 对我有用。 Windows 10 x64 创建者更新,.NET 4.5
      • @jjxtra:您是否使用网络服务器进行了测试并实际拔掉了插头?
      • @Joshua 不,只用本地文件系统测试过,所以不能代表这种情况
      • @jjxtra:除非您通过拔出磁盘导致磁盘故障,否则它似乎可以工作。您需要网络服务器知道您触发了比赛。
      【解决方案5】:

      文件系统缓存中缓冲的文件数据要写入磁盘。该数据通常是延迟写入的,基于磁盘写入头的位置。拥有千兆字节的缓存数据在技术上是可行的,因此可能需要相当长的时间。如果这对您很重要,那么请考虑使用 FileOptions.WriteThrough 选项。

      【讨论】:

      • 这也被证明是无效的。操作系统仍会缓存。
      • @trevster344,你有那个来源吗?
      • @Matt FILE_FLAG_WRITE_THROUGH 曾经被 SATA 驱动器损坏:disruptivesql.wordpress.com/2012/05/08/sata-and-write-through 我不知道目前的情况,但如果没有进一步的研究和测试,我不会相信它。跨度>
      【解决方案6】:

      将缓冲区的内容刷新到磁盘有一个简单的答案。 在你的 WriteAllText 函数之后,打开文件,关闭它,然后重置它

      这是一个例子

      My.Computer.FileSystem.WriteAllText(yourfilename, "hello", False, System.Text.Encoding.ASCII)
      FileOpen(1, yourfilename, OpenMode.Input)
      FileClose(1)
      Reset()
      

      【讨论】:

        【解决方案7】:

        抽象级别太多了,无法绝对确保将数据写入磁盘,一直到硬件级别。

        不是出色的性能或万无一失,但是一旦在单独的过程中写入文件并检查大小或内容,如何重新打开文件?

        【讨论】:

        • 那毫无意义,因为它不能保证数据已经物理写入磁盘。
        • 不仅非万无一失,而且毫无用处。重新打开请求通过缓存层,与其他任何内容相同。
        猜你喜欢
        • 2013-10-05
        • 2021-04-13
        • 2012-12-28
        • 2011-11-23
        • 2020-06-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-09-24
        相关资源
        最近更新 更多