磁盘扇区是否写入原子?

时间:2010-01-05 21:00:33

标签: database-design crash acid

澄清问题:

当操作系统发送命令将扇区写入磁盘时,它是原子的吗?即,如果电源在写入命令之后立即失败,则完全写入新数据或者保持旧数据完整。我不关心多扇区写入会发生什么 - 撕裂的页面是可以接受的。

旧问题:

假设您在磁盘上有旧数据X,您在其上写入新数据Y,并且在写入期间树落在电源线上。没有花哨的UPS或备用电池的磁盘控制器,你最终可能会出现页面撕裂的情况,磁盘上的数据是X部分和Y部分。你能否最终得到磁盘上的数据是X部分,Y部分的情况和部分垃圾?

我一直在努力理解像数据库这样的ACID系统的设计,而且我天真的想法似乎是firebird,它不使用预写日志,它依赖于给定的写入不会破坏旧数据( X) - 只能完全写入新数据(Y)。这意味着如果要覆盖X的一部分,则只能更改被覆盖的X部分,而不是我们打算保留的X部分。

为了澄清,这意味着如果你有一个页面大小的缓冲区,比如4096字节,填充了一半Y,我们要保留的一半X - 我们告诉操作系统将该缓冲区写在X上,没有短路情况严重的磁盘故障,我们想要保留的半X在写入期间被破坏。

9 个答案:

答案 0 :(得分:19)

我认为撕裂的页面不是问题。据我所知,所有驱动器都有足够的电量存储,以便在断电时完成当前扇区的写入。

问题是每个人都撒谎。

至少当数据库知道事务何时被提交到磁盘时,每个人都会撒谎。数据库发出fsync,操作系统仅在所有未完成的写入已提交到磁盘时返回,对吧?也许不吧。特别是对于RAID卡和/或SATA驱动器,通常会告诉您的程序已经提交了所有内容(即fsync返回),但驱动器上还没有数据。

您可以尝试使用Brad's diskchecker来确定您要用于数据库的平台是否能够在不丢失数据的情况下继续使用插件。底线:如果diskchecker失败,则该平台对于运行数据库是不安全的。具有ACID的数据库依赖于知道何时将事务提交到后备存储以及何时尚未提交。无论数据库是否使用预写登录,都是如此(如果数据库在没有完成fsync的情况下返回给用户,则在发生故障时可能会丢失事务,因此它不应声称它提供了ACID语义)。

有一个long thread on the Postgresql邮件列表讨论持久性。它开始谈论SSD,但随后它进入了SATA驱动器,SCSI驱动器和文件系统。您可能会惊讶地发现您的数据可能会丢失。对于拥有需要持久性的数据库的人来说,这是一个很好的线程,而不仅仅是那些运行Postgresql的人。

答案 1 :(得分:15)

似乎没有人同意这个问题。所以我花了很多时间尝试不同的Google查询,直到我终于找到答案。

来自Stephen Tweedie博士,RedHat员工和Linux内核文件系统以及虚拟内存开发人员在关于ext3(他开发的)transcript here的演讲中。如果有人知道,那就是他。

“将这些东西写入期刊是不够的,因为期刊中必须有一些标记说:好吧,(实际上有这个期刊记录)这个期刊记录实际上代表了磁盘的完全一致性?你这样做的方法是通过一些原子操作来标记交易在磁盘上完成“[23m,14s]

“现在,磁盘这些天实际上已经做出了这些保证。如果你开始对磁盘进行写操作,那么即使电源在该扇区写入中间失败,磁盘也有足够的可用功率,它实际上可以窃取来自主轴旋转能量的功率;它有足够的功率来完成现在正在写入的扇区的写入。在所有情况下,磁盘都可以保证。“ [23m,41s]

答案 2 :(得分:9)

不,他们不是。更糟糕的是,在默认设置下,磁盘可能会说谎,并且说数据实际上是在磁盘缓存中写入的。出于性能原因,这可能是合乎需要的(实际耐久性要慢一个数量级),但这意味着如果你断电并且没有物理写入磁盘缓存,那么你的数据就会消失。

遗憾的是,真正的持久性是 hard ,因为每次写入需要至少进行一次完整旋转,或者使用journalling / undo进行2次以上。这限制了您每秒几百个数据库事务,并且需要在相当低的级别禁用写入缓存。

