(2018 年,这个问题首次提出多年后)
在 Linux 上保持持久性需要什么?
通过阅读您的问题,我看到您和磁盘之间有一个文件系统。于是问题就变成了:
使用 Linux 文件系统的持久性需要什么?
您能做的最好的事情(在一般文件系统和未指定的硬件情况下)是“ fsync dance ”,它是这样的:
preallocate_file(tmp);fsync(tmp);fsync(dir);rename(tmp, normal);fsync(normal);fsync(dir);
(从LWN 上留下的评论 Andres Freund(Postgres 开发人员)中无耻地窃取),您必须在继续之前检查每个调用的返回码,看看它是否成功,如果任何返回码返回非零,则假设出现问题。如果您正在使用mmap
thenmsync(MS_SYNC)
相当于fsync
.
Dan Luu的“Files are hard”(其中有一个关于覆盖各种文件系统的原子性的好表)、LWN 文章“确保数据到达磁盘”和Ted Ts'o 的“不要害怕同步!” .
对于所有这些 [ O_DIRECT
| O_DSYNC
, O_DIRECT
+ fdatasync
, mmap
+ msync
],数据是否可能丢失(在写入或同步返回后)或因电源故障、恐慌、崩溃或其他任何原因而损坏?
是的,您可能没有注意到损坏,因为由于文件增长超过其当前范围而导致的“分配写入”可能会导致元数据操作,并且您没有检查元数据的持久性(仅数据持久性)。
如果我的服务器在 pwrite 中间死机,或者在 pwrite 开始和 fdatasync 结束之间,或者在被更改的映射内存和 msync 之间,我将混合新旧数据,[等]
由于在中断覆盖的情况下数据的状态是未定义的, 它可能是任何东西......
我希望我的个人 pwrite 调用是原子的和有序的。是这样吗?
之间fsync
的重新排序可能会发生(例如,如果O_DIRECT
默默地退回到缓冲)。
如果它们跨越多个文件?
你的麻烦就更大了。要涵盖这一点,您需要编写自己的日志并可能使用文件重命名。
如果我用 O_DIRECT | 写 O_DSYNC 到 A,然后是 O_DIRECT | O_DSYNC 到 B,
不。
fsync 是否甚至可以保证数据已写入?
是有必要(如果不充分)确定上述内容(使用现代 Linux 和假设没有错误的真实磁盘堆栈)。
ext4的journalling是否彻底解决了损坏块的问题
不。
(ETOOMANYQUESTIONS)
是的,Linux 软件堆栈可能有问题(2019 年:请参阅下面的附录)或硬件可能有问题(或以无法备份的方式存在),但这并不能阻止上述内容是您可以做的最好的事情,如果在 POSIX 文件系统上,一切都符合其讨价还价的要求。如果您知道您有一个具有特定文件系统(或没有文件系统)和特定硬件设置的特定操作系统,那么您确实可以减少对上述某些内容的需求,但通常您不应跳过任何步骤。
奖励答案:O_DIRECT
单独使用文件系统时不能保证持久性(最初的问题是“你怎么知道元数据已经被持久化了?”)。有关这一点的讨论,请参阅Ext4 wiki 中的“Clarifying Direct IO's Semantics” 。
附录(2019 年 3 月)
即使使用当前(在编写 5.0 时)Linux 内核fsync
也并不总是能看到错误通知,而且 4.16 之前的内核甚至更糟。PostgreSQL 人员发现错误通知可能会丢失,并且未写入的页面标记为干净导致fsync
返回成功的情况,即使在异步写回数据时存在(吞咽的)错误(大多数 Linux 文件系统不能可靠地保留脏数据)一旦失败如此反复地“重试”失败fsync
并不一定表明您可能期望什么)。请参阅PostgreSQL Fsync 错误 wiki 页面LWN PostgreSQL的fsync() 惊喜文章和谈话PostgreSQL 怎么可能错误地使用 fsync 了 20 年,我们将从 FOSDEM 2019 开始详细说明。
所以学分后的结论很复杂:
- 舞蹈是必要的
fsync
(即使它并不总是足够的)至少涵盖非错误的 I/O 堆栈案例
- 如果您通过直接 I/O 进行(写入)I/O,那么当写入出错时,您将能够得到准确的错误
- 早期(早于 4.16)内核在通过以下方式获取错误时存在错误
fsync
另见: