【问题标题】:transactional open/write/replace in .NET?.NET 中的事务性打开/写入/替换?
【发布时间】:2011-09-24 00:05:49
【问题描述】:

我在想也许我应该把它限制在 8k 以内。我想每次都打开一个文件并从头到尾写入。但是,如果由于某种原因(比如停电)它没有完成我不想要损坏的数据。有没有办法我可以打开/写入/关闭事务文件,所以除非它成功,否则它不会替换以前的文件?

当我用谷歌搜索时,我会得到很多关于数据库事务和 ado 而不是文件的结果

【问题讨论】:

  • NTFS 在默认情况下已经凭借其日志而具有事务性。不在这里,请在 superuser.com 上询问
  • 汉斯,问题是关于 Linux 上的单声道,通常不是 NTFS 系统 :)
  • 实际上我在 windows 上开发,但在单声道上运行它。所以......问题是关于整个.net

标签: .net linux file transactions mono


【解决方案1】:
  1. 写入临时文件,
  2. 删除旧文件
  3. 仅在写入成功时重命名临时文件。

如果您在第二步之后第三步之前断电,为了防止数据丢失,您需要多一步:

  • 在程序启动时,检查已删除主文件但尚未重命名的临时文件。如果您发现任何文件,请对这些文件再次执行第三步。

【讨论】:

  • 还是没有。想象一下:写入成功,删除或重命名要替换的文件,断电。
  • 没有数据丢失但“系统”处于无效状态。
  • @jason 只要我有办法确定哪个临时文件应该替换哪个文件,我可以检查原始文件是否存在,如果不将临时文件移动到它(因为原始文件在临时文件被完全写入之前将一直存在)
  • @Mark Byers:如果需要修复,则无效。就个人而言,我会使用事务性文件管理器/系统。
  • @jason:我需要它在单声道/Linux 上工作,但我认为 .NET 上没有。我更喜欢系统/库,但我认为没有可用的。我想我会设计它来知道临时文件是否完整并且是事务性的。也许我会使用 mysql blob 并限制文件大小
【解决方案2】:

Windows Vista 及更高版本中的 NTFS 是事务性的。

我不相信您可以从纯托管代码访问它 - 您需要 P/Invoke 到 Win32 API。

一个好的起点是CreateTransactionCommitTransactionRollbackTransactionCreateFileTransacted(和其他*Transacted)Win32 API函数。

【讨论】:

  • +1 是一个有趣的想法,但该方法仅在底层文件系统是事务性的情况下才有效。不幸的是,.NET 根本不能保证这一点。
  • @MarkByers 同意。我清楚地说明了“NTFS”和“Windows Vista”。
  • 是的,这就是为什么我不情愿地给它投了赞成票。但不幸的是,它在这里并不适用,因为 OP 使用的是 Linux。你需要扩展你的答案。
  • @MarkByers OP 没有在他的问题的任何地方声明“Linux”或“Mono”。话虽如此,我现在从其他 cmets 看到他在 Linux 上确实需要这个。我不熟悉 P/Invoke 在 Linux 上是否可用,典型的 Linux 文件系统是否是事务性的,或者这些文件系统的事务性 API 是否甚至暴露给用户级程序员,所以我恐怕无法真正帮助他。
【解决方案3】:

我一直使用的 Mark Byer 解决方案略有不同:

  1. 将新内容写入与要替换的文件位于同一目录中的临时文件。
  2. 如果写入成功,将其重命名[1]为所需文件。

重命名是原子的,因此如果成功,则系统不会处于未定义状态。如果它失败了,那么,系统仍然处于原始状态并且你有一个备份你想要在系统重新启动时替换它的内容(在临时文件中)。

我的典型命名约定如下:

/path/to/original/content.data

/path/to/original/.#content.data

如果系统在该过程中的某个地方关闭,则在重新启动您的应用程序时,您可以扫描 .#content.data 并将其呈现给用户,就像他们在系统关闭时输入的内容一样,或者使用一些自定义魔术来决定是否“完成”来决定是否在 content.data 上重命名它。

我不知道你在写什么类型的数据,所以我无法帮助你确定那个“魔法”是什么,但是如果它是一个 xml 文件,例如,你可以解析它的正确性,如果你不要得到意外的文件结尾,那么你可能有一个完整的文件。

FWIW,将自己限制为 8k 写入不会拯救您,因为您现在依赖于底层操作系统和 FS 中的实现细节,这些细节可能会在下一个版本中发生变化,或者在之前的版本中会有所不同。

链接:

  1. http://83.139.107.116:8080/1.1/handlers/monodoc.ashx?link=M%3aMono.Unix.Native.Stdlib.rename(System.String%2cSystem.String)

【讨论】:

  • 8k 是因为我不需要大文件。它只是废纸条。我提到它是因为我在想如果 mysql db 这么小的话,它可能适合它。 Woudlnt 将临时文件重命名为存在的文件是错误/异常吗?我从未听说过重命名和替换文件
  • 不正确的Cannot create a file when that file already exists.
  • 对不起,我应该提到你需要使用 Mono.Posix 来重命名文件。
猜你喜欢
  • 2014-06-29
  • 1970-01-01
  • 2013-06-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-12-28
  • 1970-01-01
相关资源
最近更新 更多