但是出于实际目的,在大多数情况下,差异并不是

答案 3 :(得分:8)

如果断电,人们似乎不同意扇区写入期间发生的事情。也许是因为它取决于所使用的硬件,甚至是文件系统。

来自维基百科(http://en.wikipedia.org/wiki/Journaling_file_system):

  

某些磁盘驱动器可保证写入   电源故障期间的原子性。   然而,其他人可能会停止写作   在电力供应之后的一个部门中途   迷路了,让它与之不相称   它的纠错码。该部门   因此腐败,其内容丢失。   物理期刊防范这种情况   腐败因为它拥有完整的   该部门的副本,它可以   重播下一次腐败   安装。

似乎暗示某些硬盘驱动器无法完成该扇区的编写,但是日志文件系统可以像xlog保护数据库一样保护您免受数据丢失。

从linux内核邮件列表中讨论ext3日记文件系统:

  

无论如何坏扇区校验和   硬件错误。假设扇区写入   要是原子的,它要么发生,要么发生   不

我倾向于相信维基评论。实际上,没有xlog的数据库(firebird)的存在意味着扇区写入是原子的,它不能破坏你不想改变的数据。

关于扇区写入的原子性,有很多关于Here的讨论,并且没有达成一致意见。但是那些不同意的人似乎在谈论多扇区写入(在许多现代硬盘驱动器上并不是原子的。)那些说扇区写入是原子的人似乎更多地了解他们所谈论的内容。 / p>

答案 4 :(得分:7)

很少写入磁盘设备规格来保证在突然断电的情况下任何/每个扇区的写操作都是原子的(但请参阅下面的NVMe规范讨论)。但是,似乎默认地认为非古老的“真实”磁盘会悄悄地提供这种行为(例如,2017年演示文稿中的Linux内核开发人员Christoph Hellwig mentions this off-hand)。

对于合成磁盘(例如,网络连接的块设备,某些类型的RAID等),情况不太清楚,它们在根据其给定规范合法运行时可能会或可能不会提供扇区原子性保证。想象一下一个RAID 1阵列(无日志),该磁盘由一个提供512字节大小的扇区的磁盘组成,而另一个磁盘提供了4KiB大小的扇区,从而迫使RAID暴露4KiB的扇区大小。作为一项思想实验,您可以构建一个方案,其中每个磁盘都提供扇区原子性(相对于其自身的扇区大小),而RAID设备却没有断电。这是因为这将取决于512字节的扇区磁盘是否是RAID读取的磁盘,以及8 512字节的扇区中有多少损坏了它在掉电之前写入的4KiB RAID扇区。

有时规范提供原子性保证,但仅在某些写入命令上提供。 SCSI磁盘规范就是一个例子,可选的WRITE ATOMIC (16)命令甚至可以提供超出扇区的保证,但是可选的,它很少实现(因此很少使用)。更普遍实现的COMPARE AND WRITE也是原子性的(也可能是多扇区的),但是对于SCSI设备而言,它也是可选的,它具有与普通写入不同的语义...

奇怪的是,至少有NVMe spec was written in such a way to guarantee sector atomicity的一个版本(1.4),这要归功于Linux内核开发人员Matthew Wilcox。对此规格提出投诉的设备可以做广告,它们提供了不只是扇区写入原子性甚至是(如果设备选择的话)多扇区原子性达到指定限制的坚定保证。

(以下内容以Linux为中心,但是许多概念适用于未在严格控制的硬件环境中部署的通用OS)

追溯到2013年,BtrFS的首席开发人员Chris Mason谈到了(现在已经停产的)Fusion-io如何创建实现原子操作的存储产品(Chris当时在Fusion-io工作)。 Fusion-io还创建了专有文件系统“ DirectFS”(由Chris编写)以公开此功能。 MariaDB开发人员实施了一种模式,该模式可以通过不再进行双重缓冲来利用这种行为,从而导致“每秒增加43%的事务,并使存储设备的消耗减少一半”。克里斯提出了一个补丁,以便通用文件系统(例如BtrFS)可以宣传它们通过new flag O_ATOMIC提供了原子性保证,但还需要更改块层。 block layer changes were also proposed by Chris in a later patch series that added a function blk_queue_set_atomic_write()说。但是,这两个补丁程序系列都没有进入主线Linux内核,并且(在当前版本的2020年)主线5.7 Linux内核中没有<{1}标志。

在进一步介绍之前,值得注意的是,即使较低的级别不提供原子性保证,只要知道写入的时间,较高的级别仍可以为其用户提供原子性(尽管会增加性能开销)。达到稳定的存储。 如果 O_ATOMIC可以告诉您何时将数据写入稳定的存储空间(技术上不能由POSIX保证,但是在现代Linux上是这样),则因为POSIX重命名是原子的,所以可以使用create new file / fsync / rename dance可以执行原子文件更新,从而允许应用程序自己进行双缓冲/预先写入。堆栈中另一个较低的示例是BtrFS和ZFS等写入时复制文件系统。这些文件系统由于其语义而在崩溃之后以大于扇区的大小为用户空间程序提供了“所有旧数据”或“所有新数据”的保证,即使许多磁盘不提供原子写入也是如此。您可以将此想法完全推入NAND based SSDs don't overwrite the area currently used by an existing LBA and instead write the data to a new region and keep a mapping of where the LBAs data now所在的磁盘本身。

恢复我们缩短的时间表,2015年,HP研究人员撰写了一篇论文Failure-Atomic Updates of Application Data in a Linux File System (PDF)media),内容涉及在AdvFS的Linux端口中引入新功能(AdvFS最初是DEC Tru64的一部分):

