在研究云系统提供的持久性时,想确保自己了解基本知识。首先阅读NVMe规范,以了解disks提供的保证(https://www.evanjones.ca/durability-nvme.html)。简单来说,你应该假设,在发出写入到刷新或强制数据单元访问写入完成之间,数据已损坏。
大多数程序使用系统调用来写入数据。本文着眼于linux文件API提供的保证。看起来这应该很简单:程序调用write()并且完成后,数据是持久的。然而,write()仅将数据从应用程序复制到内存中的内核缓存中。为了强制数据持久,您需要使用一些其他机制。
本文知识的凌乱集。(简单来说使用fdatasync或使用O_DSYNC打开)。更好更清晰的概述,请参见LWN的文章(https://lwn.net/Articles/457667/)。
在IEEE POSIX标准中,将write系统调用定义为尝试将数据写入文件描述符。成功返回后,即使是由其他进程或线程读取或写入的,也需要进行读取以返回已写入的字。
常规文件操作的线程交互:“如果两个线程各自调用这些函数之一,则每个调用应看到另一个调用的所有指定效果,或者都不看到。” 这表明所有文件I / O必须有效地持有一个锁。
这是否意味着写是原子的?从技术上讲,是的:将来的读取必须返回写入的全部内容,或者不返回任何内容。但是,写入并不一定要完成,只允许传输部分数据。例如,有两个线程,每个线程将1024个字节附加到一个文件描述符中。两次写入到每次只写入一个字节是可以接受的。这仍然是“原子的”,但也会导致不希望的交错输出。有一个很棒的StackOverflow答案,有更多细节。https://stackoverflow.com/questions/42442387/is-write-safe-to-be-called-from-multiple-threads-simultaneously/42442926#42442926
在磁盘上获取数据的最直接方法是调用fsync()。它要求操作系统将缓存中所有修改的块以及所有文件元数据(例如访问时间,修改时间等)传输到磁盘。元数据很少有用,因此除非你知道需要元数据,否则应使用fdatasync。该fdatasync是flush尽可能多的元数据作为必要“的后续数据读取要正确处理”,这才是多数应用关心的事,。
一个问题是不能保证可以再次找到该文件。特别是,第一次创建文件时,需要在包含该文件的目录上调用fsync,否则在失败后该文件可能不存在。原因基本上是在UNIX中,由于硬链接,一个文件可以存在于多个目录中,因此,当你在文件上调用fsync时,无法确定应该写出哪个目录(https://www.quora.com/When-should-you-fsync-the-containing-directory-in-addition-to-the-file-itself)。ext4实际上可能会自动同步目录,但是对于其他文件系统可能并非如此。
实施方式会因文件系统而异。使用blktrace来检查ext4和xfs使用了哪些磁盘操作。他们都对文件数据和文件系统日志发出正常的磁盘写操作,使用高速缓存刷新,然后对日志进行FUA(Force Unit Access)写操作,这可能表示操作已提交。在不支持FUA的磁盘上,这涉及两次缓存刷新。实验表明,fdatasync比fsync快一点,而blktrace显示fdatasync倾向于写入更少的数据(ext4:fsync为20 kiB,fdatasync为16 kiB)。实验还表明,xfs的速度比ext4稍快,并且blktrace再次表明它倾向于清除较少的数据(xfs:fdatasync为4 kiB)。
系统要求耐久性。另一种选择是在open()系统调用中使用O_SYNC或O_DSYNC选项。这将导致每个写入的语义与写入后分别带有fsync / fdatasync的语义相同。POSIX规范将此称为“同步I / O文件完整性完成”和“数据完整性完成”。这种方法的主要优点是,你只需要单个系统调用,而不是先写入后跟fdatasync。最大的缺点是使用该文件描述符的所有写入都将被同步,这可能会限制应用程序代码的结构。
open()系统调用具有O_DIRECT选项,该选项旨在绕过操作系统的缓存,而直接对磁盘进行I / O。这意味着在许多情况下,应用程序的写调用将直接转换为磁盘命令。但是,通常这不能替代fsync或fdatasync,因为磁盘本身可以自由延迟或缓存那些写入。更糟糕的是,在某些情况下,意味着O_DIRECT I / O会退回到传统的缓冲I / O上。最简单的解决方案是也使用O_DSYNC选项打开,这意味着在每次写入后都将有效地跟随fdatasync。
Linux还具有sync_file_range,它可以允许将文件的一部分刷新到磁盘而不是整个文件,并触发异步刷新,而不是等待它。但是,手册页指出它“极度危险”,因此不鼓励使用它。用sync_file_range最好地描述了某些差异和危险,这是Yoshinori Matsunobu的有关其工作原理的文章。
http://yoshinorimatsunobu.blogspot.com/2014/03/how-syncfilerange-really-works.html
结论是,持久性I / O基本上有三种方法。所有这些都要求在首次创建文件时在包含目录上调
用fsync()。
它们许多差异很小。
更多阅读:https://www.evanjones.ca/durability-filesystem.html