我有很多数据要以二进制形式保存到磁盘,并且希望尽可能接近ACID属性。由于我有大量数据并且无法将其全部保存在内存中,因此我了解我有两种基本方法:
所以我的问题是:
如果我选择使用大文件选项并将其作为内存映射文件打开(或使用Stream.Position
和Stream.Write
),并且断电,是否可以保证该文件可能会发生什么?
是否有可能丢失整个大文件,或者只是以中间的数据损坏而告终?
NTFS是否确保始终完全写入一定大小(4k?)的块?
在Unix / ext4上结果是否更好/更差?
我想避免使用NTFS TxF,因为微软已经提到它打算退休。我正在使用C#,但是语言可能并不重要。
(附加说明)
似乎应该有一定的保证,因为-除非我错了-如果在写入文件时有可能丢失整个文件(或遭受非常奇怪的损坏),那么现有的数据库都不是ACID ,除非他们1)使用TxF或2)在写入之前制作整个文件的副本?如果丢失部分您甚至不打算接触的文件,我认为日记不会为您提供帮助。
答案 0 :(得分:1)
NTFS文件系统(和ext3-4)使用事务日志来操作更改。每次更改都会存储在日记中,然后日记本身就可以有效地执行更改。 除了灾难性磁盘故障外,文件系统还被设计为在自己的数据结构中保持一致,而不是在您自己的数据结构中保持一致:万一发生崩溃,恢复过程将决定回滚哪些内容以保留该文件系统。一致性。在回滚的情况下,“尚未写入但要写入”的数据将丢失。 文件系统将保持一致,而您的数据将保持一致。
此外,还涉及其他几个因素:软件和硬件缓存引入了一个附加层,因此导致了故障点。通常,这些操作是在缓存中执行的,然后缓存本身会在磁盘上刷新。文件系统驱动程序不会看到在缓存中“执行”的操作,但是我们会看到刷新操作。 这样做是出于性能方面的考虑,因为硬盘是瓶颈。硬件控制器确实具有电池,以确保即使在断电的情况下也可以刷新其自身的缓存。
扇区的大小是另一个重要因素,但是由于硬盘驱动器本身可能出于互操作性目的而位于其本地大小之内,因此不应考虑此细节。
如果已映射内存,并且在电源中断的情况下在中间插入数据,则文件的内容可能会部分包含您所做的更改(如果更改超过内部缓冲区的大小)。
TxF是缓解此问题的一种方法,但是有一些影响,限制了可以使用它的上下文:对于eaxample,它不适用于其他驱动器或共享网络。
为了成为ACID,您需要设计数据结构和/或使用它的方式,以便不依赖于实现细节。例如,Mercurial(版本工具)总是将其自己的数据附加到其自己的修订日志中。 可能的模式很多,但是,您需要的担保越多,您所获得的技术就越多(并与之相关)。
答案 1 :(得分:1)
您可以调用FlushViewOfFile
来启动脏页写操作,然后调用according to this article来FlushFileBuffers
保证页面已写完。
每次写入后调用FlushFileBuffers
可能更“安全”,但不建议这样做。您必须知道可以容忍多少损失。有一些模式可以限制这种潜在的损失,即使是最好的数据库也可能遭受写故障。您只需要重新恢复生命,并尽可能减少损失,这通常需要使用多阶段提交进行一些记录。
我想可以用FILE_FLAG_NO_BUFFERING
和FILE_FLAG_WRITE_THROUGH
打开内存映射文件,但这会浪费您的吞吐量。我不这样做我打开了用于异步I / O的内存映射文件,让操作系统通过其自己的异步I / O完成端口实现来优化吞吐量。这是最快的吞吐量。我可以忍受潜在的损失,并已适当缓解。我的内存映射数据是文件备份数据...如果检测到丢失,一旦清除了硬件错误,我就可以检测并重新备份丢失的数据。
显然,文件系统必须足够可靠才能运行数据库应用程序,但是我不知道有任何供应商建议您仍然不需要备份。 会发生坏事。计划损失。我要做的一件事是,我从不写入数据中间。我的数据是不可变的且具有版本控制,每个“数据”文件的大小限制为2gb,但是每个应用程序采用的策略都不相同。