如果使用新的fsync()标志打开文件,则其应用程序数据的状态将始终反映最近成功执行的msync,fsync或fdatasync。 AdvFS还包括一个新的O_ATOMIC操作,该操作将对多个文件的更新组合到一个故障原子包中[...]

2017年,克里斯托夫·海尔维格(Christoph Hellwig)撰写了experimental patches to XFS to provide O_ATOMIC。在"Failure-Atomic file updates for Linux" talkslides)中,他解释了他如何从2015年论文中汲取灵感(但没有多文件支持),并且补丁集扩展了已经存在的XFS reflink工作。但是,尽管有initial mailing list post,但在撰写本文时(2020年中),此补丁集不在主线内核中。

在2019 Linux Plumbers Conference的数据库跟踪期间,MySQL开发人员Dimitri Kravtchuk asked if there were plans to support O_ATOMIC(链接进入电影讨论的开始)。观众提到上面的XFS工作,英特尔声称他们可以在Optane上实现原子性,但是Linux没有提供暴露它的接口,即Google claims to provide 16k atomicity on GCE storage 1 。另一个关键点是数据库开发人员需要大于4KiB的原子性来避免必须进行两次写入-MySQL需要16KiByte,而PostgreSQL需要8KiByte,显然Oracle数据库需要64KiB。此外,Richard Hipp博士(SQLite数据库的作者)询问是否存在请求原子性的标准接口,因为今天SQLite makes use of the F2FS filesystem's ability to do atomic updates via custom ioctl()s但这与一个文件系统有关。克里斯·梅森(Chris Mason)答复说,暂时没有标准,也没有提供syncv接口。

TLDR;如果您从应用程序一直严格控制物理磁盘上的整个堆栈(以便您可以控制和限定整个批次),则可以安排使用硬件原子性所需的组件。如果您不在那种情况下,或者您在谈论一般情况,那么您就不能依赖扇区写入是原子的。

当操作系统发送命令将扇区写入磁盘时,它是原子的吗?

撰写本文时(2020年中):

  • 使用主线内核时
  • 如果要处理真实磁盘

内核发送的扇区写可能是原子的(假定扇区不大于4KiB)。在受控情况下(电池后备控制器,声称支持原子写入的NVMe磁盘,供应商已向您保证的SCSI磁盘等),只要O_ATOMIC被使用,用户空间程序就可以使用O_DIRECT不会恢复为缓冲状态,I / O不会在块层拆分/合并/您正在发送设备特定的命令并绕过了块层。但是,在一般情况下,内核和用户空间程序都不能假定原子性。

您是否最终会遇到磁盘上的数据是X部分,Y部分和部分垃圾的情况?

从规范角度来看,如果您正在谈论执行常规SCSI WRITE(16)的SCSI磁盘,并且在写入过程中发生电源故障,那么答案是肯定的:一个扇区可以包含X部分,Y部分以及部分垃圾。进行中的写入期间发生崩溃,意味着从正在写入的区域读取的数据不确定,并且磁盘可以自由选择从该区域作为数据返回的内容。这意味着所有旧数据,所有新数据,一些新旧数据,全零,全一,随机数据等都是要为所述扇区返回的“合法”值。来自old draft of the SBC-3 spec

