是否与O_DIRECT ACID兼容的write()?

时间:2012-03-14 01:51:48

标签: linux database-design

我的数据库引擎通过发出整个磁盘块的write()系统调用来写入64字节的记录。使用O_DIRECT模式打开设备。例如,块中的第三条记录从字节128开始,到第192位结束,当我执行UPDATE时,写入整个磁盘块(默认为512字节)。

我的问题是,如果我每次更新UPDATE时都记录自己的记录,我是否可以保持ACID合规性?通常,数据库引擎通过将修改后的磁盘块写入另一个(空闲)位置,然后在第一次写入返回成功后立即使用一个(原子)写入将索引更新为新块,从而分两步完成此操作。但我不这样做,我用新的覆盖当前数据,期望写入成功。我的方法有任何潜在的问题吗?它符合ACID吗?如果硬件只写了一半的块而我的记录正好在中间呢?或者硬件是否已经执行了我描述的两步写入过程,但是在块级别,所以我不需要在软件中重复相同的操作?

(注意:没有记录比物理磁盘块大(默认为512字节),fsync在每次write()之后都会出现,这仅适用于Linux)

3 个答案:

答案 0 :(得分:1)

没有

您无法假设磁盘写入成功。并且您不能假设磁盘将保留现有数据。 Here is some QNX documentation also stating this.

如果真的非常不幸,磁盘电源在写入时会失败,从而导致块中的校验和和半写数据损坏。

这就是ACID系统至少使用两份数据的原因。

答案 1 :(得分:0)

ACID预计失败,并提出处理它们的方法。两阶段提交和三阶段提交是两种相当常见且易于理解的方法。

虽然我是一个数据库人,但dbms让我不必再考虑这类事情了。但是我会说在不采取任何其他预防措施的情况下覆盖记录可能会导致“C”和“D”属性失效(“一致”和“持久”)。

要构建非常好的代码,假设您的dbms服务器没有电池备份缓存,只有一个电源,并且在事务期间,该电源出现灾难性故障。如果您的dbms可以非常干净地应对这种类型的故障,我认为您可以将其称为ACID兼容。

稍后。 。 。

我读过Tweedie的成绩单。他不是在谈论数据库直接磁盘访问;他在谈论一个日志文件系统。日记文件系统也执行两阶段提交。

听起来您正试图通过单阶段提交达到ACID合规性(在数据库意义上)。我认为你不能逃脱这一点。

Opening with O_DIRECT表示“尝试以最小化I / O与此文件之间的缓存效果”(强调添加)。我想你也需要O_SYNC。 (但是链接的内核文档警告说,大多数Linux文件系统都没有实现O_SYNC的POSIX语义。而且已知文件系统和磁盘都是关于写入是否触及盘片的。)

内核文档中还有两个注意事项。首先,“建议应用程序将O_DIRECT用作默认禁用的性能选项。”你不是那样做的。您正在尝试使用它来实现ACID合规性。

第二,

  

“O_DIRECT一直让我感到不安的是那个   整个界面只是愚蠢的,可能是由一个   一些严重的精神控制物质上的疯狂猴子。“ - Linus

SQLite有一篇关于它们如何处理原子提交的可读文章。 Atomic Commit in SQLite

答案 2 :(得分:0)

具有x.equals(y)的ACID的y.equals(x)是否兼容?

,通常情况下无法保证。以下是一些耐用性的反例:

  • write()不保证已确认的数据来自设备的易失性缓存
  • O_DIRECT不保证文件系统元数据的持久性,这可能是实际读回(确认的)写数据所必需的(例如,在追加写操作的情况下)

我的问题是,如果每次发生UPDATE时都在其自身上写记录,我可以确定是否符合ACID标准吗?

一般情况下,没有例如,符合规范的SCSI磁盘不必保证在写入过程中发生崩溃时仅获取旧数据或仅获取新数据的语义(合法的做法是,返回错误读取该数据直到该区域无条件地读取是合法的覆盖)。如果要对文件系统中的文件进行写操作,则情况会更加复杂。在发出新I / O之前,在O_DIRECT之后成功O_DIRECT可以帮助您知道写入是稳定的,但在一般情况下,不足以确保原子性(仅旧数据或新数据)尴尬的定时断电。

我的方法(假设覆盖完全是原子的)是否有[潜在的]问题?

是的,请参见上文。在某些设置中,您正在做的事情可能会按您希望的那样工作,但不能保证它应该全部工作(即使按照其规范它们是“无故障的”)。

有关进一步的讨论,请参见此answer on "What does O_DIRECT really mean?"