我正在编写一些软件来处理非常关键的数据,并且需要知道我需要做些什么来实现持久性。
我看到的任何地方都是矛盾的信息,所以我很欣赏任何见解。
我写入磁盘的方式有三种。
使用O_DIRECT | O_DSYNC,pread'ing然后pwrite'ing 512字节--16 MB块。
使用O_DIRECT,pread'ing然后编写512字节块,并根据需要定期调用fdatasync。
使用内存映射文件,我会根据需要定期调用msync(...,MS_SYNC | MS_INVALIDATE)。
这都是在ext4上使用默认标志。
对于所有这些,数据是否可能丢失(在写入或同步返回之后)或由于电源故障,恐慌,崩溃或其他任何原因而损坏?
如果我的服务器在pwrite中间,或者在pwrite的开头和fdatasync的结尾之间,或者在被更改的映射内存和msync之间死掉,我可能会混合旧数据和新数据,或者是它是一个还是另一个?我希望我的个人pwrite调用是原子的并且是有序的。是这样的吗?如果它们跨多个文件就是这种情况吗?所以,如果我用O_DIRECT |写O_DSYNC到A,然后是O_DIRECT | O_DSYNC到B,我保证,无论发生什么,如果数据在B中,它也在A?
fsync甚至可以保证数据的写入吗? This没有说,但我不知道从那时起事情是否发生了变化。
ext4的日志记录是否完全解决了this SO answer所说的腐败块的问题?
我目前通过调用posix_fallocate然后ftruncate来增长文件。这些都是必要的,它们是否足够?我认为ftruncate实际上会初始化已分配的块以避免these issues。
为了给混音添加混乱,我在EC2上运行它,我不知道这是否会影响任何东西。虽然它很难测试,因为我无法控制它被关闭的程度。
答案 0 :(得分:3)
对于所有这些,数据是否可能丢失(在写入或同步返回之后)或由于电源故障,恐慌,崩溃或其他任何原因而损坏?
绝对
fsync甚至可以保证数据的写入吗?这不是说,但我不知道从那时起事情是否发生了变化。
没有。答案取决于设备,可能依赖于文件系统。不幸的是,该文件系统可以是“实际”存储设备上方的层和层。 (例如md
,lvm
,fuse
,loop
,ib_srp
等。
虽然它很难测试,因为我无法控制它被关闭的程度。
这是真的。但你可能仍然可以使用NMI或sysrq-trigger
来创建一个非常突然的停止。
答案 1 :(得分:2)
(2018年,这个问题被问了很多年之后)
要在Linux上保持持久性需要什么?
通过阅读您的问题,我看到您和磁盘之间有一个文件系统。所以问题变成了:
使用Linux文件系统具有持久性需要什么?
在一般文件系统和未指定硬件的情况下,您能做的最好的事情就是“ fsync dance ”,它像这样:
preallocate_file(tmp);fsync(tmp);fsync(dir);rename(tmp, normal);fsync(normal);fsync(dir);
(从the comment Andres Freund (Postgres Developer) left on LWN被无耻地偷走了),并且您必须先检查每个呼叫的返回代码,然后再继续查看是否成功,并假定如果返回的代码返回非零,则出了问题。 。如果您使用的是mmap
,那么msync(MS_SYNC)
相当于fsync
。
Dan Luu's "Files are hard"和LWN article "Ensuring data reaches disk"和Ted Ts'o's "Don’t fear the fsync!"(上面有一个关于各种文件系统的覆盖原子性的漂亮表)中提到了与上述类似的模式。
对于所有这些[
O_DIRECT
|O_DSYNC
,O_DIRECT
+fdatasync
,mmap
+msync
],数据是否有可能丢失(在返回写入或同步之后)或损坏是因为电源故障,死机,崩溃或其他原因?
是的,由于"allocating writes" due to growing the file past its current bounds can cause metadata operations,您可能没有注意到损坏,并且您没有检查元数据的持久性(仅数据持久性)。
如果我的服务器在pwrite中间,或者在pwrite的开始到fdatasync的结束之间,或者在更改的映射内存和msync之间死了,我将混合使用新旧数据,等等。
>
由于在覆盖中断的情况下数据的状态不确定,因此可以是任何东西 ...
我希望我的单个pwrite调用是原子的且有序的。是这样吗?
fsync
之间可能会发生重新排序(例如,如果O_DIRECT
默默地退回到缓冲状态)。
如果它们跨越多个文件,那么
您遇到的麻烦更大。为了解决这个问题,您将需要编写自己的日志,并可能使用文件重命名。
如果我用O_DIRECT编写| O_DSYNC到A,然后是O_DIRECT | O_DSYNC到B,
否。
fsync甚至可以保证数据已写入吗?
是必须确定(如果不够的话)(对于现代Linux和假设没有错误的真实磁盘堆栈)。
ext4的日志记录是否完全解决了损坏的块的问题
否。
(ETOOMANYQUESTIONS)
是的,Linux软件堆栈可能有故障(2019年:请参见下面的附录),或者硬件可能有故障(或存在无法备份的方式),但这并不能阻止以上所述成为您的最佳选择如果一切都在POSIX文件系统上完成,就可以做到。如果您知道具有带有特定文件系统(或没有文件系统)和特定硬件设置的特定操作系统,那么确实可以减少对某些上述内容的需求,但总的来说,您不应跳过任何步骤。 / p>
红利答案:O_DIRECT
本身不能保证与文件系统一起使用时的持久性(最初的问题是“您如何知道元数据已被持久化?”)。有关这一点的讨论,请参见"Clarifying Direct IO's Semantics" in the Ext4 wiki。
即使使用当前的(在编写5.0时)Linux内核fsync
也不总是看到错误通知,4.16之前的内核甚至更糟。 PostgreSQL的人们发现错误的通知可能会丢失,未写的页面会标记为干净,这会导致fsync
返回成功的情况,即使发生(吞咽)错误以异步方式写回数据(大多数Linux文件系统都不会这样做)一旦发生故障,就可以可靠地保存脏数据,因此反复“重试”失败的fsync
不一定表示您可能期望的内容。有关详细信息,请参见PostgreSQL Fsync Errors wiki page和LWN PostgreSQL's fsync() surprise文章以及FOSDEM 2019的演讲How is it possible that PostgreSQL used fsync incorrectly for 20 years, and what we'll do about it。
因此,帖子的结论很复杂:
fsync
舞蹈是必要的(即使并不总是足够的)fsync
来获取错误时,较早的内核(早于4.16)是错误的。