延迟将数据写入磁盘的背后原理是什么?


72

在Linux中,命令的完成执行(例如cpdd不代表已将数据写入设备)。例如,必须调用sync或调用驱动器上的“安全删除”或“弹出”功能。

这种方法背后的哲学是什么?为什么不立即写入数据?是否存在由于I / O错误而导致写入失败的危险?


16
请记住,读写系统调用一次只能使用一个字节,但是磁盘驱动器只能读取或写入固定大小的块。在没有缓冲的情况下,I / O时字节的开销是无法忍受的。有了缓冲,这是可以忍受的。
乔纳森·勒夫勒

Answers:


47

这种方法背后的哲学是什么?

效率(更好地利用磁盘特性)和性能(允许应用程序在写入后立即继续运行)。

为什么不立即写入数据?

主要优点是OS可以自由地重新排序和合并连续的写操作,以提高其带宽使用率(更少的操作和更少的查找)。当请求少量的大型操作时,硬盘的性能会更好,而应用程序往往需要大量的小型操作。另一个明显的优化是,OS还可以在短时间内多次写入同一块数据时删除除最后一次写入以外的所有内容,如果同时删除了受影响的文件,则OS甚至可以全部删除部分写入内容。

这些异步写入是write系统调用返回完成的。这是第二个也是大多数用户可见的优势。异步写入可以提高应用程序的速度,因为它们可以自由地继续工作,而不必等待数据实际在磁盘上。对于读取操作也实现了相同类型的缓冲/缓存,其中最近或经常读取的块保留在内存中,而不是再次从磁盘读取。

是否存在由于IO错误而导致写入失败的危险?

不必要。这取决于使用的文件系统和适当的冗余。如果可以将数据保存在其他位置,则I / O错误可能是无害的。像ZFS这样的现代文件系统会自我修复坏的磁盘块。另请注意,I / O错误不会使现代OS崩溃。如果它们在数据访问期间发生,则仅将它们报告给受影响的应用程序。如果它们在结构化元数据访问期间发生并且使文件系统处于危险之中,则可能会将其重新安装为只读或无法访问。

如果发生操作系统崩溃,断电或硬件故障,还存在轻微的数据丢失风险。这就是为什么必须100%确保数据在磁盘上的应用程序(例如数据库/金融应用程序)的效率较低,但同步写入更为安全的原因。为了减轻性能影响,许多应用程序仍使用异步写入,但最终在用户明确保存文件(例如vim,文字处理器)时将其同步。

另一方面,绝大多数用户和应用程序都不需要,也不关心同步写入提供的安全性。如果发生崩溃或停电,唯一的风险通常是在最坏的最后30秒丢失数据。除非涉及财务交易或类似的事情,这意味着花费的时间远远超过其30秒,否则异步写入的性能(不是错觉,而是非常真实的)巨大的收益将大大超过风险。

最后,同步写入不足以保护仍然写入的数据。如果您的应用程序确实需要确保无论发生什么情况都不会丢失其数据,则需要在多个磁盘和多个地理位置上进行数据复制,以抵御火灾,洪水等灾难。


除了成本外,还要考虑是否已完成某些依赖于已保存数据的操作。如果我正在打字,顺序保存,断电意味着我会损失30秒的工作时间,那么不管那30秒的值是多少,至少我会恢复到打字过程中实际发生的状态,我可以从那里重新开始。另一方面,如果我单击“保存”,然后从桌上的纸张待办事项清单中划掉一些东西,那么当我恢复时,我的硬盘和纸张之间就会出现不一致的情况。通常很难从……恢复
Steve Jessop

1
...因此,作为普通用户,我可能想在待办事项清单上划掉“完成我的小说的写作”之前同步文件系统,以确保我认为自己没有做过真正失败的事情。这就是为什么数据库等需要同步写入的原因:即使它们丢失数据,它们也绝对必须保持一致性。
史蒂夫·杰索普

1
@SteveJessop我同意您的示例,但是我不希望临时用户手动进行同步。如果保存文档时用来编写这本珍贵小说的编辑器没有调用fsync或类似工具,则这是一个需要修复的错误,例如bugs.launchpad.net/ubuntu/+source/libreoffice/+bug/817326。我会用vi(vim)来写我的,vim默认在保存时调用fsync。
jlliagre

59

它只是给那些实际上不必等到写入完成的程序带来了速度上的错觉。在同步模式下挂载文件系统(这使您可以立即进行写操作),并查看所有操作的速度。

