9

我正在编写一些软件来处理非常关键的数据,并且需要知道我需要做什么才能实现持久性。

我到处都是矛盾的信息,所以我很感激任何见解。

我有三种方式写入磁盘。

  • 使用 O_DIRECT | O_DSYNC,先读后写 512 字节 - 16 MB 块。

  • 使用 O_DIRECT,先读取然后写入 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 是否甚至可以保证数据已写入?说没有,但我不知道从那以后情况是否发生了变化。

ext4 的日志记录是否完​​全解决了这个 SO 答案所说的存在损坏块的问题?

我目前正在通过调用 posix_fallocate 然后 ftruncate 来增长文件。这两者都是必要的吗,它们是否足够?我认为 ftruncate 实际上会初始化分配的块以避免这些问题

为了增加混乱,我在 EC2 上运行它,我不知道这是否会影响任何东西。尽管它使测试变得非常困难,因为我无法控制它被关闭的积极程度。

4

2 回答 2

4

(2018 年,这个问题首次提出多年后)

在 Linux 上保持持久性需要什么?

通过阅读您的问题,我看到您和磁盘之间有一个文件系统。于是问题就变成了:

使用 Linux 文件系统的持久性需要什么?

您能做的最好的事情(在一般文件系统和未指定的硬件情况下)是“ fsync dance ”,它是这样的:

preallocate_file(tmp);fsync(tmp);fsync(dir);rename(tmp, normal);fsync(normal);fsync(dir);

(从LWN 上留下的评论 Andres Freund(Postgres 开发人员)中无耻地窃取),您必须在继续之前检查每个调用的返回码,看看它是否成功,如果任何返回码返回非零,则假设出现问题。如果您正在使用mmapthenmsync(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

另见:

于 2018-09-05T05:21:33.477 回答
3

对于所有这些,数据是否可能丢失(在写入或同步返回后)或因电源故障、恐慌、崩溃或其他任何原因而损坏?

绝对地。

fsync 是否甚至可以保证数据已写入?这说没有,但我不知道从那以后情况是否发生了变化。

不。答案取决于设备,并且可能取决于文件系统。不幸的是,该文件系统可能是“实际”存储设备之上的一层又一层。(例如md, lvm, fuse, loop,ib_srp等)。

尽管它使测试变得非常困难,因为我无法控制它被关闭的积极程度。

确实如此。但是您可能仍然可以使用 NMI 或sysrq-trigger创建一个非常突然的停止。

于 2012-10-24T02:03:59.293 回答