4.9写入失败

如果执行写操作的一个或多个命令在任务集中,并且在断电(例如,导致应用程序客户端导致供应商特定的命令超时)或中等错误或硬件错误发生时(例如,)正在处理,因为错误地卸载了可移动介质),这些命令所写入的逻辑块中的数据是不确定的。当通过执行读取或验证操作的命令访问该设备时(例如,在通电后或在安装可移动介质之后),设备服务器可能会在这些逻辑块中返回旧数据,新数据或特定于供应商的数据。

在读取遇到此类故障的逻辑块之前,应用程序客户端应重新发出所有执行未完成写操作的命令。


1 在2018年Google announced it had tweaked its cloud SQL stack and that this allowed them to use 16k atomic writes MySQL's with innodb_doublewrite=0中通过O_DIRECT ... Google执行的基础自定义被描述为位于虚拟化存储kernel,virtio和ext4文件系统层。此外,标题为Best practices for 16 KB persistent disk and MySQL (archived copy)的不再可用的beta文档描述了最终用户为了安全地使用该功能而必须做的事情。更改包括:使用适当的Google提供的VM,使用专用存储,更改块设备参数以及精心创建具有特定布局的ext4文件系统。但是,在2020年的某个时候,该文档从GCE的在线指南中消失了,表明不支持这种最终用户调整。

答案 5 :(得分:5)

第一个问题的答案取决于所涉及的硬件。至少对于一些较旧的硬件,答案是肯定的 - 电源故障可能导致垃圾被写入磁盘。然而,大多数当前的磁盘都有一些内置于磁盘本身的“UPS” - 一个足够大的电容器足以为磁盘供电足够长的时间,以便将磁盘缓存中的数据写入磁盘盘片。它们还具有检测电源是否仍然良好的电路,因此当电源变得不稳定时,它们会将缓存中的数据写入盘片,并忽略它们可能收到的垃圾。

就“撕裂的页面”而言,典型的磁盘只接受命令一次写入整个扇区,因此您将获得的内容通常是正确写入的整数扇区,而其他扇区保持不变。但是,如果您使用的逻辑页大小大于单个扇区,那么您最终可能会得到一个部分写入的页面。

然而,这主要适用于与普通移动盘式硬盘的直接连接。几乎所有其他事情,规则可以而且往往会有所不同。仅举一个明显的例子,如果您是通过网络编写的,那么您将主要使用正在使用的网络协议。如果您通过TCP传输数据,将拒绝与CRC不匹配的数据,但可能会接受通过UDP传输的具有相同损坏的相同数据。

答案 6 :(得分:2)

我怀疑这个假设是错误的。

现代HDD对扇区中的数据进行编码 - 并使用ECC进行保护。因此,您可以最终使用所有扇区内容 - 这对使用的编码没有意义。

对于越来越多的POP,情况更加可怕 - 在被覆盖之前块被清除,因此,根据所使用的固件和可用空间量,完全不相关的扇区可能会被损坏。

顺便说一下,操作系统崩溃不会导致单个扇区内的数据损坏。

答案 7 :(得分:0)

我希望一个撕裂的页面由X部分,Y部分和部分不可读扇区组成。如果在电源出现故障时磁头正在写入一个扇区,驱动器应立即停放磁头,这样驱动器的其余部分(除了那个扇区)将保持完好无损。

在某些情况下,我会期望几个撕裂的页面由X部分和Y部分组成,但只有一个撕裂的页面包含一个不可读的扇区。几个页面撕裂的原因是驱动器可以在内部缓冲大量写入,并且写入顺序可能会交错各个页面的各个扇区。

我读过一些相互矛盾的故事,关于对不可读部门的新写入是否会使其再次可读。即使答案是肯定的,那也是新数据Z,既不是X也不是Y.

答案 8 :(得分:-1)

更新时 磁盘,驱动器制造商唯一保证的是单个512- 字节写入是原子的(即,它将完整地完成或者不会完整 完全完成);因此,如果发生不合时宜的电力损失,只有一部分 更大的写入可能完成(有时称为撕裂写入)。