有时文件只是暂时存在的...程序会做一些工作,并在工作完成后立即删除文件。如果您延迟了这些写操作,那么您可能一开始就从来没有写过它们。

是否存在由于IO错误而导致写入失败的危险?

哦,绝对。在这种情况下,通常整个文件系统都进入只读模式,一切都变得很糟糕。但这很少发生,总的来说不会失去性能优势。


某些HDD控制器具有备用电池,因此在电源故障的情况下,未提交的数据将保留在控制器上,直到恢复电源为止。这允许在无法丢失数据的数据库应用程序中使用。
strattonn

Linux存储尚未写入RAM而不是HDD的数据。硬盘也有自己的缓存。
Barafu Albino 2015年

如果在进程关闭时同步由进程打开的任何文件,将非常方便。这不会影响流程本身,但是可以简化外壳脚本等(现在必须同步整个文件系统)
MSalters 2015年

14
这不只是一种幻想。异步写入确实可以提高应用程序的整体性能。
jlliagre

4
@frostschutz:除了只是暂时存在的文件之外,还有一个事实是文件的某些区域会被一遍又一遍地重写。
Matthieu M.

26

在Linux甚至Unix之前,就已经使用了异步的,缓冲的I / O。Unix拥有它,因此它的所有分支也是如此。

这是Ritchie和Thompson在其CACM论文UNIX Time-Sharing System中写的

对用户而言,文件的读取和写入似乎是同步的且没有缓冲。也就是说,从读取调用返回后,数据立即可用,反之,写入后,用户的工作空间可能会被重用。实际上,系统维护了相当复杂的缓冲机制,该机制大大减少了访问文件所需的I / O操作数量。


在您的问题中,您还写道:

是否存在由于IO错误而导致写入失败的危险?

是的,写入可能会失败,并且程序可能永远都不知道。尽管这从来都不是一件好事,但是在I / O错误导致系统崩溃的情况下,可以将这种影响降到最低(在某些操作系统上,这是可配置的-不必惊慌,系统可以继续运行,但受影响的文件系统却可以未安装或已安装为只读)。然后可以通知用户该文件系统上的数据是可疑的。并且可以主动监视磁盘驱动器,以查看其增长的缺陷列表是否正在迅速增加,这表明该驱动器发生了故障。

BSD添加了fsync系统调用,因此程序可以确定其文件数据在继续之前已完全写入磁盘,并且随后的Unix系统提供了进行同步写入的选项。GNU dd有一个选项conv=fsync可以确保在命令退出之前所有数据都已被写出。当写入速度较慢的可移动闪存驱动器时,它非常方便,在该驱动器中,缓冲的数据可能需要花费几分钟才能被写入。

文件损坏的另一个原因是系统突然关闭,例如由于断电。几乎所有当前系统都在其文件系统中支持清除/脏标志。当没有更多数据要写出并且文件系统将要卸装时,通常在系统关闭或通过手动调用时,将标志设置为cleanumountfsck如果系统检测到文件系统未完全关闭,通常将在重新引导时运行。


假设我们将音乐从HDD复制到外部驱动器。外部驱动器可能已损坏,并且写入将失败。这不会导致程序使用错误的数据运行。而且,对外部设备上出现故障的IO感到恐慌似乎是一种过大的杀伤力。
marmistrz

好点子。我将修改答案。
Mark Plotnick

15

许多好的答案,但让我补充一点……请记住,Unix是一个多进程和多用户的系统,因此潜在的许多用户将试图在(几乎)同时。如果使用旧的慢速硬盘(可能通过网络安装),这不仅会花费时间(程序基本上会锁定并且用户必须等待),而且还会导致移动硬盘的读/写头很多。磁盘来回。

因此,等待写入的文件会在内存中保留一段时间,然后按照它们应该在磁盘上结束的位置进行排序 ……以及缓冲区已满时-或磁盘同步守护程序已等待所需的秒数(我认为通常是30秒左右)-将整个缓冲区“按顺序”写出到磁盘上,而写头只需要做一次连续的扫动动作,就可以将文件写到磁盘上它走了……而不是到处跳。

与当今的快速磁盘(更不用说固态设备)相比,获得的收益要少得多……尤其是在家用linux系统上,在该系统中,一次只有一个用户在工作,并且只有几个程序。

无论如何,通过将更多的读入(读入高速缓存/缓冲区)中的预期读取组合在一起,然后对等待写入的数据进行排序,以便可以“一动不动”地写入数据,这实际上是一个很好的主意。时间,特别是在许多用户进行大量读写的系统上。


