在Linux上持久耐用需要什么?

Max*_*Max 9 linux posix mmap fsync durability

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

我看的每个地方都是矛盾的信息,所以我很欣赏任何见解.

我写入磁盘有三种方法.

  • 使用O_DIRECT | O_DSYNC,pread'ing然后pwrite'ing 512字节 - 16 MB块.

  • 使用O_DIRECT,pread'ing然后pwrite'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甚至可以保证数据的写入吗?这不是说,但我不知道从那时起事情是否发生了变化.

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

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

为了给混音添加混乱,我在EC2上运行它,我不知道这是否会影响任何东西.虽然它很难测试,因为我无法控制它被关闭的积极程度.

Ano*_*non 6

(2018 年,距离第一次提出这个问题已经很多年了)

\n
\n

怎样才能在 Linux 上持久运行?

\n
\n

通过阅读您的问题,我发现您和磁盘之间有一个文件系统。所以问题就变成了:

\n
\n

使用 Linux 文件系统需要什么才能持久?

\n
\n

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

\n
\n
preallocate_file(tmp);fsync(tmp);fsync(dir);rename(tmp, normal);fsync(normal);fsync(dir);\n
Run Code Online (Sandbox Code Playgroud)\n
\n

(无耻地窃取了Andres Freund(Postgres 开发人员)在 LWN 上留下的评论)并且您必须在继续查看它是否成功之前检查每个调用的返回代码,如果任何返回代码返回非零,则假设出现问题。如果您正在使用mmapthen 则msync(MS_SYNC)相当于fsync.

\n

Dan Luu 的“文件很困难”(其中有一个关于各种文件系统的覆盖原子性的很好的表格)、LWN 文章“确保数据到达磁盘”和Ted Ts\'o\'中提到了与上述类似的模式s “不要害怕 fsync!” 。

\n
\n

对于所有这些[ O_DIRECT| O_DSYNC, O_DIRECT+ fdatasync, mmap+ msync],数据是否可能丢失(写入或同步返回后)或因电源故障、恐慌、崩溃或其他原因而损坏?

\n
\n

是的,您可能会出现未被注意到的损坏,因为由于文件增长超过其当前界限而“分配写入”可能会导致元数据操作,并且您没有检查元数据持久性(仅数据持久性)。

\n
\n

如果我的服务器在 pwrite 中间,或者在 pwrite 开始和 fdatasync 结束之间,或者在更改映射内存和 msync 之间死掉,我将混合使用旧数据和新数据,[等等]

\n
\n

由于在中断覆盖的情况下数据的状态是未定义的,因此 它可能是任何东西......

\n
\n

我希望我的个人 pwrite 调用是原子的和有序的。是这样吗?

\n
\n

fsync\之间可能会发生重新排序(例如,如果O_DIRECT默默地回退到缓冲)。

\n
\n

如果它们跨多个文件,情况如何?

\n
\n

你的麻烦就更大了。为了解决这个问题,您需要编写自己的日志,并且可能需要使用文件重命名。

\n
\n

如果我用 O_DIRECT | 写 O_DSYNC 到 A,然后 O_DIRECT | O_DSYNC 至 B,

\n
\n

不。

\n
\n

fsync 是否能保证数据已写入?

\n
\n

是的,有必要(如果还不够的话)确定上述内容(使用现代 Linux 和真实的磁盘堆栈,假设没有错误)。

\n
\n

ext4 的日志记录是否完​​全解决了损坏块的问题

\n
\n

不。

\n

(还有很多问题)

\n

是的,Linux 软件堆栈可能有错误(2019 年:请参阅下面的附录),或者硬件可能有错误(或无法备份),但这并不能阻止上述内容成为您所能做到的最好的如果 POSIX 文件系统上的一切都达到了预期效果,那么就应该这样做。如果您知道您有一个具有特定文件系统(或没有文件系统)和特定硬件设置的特定操作系统,那么您确实可以减少对上述某些内容的需求,但通常您不应该跳过任何步骤。

\n

额外的答案:O_DIRECT单独使用不能保证与文件系统一起使用时的持久性(最初的问题是“你如何知道元数据已被持久化?”)。有关这一点的讨论,请参阅Ext4 wiki 中的“澄清 Direct IO 的语义” 。

\n

附录(2019 年 3 月)

\n

即使使用当前(在编写 5.0 时)Linux 内核,fsync也并不总是能看到错误通知,4.16 之前的内核甚至更糟。PostgreSQL 人员发现错误通知可能会丢失,并且未写入的页面被标记为干净,从而导致fsync即使存在异步写回数据的(吞没的)错误,也可能会返回成功(大多数 Linux 文件系统不能可靠地保留脏数据)一旦发生故障,那么反复“重试”失败fsync并不一定表明您可能期望什么)。请参阅PostgreSQL Fsync 错误 wiki 页面、 LWN PostgreSQL 的 fsync() 惊喜文章以及 FOSDEM 2019 中的演讲How is it possible that PostgreSQL 使用 fsync 错误了 20 年,以及我们将对此采取什么措施以了解详细信息。

\n

所以片尾字幕的结论很复杂:

\n
    \n
  • 这种fsync舞蹈是必要的(即使它并不总是足够的),以至少覆盖无错误的 I/O 堆栈情况
  • \n
  • 如果您通过直接 I/O 执行(写入)I/O,则当写入出错时您将能够获得准确的错误
  • \n
  • 早期(早于 4.16)的内核在通过以下方式获取错误时存在错误:fsync
  • \n
\n

另请参阅:

\n\n


Bri*_*ain 3

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

绝对地。

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

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

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

这是真的。但您可能仍然可以使用 NMI 或sysrq-trigger创建一个相当突然的停止。