为了账号安全,请及时绑定邮箱和手机立即绑定

使用内存映射文件或普通 Stream.Write 时是否有任何持久性保证

使用内存映射文件或普通 Stream.Write 时是否有任何持久性保证

C#
三国纷争 2022-07-23 18:01:55
我有很多数据想以二进制形式保存到磁盘,我想尽可能接近具有 ACID 属性。由于我有大量数据并且无法将其全部保存在内存中,因此我知道我有两种基本方法:有很多小文件(例如每分钟左右写入磁盘) - 如果发生崩溃,我只会丢失最后一个文件。然而,性能会更差。有一个大文件(例如打开、修改、关闭) - 之后的最佳顺序读取性能,但如果发生崩溃,我最终可能会得到一个损坏的文件。所以我的问题是:如果我选择使用大文件选项并将其作为内存映射文件(或使用Stream.Positionand Stream.Write)打开,并且断电,是否可以保证文件可能会发生什么?是否有可能丢失整个大文件,或者最终导致数据在中间损坏?NTFS 是否确保一定大小(4k?)的块总是被完全写入?Unix/ext4 上的结果更好/更差?我想避免使用 NTFS TxF,因为微软已经提到它计划停用它。我正在使用 C#,但语言可能无关紧要。(补充说明)似乎应该有一定的保证,因为 - 除非我错了 - 如果在写入文件时可能会丢失整个文件(或遭受非常奇怪的损坏),那么现有的数据库将不会是 ACID,除非他们1) 使用 TxF 或 2) 在写入之前复制整个文件?如果您丢失了您甚至不打算触摸的部分文件,我认为日记不会帮助您。
查看完整描述

2 回答

?
PIPIONE

TA贡献1829条经验 获得超9个赞

您可以调用FlushViewOfFile,它启动脏页写入,然后FlushFileBuffers根据本文,它保证页面已被写入。

每次写入后调用FlushFileBuffers可能“更安全”,但不建议这样做。你必须知道你能承受多少损失。有一些模式可以限制这种潜在的损失,即使是最好的数据库也可能遭受写入失败。您只需要以尽可能少的损失恢复生活,这通常需要一些多阶段提交的日志记录。

我想可以打开内存映射文件, FILE_FLAG_NO_BUFFERING但这FILE_FLAG_WRITE_THROUGH会消耗你的吞吐量。我不这样做。我为异步 I/O 打开内存映射文件,让操作系统通过它自己的异步 I/O 完成端口实现来优化吞吐量。这是最快的吞吐量。我可以容忍潜在的损失,并已适当减轻。我的内存映射数据是文件备份数据......如果我检测到丢失,一旦硬件错误被清除,我可以检测并重新备份丢失的数据。

显然,文件系统必须足够可靠才能运行数据库应用程序,但我不知道有任何供应商建议您仍然不需要备份。坏事发生。计划损失。我做的一件事是我从不写入数据中间。我的数据是不可变的和版本化的,每个“数据”文件限制为 2gb,但每个应用程序采用不同的策略。


查看完整回答
反对 回复 2022-07-23
?
ITMISS

TA贡献1871条经验 获得超8个赞

NTFS 文件系统(和 ext3-4)使用事务日志来操作更改。每个更改都存储在日志中,然后日志本身用于有效地执行更改。除了灾难性的磁盘故障外,文件系统被设计为在其自己的数据结构中保持一致,而不是您的:在崩溃的情况下,恢复过程将决定回滚什么以保持一致性。在回滚的情况下,您的“尚未写入但要写入”的数据将丢失。文件系统将是一致的,而您的数据则不是。

此外,还涉及其他几个因素:软件和硬件缓存引入了一个额外的层,因此是一个故障点。通常操作是在缓存中执行的,然后缓存本身会刷新到磁盘上。文件系统驱动程序不会看到“在”缓存中执行的操作,但我们会看到刷新操作。这样做是出于性能原因,因为硬盘是瓶颈。硬件控制器确实有电池,以保证即使在断电的情况下也可以刷新自己的缓存。

扇区的大小是另一个重要因素,但不应考虑此细节,因为出于互操作性目的,硬盘驱动器本身可能与其原始大小有关。

如果你有一个 mewmory 映射并且你在中间插入数据,当电源关闭时,如果文件的内容超过了内部缓冲区的大小,则文件的内容可能部分包含你所做的更改。

TxF 是一种缓解问题的方法,但有几个影响限制了您可以使用它的上下文:例如,它不适用于不同的驱动器或共享网络。

为了成为 ACID,你需要设计你的数据结构和/或你使用它的方式,以免依赖于实现细节。例如,Mercurial(版本控制工具)总是将自己的数据附加到自己的修订日志中。有许多可能的模式,但是,您需要的保证越多,您获得的技术就越具体(并且与之相关)。


查看完整回答
反对 回复 2022-07-23
  • 2 回答
  • 0 关注
  • 131 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信