2
XFS甚至没有决定在写出数据之前将数据放在何处。延迟分配为分配器提供了更多的信息以使其决策依据。首次写入文件时,无法知道它是4k文件还是1G且仍在增长的文件。如果某处有10G的连续可用空间,则将4k文件放在它的开头是没有用的。将大文件放在大可用空间的开头可减少碎片。
彼得·科德斯

13

它不是特定于Linux的,它被称为页面缓存(Linux相当不错)。另请参见http://linuxatemyram.com/ ; 因此,如果写入了文件,然后在几秒钟后再次读取,则通常不需要磁盘I / O。

主要优点是在许多系统上有很多RAM,并且其中一些可以被内核用作缓存。因此,某些文件操作可以从此缓存中获利。而且,磁盘I / O时间要比RAM慢很多(对于SDD,通常为数千倍,对于机械硬盘,则为近一百万倍)。

应用程序代码可以提示有关此缓存的信息:请参见例如posix_fadvise(2)madvise(2)


8

旋转盘片比RAM慢。我们使用读/写缓存来“隐藏”这一事实。

关于写IO的有用之处在于它不需要立即发生磁盘IO-与读取不同,在读取完成之前,您无法将数据返回给用户,直到读取完成。

因此,写入操作在软时间限制下进行-只要我们的持续吞吐量不超过磁盘的吞吐量,我们就可以在写入缓存中隐藏很多性能损失。

而且我们确实需要写缓存-旋转磁盘相对而言非常慢。但是,这样做的话,现代RAID类型会对操作产生重大影响。

例如,为了完成一个写IO,一个RAID 6必须:

  • 读取更新块
  • 读取奇偶校验1
  • 读取奇偶校验2
  • 写新块
  • 写奇偶校验1
  • 写奇偶校验2

因此,每次写入实际上是6个 IO操作-尤其是当您有慢速磁盘(如大型SATA驱动器)时,这将变得非常昂贵。

但是有一个很好的简单解决方案-编写合并。如果您可以在缓冲区中构建“全条带”写入,则无需从磁盘读取奇偶校验-您可以根据内存中的内容进行计算。

这样做是非常可取的,因为这样您就不再需要写放大了。的确,您最终可以获得比RAID 1 + 0低的写代价。

考虑:

RAID 6,8 + 2-10主轴

连续写入8个数据块-计算缓存中的奇偶校验,并将一个块写入每个磁盘。每8次写入10次,表示写入损失为1.25。RAID 1 + 0的10个磁盘仍然具有2的写入惩罚(因为您必须写入每个子镜像)。因此,在这种情况下,实际上可以使RAID 6的性能优于RAID1 + 0。在实际使用中,您会获得更多的混合IO配置文件。

因此,写入缓存对RAID集的可感知性能产生了巨大的影响-您可以以RAM速度进行写入,并且写入损失较低-如果这样做,可以提高持续吞吐量。

如果不这样做,则会遭受SATA严重的慢速性能,但是将其乘以6并在其中添加一些竞争。没有写缓存的10路SATA RAID-6会比没有RAID的单个驱动器快一点……但幅度不是很大。

但是,您确实要冒风险-如您所述-断电意味着数据丢失。您可以通过缓存刷新周期,电池支持缓存或使用SSD或其他非易失性缓存来减轻这种情况。


7

其他答案都没有提到延迟分配。XFS,ext4,BTRFS和ZFS都使用它。从ext4出现之前开始,XFS就一直在使用它,因此我将使用它作为示例:

XFS甚至没有决定在写出数据之前将数据放在何处。 延迟分配为分配器提供了更多的信息以使其决策依据。首次写入文件时,无法知道它是4k文件还是1G且仍在增长的文件。如果某处有10G的连续可用空间,则将4k文件放在它的开头是没有用的。将大文件放在大可用空间的开头可减少碎片。


4

