大多数应用程序不直接处理磁盘,而是将数据存储在文件系统的文件中,这保护了我们免受那些无赖磁盘的侵害。毕竟,文件系统的关键任务是确保文件系统在计划外的系统崩溃(例如,电源故障)后始终可以恢复到一致状态。虽然一个好的文件系统能够制服磁盘,但所需的工作量可能很大,性能降低也令人恼火。本文探讨了磁盘所采取的捷径以及文件系统必须经历的曲折才能获得所需的可靠性。
虽然文件系统必须恢复到一致状态,但该状态通常反映了文件系统在崩溃前一段时间所处的状态。通常,在崩溃前一分钟写入的数据可能会丢失。造成这种丢失的原因是文件系统尚未有机会将该数据写入磁盘。当应用程序需要确保数据在崩溃后可以恢复时,它会执行fsync系统调用,针对包含需要长期稳定性的数据的文件。在从fsync系统调用返回之前,文件系统必须确保与文件关联的所有数据在崩溃后都可以恢复,即使崩溃发生在fsync系统调用返回后立即发生。
文件系统实现fsync通过查找所有脏(未写入)的文件数据并将其写入磁盘。历史上,文件系统会向磁盘发出写入请求以写入脏文件数据,然后等待写入完成通知到达。这种技术一直可靠地工作,直到磁盘控制器中出现磁道缓存。磁道缓存控制器在控制器中有一个大的缓冲区,用于累积正在写入磁盘的数据。为了避免在写入连续磁盘块时,为了拾取下一个块的开头而损失几乎整个旋转周期,控制器会在数据位于磁道缓存中而不是在磁盘上时发出写入完成通知。提前发出写入完成通知是为了希望系统及时为磁盘上的下一个块发出写入请求,以便控制器能够紧跟前一个块的末尾立即写入它。
这种方法有一个严重的负面副作用。当写入完成通知传递时,文件系统期望数据位于稳定存储上。如果数据仅在磁道缓存中但尚未在磁盘上,则文件系统可能无法兑现使用fsync系统调用向用户应用程序承诺的完整性。特别是,如果在写入完成通知之后但在数据写入磁盘之前电源发生故障,则语义将被违反。一些供应商通过使用非易失性存储器作为磁道缓存并提供电源故障后的微码重启来确定哪些操作需要完成来消除这个问题。由于这种选择很昂贵,因此很少有控制器提供此功能。
较新的磁盘使用一种称为标记队列的技术来解决这个问题,其中传递给磁盘驱动程序的每个请求都被分配一个唯一的数字标记。大多数支持标记队列的磁盘控制器将接受至少 16 个待处理的 I/O 请求。在每个请求完成后(可能与呈现给磁盘的顺序不同),已完成请求的标记将作为写入完成通知的一部分返回。如果将几个连续的块呈现给磁盘控制器,则它可以开始处理下一个块,同时返回前一个块的标记的通知。因此,标记队列允许应用程序在数据到达稳定存储时得到准确通知,而不会在写入连续块时产生丢失磁盘旋转周期的惩罚。fsync是通过将文件的所有修改过的块发送到磁盘,然后等待直到所有这些块的标记都被确认为已写入来实现的。
标记队列最初在 SCSI 磁盘中实现,使其既具有可靠性又具有速度。ATA 磁盘缺乏标记队列,可以启用其写入缓存(默认)以提供速度,但以崩溃后的可靠性为代价,或者禁用写入缓存,这提供了崩溃后的可靠性,但写入速度降低了 50%。
为了摆脱这种困境,ATA 规范添加了一种尝试标记队列的方法,其名称与 SCSI 规范使用的名称相同:TCQ(标记命令队列)。不幸的是,与 SCSI 规范不同的是,ATA 的 TCQ 允许标记请求的完成取决于是否启用了写入缓存(在缓存命中时发出写入完成通知)或禁用(在介质命中时发出写入完成通知)。因此,它增加了复杂性,但没有任何好处。
幸运的是,SATA(串行 ATA)有一个新的定义称为 NCQ(本机命令队列),它在写入命令中有一个位,告诉驱动器它应该在介质写入时还是在缓存命中时报告完成。如果驱动程序正确设置了此位,则磁盘将显示正确的行为。
在现实世界中,许多面向桌面市场的驱动器未实现 NCQ 规范。为了确保可靠性,系统必须禁用磁盘上的写入缓存,或者在每次元数据更新、日志更新(对于日志文件系统)或fsync系统调用后发出缓存刷新请求。这两种技术都会导致明显的性能下降,因此它们通常被禁用,如果电源发生故障,则文件系统会面临风险。对于速度和可靠性都很重要的系统,不应使用 ATA 磁盘。相反,它们应该使用支持 NCQ 的光纤通道、SCSI 或 SATA 驱动器。
旋转介质的另一个最新趋势是磁盘上扇区大小的变化。从 1950 年代首次面世到大约 2010 年,磁盘上的扇区大小一直为 512 字节。在 2010 年,磁盘制造商开始生产 4,096 字节扇区的磁盘。
随着磁盘的写入密度多年来不断提高,每比特的错误率也在上升,需要使用更长的纠错码。错误并非均匀分布在整个磁盘上。相反,一个小的缺陷会导致一串位的丢失。大多数扇区的错误很少,但一个小缺陷可能会导致单个扇区经历许多需要纠正的位。因此,即使大多数扇区不需要,错误代码也必须为每个扇区提供足够的冗余以处理高纠正率。使用更大的扇区可以分摊额外纠错位的成本,使其分布在更长的位运行中。使用大八倍的扇区还可以消除 88% 的扇区起始和停止标头,进一步减少磁盘上非数据位的数量。从 512 字节扇区到 4,096 字节扇区的净效果是,在给定的磁盘技术上可以存储的用户数据量几乎翻了一番。
当对磁盘执行 I/O 时,所有传输请求都必须是扇区大小的倍数。在 2010 年之前,对磁盘的最小读取或写入为 512 字节。现在,对磁盘的最小读取或写入为 4,096 字节。
为了与旧应用程序兼容,具有 4,096 字节扇区的新磁盘上的磁盘控制器模拟旧的 512 字节扇区磁盘。当完成 512 字节的写入时,控制器会读取包含要写入区域的 4,096 字节扇区到缓冲区中,覆盖扇区内要替换的 512 字节,然后将更新后的 4,096 字节缓冲区写回磁盘。在这种模式下运行时,由于需要读取和写入,磁盘速度至少降低 50%。通常,速度会变得慢得多,因为控制器必须等待磁盘盘片几乎完整旋转一圈才能重写刚刚读取的扇区。
文件系统需要意识到底层介质的变化,并确保它们通过始终以较大扇区大小的倍数写入来适应。历史上,文件系统被组织为将小于 512 字节的文件存储在单个扇区中。随着磁盘技术的变化,大多数文件系统通过使 4,096 字节成为最小分配大小来避免 512 字节写入的减速。因此,小于 512 字节的文件现在被放置在 4,096 字节的块中。此更改的结果是,存储主要由小文件组成的文件系统最多需要八倍的空间。由于多年来平均文件大小一直在增长,因此对于典型的文件系统,将 4,096 字节作为最小分配大小的转换导致所需存储空间增加了 10% 到 15%。
一些文件系统已通过将多个小文件放置在单个 4,096 字节扇区中来适应扇区大小的变化。为了避免执行读-修改-写操作来更新小文件,文件系统收集一组最近更改的小文件,并将它们一起写入新的 4,096 字节扇区。当扇区内的大多数小文件已在其他地方重写后,通过获取扇区内剩余的几个小文件并将它们与新写入的其他小文件包含在新扇区中来回收该扇区。然后,现在为空的扇区可用于将来的分配。
结论是,文件系统需要了解其运行所在的磁盘技术,以确保它们可以可靠地交付它们承诺的语义。用户需要了解不同磁盘技术对文件系统的约束,并选择一种不会导致他们将要使用的文件系统工作负载性能不佳的技术。也许展望未来,他们应该避开那些撒谎的磁盘,转而使用闪存技术——除非,当然,闪存存储开始使用相同的降低成本的技巧。
喜欢它,讨厌它?请告诉我们
Marshall Kirk McKusick 博士撰写书籍和文章,教授有关 Unix 和 BSD 相关主题的课程,并就软件专利、商业秘密和版权问题提供专家证词,特别是那些与操作系统和文件系统相关的问题。自 1994 年 FreeBSD 项目成立以来,他一直是该项目的开发人员和提交者。在加州大学伯克利分校期间,他实现了 4.2BSD 快速文件系统,并且是伯克利 CSRG(计算机系统研究组)的研究计算机科学家,负责监督 4.3BSD 和 4.4BSD 的开发和发布。
© 2012 1542-7730/12/0900 $10.00
最初发表于 Queue vol. 10, no. 9—
在 数字图书馆 中评论这篇文章
Pat Helland - 关注您的状态,为了您的心态
应用程序在进入分布式和可扩展的世界后,经历了有趣的演变。同样,存储及其近亲数据库也与应用程序并肩发展。很多时候,存储和应用程序的语义、性能和故障模型都在微妙地跳舞,因为它们在不断变化以支持不断变化的业务需求和环境挑战。在组合中添加规模确实激起了波澜。本文着眼于其中的一些问题及其对系统的影响。
Alex Petrov - 现代存储系统背后的算法
本文仔细研究了现代数据库中大多数使用的两种存储系统设计方法(读取优化的 B 树和写入优化的 LSM(日志结构合并)树),并描述了它们的用例和权衡。
Mihir Nanavati, Malte Schwarzkopf, Jake Wires, Andrew Warfield - 非易失性存储
对于大多数从业计算机科学家的整个职业生涯而言,一个基本观察结果始终成立:CPU 的性能显着高于 I/O 设备,价格也更昂贵。CPU 可以极高的速率处理数据,同时为多个 I/O 设备提供服务,这一事实对各种规模系统的硬件和软件设计产生了深远的影响,几乎从我们开始构建它们以来就是如此。
Thanumalayan Sankaranarayana Pillai, Vijay Chidambaram, Ramnatthan Alagappan, Samer Al-Kiswany, Andrea C. Arpaci-Dusseau, Remzi H. Arpaci-Dusseau - 崩溃一致性
数据的读取和写入是任何冯·诺依曼计算机最基本的方面之一,但出乎意料地微妙且充满细微差别。例如,考虑在具有多个处理器的系统中访问共享内存。虽然一种称为强一致性的简单直观方法最容易让程序员理解,但许多较弱的模型已被广泛使用(例如,x86 总存储顺序);这些方法提高了系统性能,但代价是使对系统行为的推理更加复杂且容易出错。