此处的所有其他答案至少都对正常情况至少是正确的,我建议在我的情况下先阅读其中的任何一个,但是您提到dd和dd具有典型的用例,可能不涉及写缓存。写缓存主要在文件系统级别实现。原始设备通常不进行写缓存(多个设备驱动程序,例如raid或lvm是又一个蜡球)。由于dd通常与原始块设备一起使用,因此它提供bs和相关选项以允许大写操作以在原始设备上获得更好的性能。当两个端点都是常规文件时,此功能就没有用(尽管在这种情况下,较大的写入操作使用的系统调用较少)。另一个常见的地方是mtools包,它是用户空间胖文件系统的实现。将mtools与软盘驱动器一起使用总是感觉非常缓慢,因为这些工具完全同步并且软盘驱动器非常慢。挂载软盘并使用内核胖文件系统的响应性要强得多,除了umount是同步的(这对于防止数据丢失(特别是对于软盘之类的可移动设备而言)非常重要)。我知道只有少数几个程序经常与原始设备一起使用,例如特殊配置的数据库(它们实现自己的写缓存),tar,特殊设备和文件系统工具(如chdsk,mkfs和mt)。挂载软盘并使用内核胖文件系统的响应性要强得多,除了umount是同步的(这对于防止数据丢失(特别是对于软盘之类的可移动设备而言)非常重要)。我知道只有少数几个程序经常与原始设备一起使用,例如特殊配置的数据库(它们实现自己的写缓存),tar,特殊设备和文件系统工具(如chdsk,mkfs和mt)。挂载软盘并使用内核胖文件系统的响应性要强得多,除了umount是同步的(这对于防止数据丢失(特别是对于软盘之类的可移动设备而言)非常重要)。我知道只有少数几个程序经常与原始设备一起使用,例如特殊配置的数据库(它们实现自己的写缓存),tar,特殊设备和文件系统工具(如chdsk,mkfs和mt)。


4
Linux块设备默认情况下读/写页面缓存。O_DIRECT如果要绕过缓存,则必须使用。 dd oflag=direct。IIRC,某些unices默认在块设备上直接I / O。(并且需要读/写对齐的块,Linux并不需要,因为它只是在写页面缓存。)
Peter Cordes

3

该原理默认情况下是不安全的。

可能有两种合理且显而易见的策略:立即刷新写入磁盘或延迟写入。UNIX历史上选择了后者。因此,为了安全起见,您需要fsync稍后致电。

但是,您可以通过安装带有option的设备来预先指定安全性sync,或者使用来打开每个文件来指定安全性O_SYNC

请记住,UNIX是为计算机专家设计的。不考虑“默认情况下安全”。安全意味着I / O速度较慢,而那些早期系统的I / O速度确实很慢,因此价格很高。不幸的是,即使这是一个不小的变化,UNIX和Linux都没有切换到默认安全设置。


6
绝大多数应用程序和用户不需要或不在意同步写入将提供的安全性。如果发生崩溃或断电,则可能会丢失最后30秒的数据。除非涉及财务交易或类似的事情花费了我们30秒钟以上的时间,否则对大多数人来说这很好。默认为同步I / O将意味着所有以可用性为目标的应用程序都已定义O_NOSYNC。
jlliagre

2

它以少量的可靠性换取了吞吐量的极大提高。

例如,假设一个视频压缩程序。延迟写入(“回写”):

  1. 花10ms压缩帧
  2. 向磁盘发出写帧
  3. 等待10毫秒等待磁盘确认写入完成
  4. 转到1

  1. 花10ms压缩帧
  2. 向磁盘发出写帧(在后台完成)
  3. 转到1

第二个版本显示速度快两倍,因为它可以同时使用CPU和磁盘,而第一个版本总是在等待一个或另一个。

通常,您需要对流操作和批量文件操作进行回写,而对数据库和类似数据库的应用程序则需要进行透写。


1

在许多应用程序中,存储设备将间歇性地忙于读取数据。如果系统始终能够将写操作推迟到存储设备不忙于读取数据的时间,那么从应用程序的角度来看,写操作将花费零时间来完成。唯一不会瞬时写入的情况是:

  1. 写缓冲区已满,直到实际完成写操作为止,再也不能接受任何延迟写入请求。

  2. 有必要关闭或删除正在等待写操作的设备。

  3. 应用程序专门要求确认写入实际上已完成。

确实,仅由于上述要求,才需要真正进行写入。另一方面,通常没有理由在设备空闲时不执行任何挂起的写操作,因此许多系统随后都执行了写操作。


0

还有这个:

写“ Hi,Joe Moe”

写“ Hi,”
写“ Joe”
写“ Moe” 快。

并且:

写“你好,你好吗?”
快于:
写“嗨,怎么了?”
删除该行,
写“你好,你好吗?”
删除并
写上“嗨,你好吗?”

在RAM中进行修改和聚合比在磁盘上进行修改和聚合更好。批处理磁盘写入可将应用程序开发人员从这些问题中解放出来。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.