在 1994 年,论文《XFS 文件系统的可扩展性》发表了。自 1984 年以来,计算机的倒退速度变得更快,存储容量也减少了。值得注意的是,在这个期间呈现了更多装备多个 CPU 的计算机,并且存储容量曾经达到了 TB 级别。对于这些设施,仅仅对 4.3BSD 疾速文件系统(或 SGI IRIX 中称为 EFS 的批改版本)进行改良已不再足够。(点击此处
SGI 的基准测试中采纳的计算机领有大型背板和多个控制器(其中一项基准测试采纳了一个具备 20 个 SCSI 控制器的设施),大量的磁盘(上百块硬盘驱动器)以及多个 CPU(12 个 CPU 插槽)和大量内存(最高 1GB)。
SGI 是一家制作高性能计算机(HPC)和图形工作站的企业。在 20 世纪 80 年代和 90 年代,SGI 是计算机图形和可视化畛域的先驱和领导者。在进行基准测试时,SGI 会应用一系列具备特定配置的计算机设备,并进行性能测试和比拟,以评估其零碎的性能和能力。
然而,SGI 在 2009 年申请破产爱护,并在 2016 年以“Silicon Graphics International”为名重组,持续致力于提供高性能计算和数据分析解决方案。SGI 在计算机发展史上留下了重要的脚印,并对计算机图形和可视化畛域产生了深远的影响。
以后所需的文件系统解决能力曾经超出了 FFS(Fast Filling System),文件的大小也超过了 FFS 能够的解决能力,目录中的文件数量增大导致查找时间过长,像调配位图(allocation bitmaps)这样的地方数据结构无奈进行无效的扩大,并且全局锁在多个 CPU 的状况下会造成低效的文件系统并发拜访。于是,SGI 决定设计一个齐全不同的文件系统。
此外,整个 Unix 社区也面临着来自 David Cutler 和 Helen Custer 的挑战,他们开发了 Windows NT 4.0 的开发者。通过 Windows NT 4.0 中的 NTFS,他们展现了从头开始设计零碎的可能性。
新要求
XFS 文件系统充斥了翻新思维,与传统的 Unix 文件系统设计有很大的不同。其中的新个性包含:
-
通过以下形式实现并发性 :
- 调配区域
- Inode 锁拆散
- 大规模并行 I/O 申请、DMA 和零拷贝 I/O 性能
-
通过以下概念,进步拜访的可扩展性 :
- B+ 树:一种均衡的多路搜寻树,能够无效地存储和检索大量的数据;
- extent:一种用来形容间断的磁盘块的数据结构,由(起始块,长度)两个字段组成 2;
- 将“文件写入”和“文件在磁盘上的布局”拆散,以便通过应用提早调配和预调配来实现间断的文件。
extent(区段)示意文件在磁盘上间断的一段数据块。每个 extent 由一个起始地位(start)和一个长度(length)描述符组成,用于指定文件在磁盘上的物理存储地位。通过应用 extent,文件系统能够实现动静增长的 I/O 大小,从而进步吞吐量。extent 的概念还能够与提早调配和预调配相结合,以优化文件的布局,使得文件在磁盘上能够间断存储。这种间断存储能够缩小磁盘寻址的开销,进步文件读写的效率。
-
引入预写日志(write-ahead log)以记录元数据更改 :
- 异步记录日志以实现写入合并
- 利用日志进行复原,使复原工夫与正在解决的数据量成比例,而不是与文件系统的大小成比例。
XFS 是为了满足这些个性而开发的,实现这些个性后就能够在视频编辑、视频服务和科学计算等畛域充分发挥大型 SGI 设施的性能。
一个不应用日志构造的日志文件系统
在约同一期间,John K. Ousterhout 提出了一个问题:“为什么操作系统的速度没有跟上硬件的倒退速度?”Ousterhout 开始在实验性的 Sprite 操作系统中摸索基于日志的文件系统的想法 。
Sprite 是一种晚期的分布式操作系统,最早由 John K. Ousterhout 和 Kenneth L. Dickey)于 1984 年在加州大学伯克利分校开发。Sprite 的设计指标是提供高度牢靠的分布式环境,反对在网络上连贯的多台计算机之间进行合作和通信。它在学术界和钻研畛域具备肯定影响力,为后续分布式操作系统的倒退奠定了根底。
基于日志的文件系统是一个十分激进的想法,咱们在后续的文章中会探讨。只管它们在工夫上比 XFS 早一点,但这个概念的引入具备重要的意义。最后它们并不实用,因为它们须要不同的硬件提供更多的磁盘寻道。日志结构化文件系统的理念必须变得更加精密能力产生理论影响,咱们将在本系列的后续局部中探讨它们。
IRIX 的处境
IRIX 是 Silicon Graphics Inc.(SGI)开发的操作系统,用于其工作站和服务器产品线。它是基于 Unix System V 的变种,并蕴含了许多 SGI 独有的性能和优化,以适应其高性能计算和图形处理需要。IRIX 在 1988 年首次公布,并成为 SGI 工作站的次要操作系统,为许多迷信、工程和创意畛域的利用提供了弱小的计算和图形处理能力。
IRIX 最后应用 EFS(Extent File System)作为其文件系统,它是 BSD FFS 的一个改良版本,应用了 extents 来形容文件的间断磁盘块。它受到 8 GB 文件系统大小限度,2 GB 文件大小限度,以及无奈充分利用硬件 I/O 带宽的影响,这让许多购买了这些低廉机器的客户感到不满。起初,SGI 开发了 XFS 文件系统来取代 EFS,并在 IRIX 5.3 版本中引入了 XFS3。XFS 是一种高性能的 64 位日志文件系统,反对大容量存储、疾速复原和高级治理性能 。
视频播放和数据库社区对文件系统提出了新的需要:须要反对数百 TB 的磁盘空间、数百 MB/s 的 I/O 带宽以及许多并行的 I/O 申请,以便可能充分利用硬件资源,同时确保不会呈现 CPU 资源瓶颈。
“XFS 文件系统的可扩展性”这篇论文次要展现了它的性能,对其实现和设计决策进行了简要探讨,也没有提供详尽的基准测试。
性能个性
大容量文件系统
XFS 反对大容量文件系统。之前的文件系统应用 32 位的指针来指向磁盘块。块的大小是 8 KB,应用 32 位的块指针,文件系统的下限是 32 TB。
当应用 64 位的块指针会导致许多数据结构的大小变成 8 字节的倍数,这样的操作会造成一些些节约。
为了进步并发性(参见下文),XFS 引入了“调配组”(Allocation Groups,简称 AG)的概念,其大小总是小于 4GB。调配组(AG)领有本地实例,这些实例具备文件系统数据结构,例如,inode 映射或闲暇块跟踪。这些本地实例能够独立进行加锁,从而容许在不同的调配组中进行并发操作。
调配组(AG)还有助于减小指针的大小:个别组内编号能够用 32 位指针表白。事实上,一个 4GB 的调配组能够包容最多 1M 个块的块,因为每个块的最小大小为 4K。组内单个最大的 extent 能够用 40 位(5 字节)来示意(地位和大小各占 20 位)。
文件和文件系统最大值为 8 EB(2^63-1)。
带宽和并发
XFS 的设计指标之一就是并发操作。1994 年是 20 MB/s SCSI 控制器的时代,SGI 构建了可能包容多个控制器和多个驱动器的大型机箱。基准测试援用了具备 480 MB/s 总带宽的计算机,其文件 I/O 性能超过 370 MB/s,无需进行任何调整,包含所有开销。这对于过后的日常应用来说是相当令人印象粗浅的。
XFS 通过应用大块(4 KB 或 8 KB 块大小)和 extents 概念来实现这一点。
Extent 和二叉树
在 XFS 中,“extent”是一个外围概念,它通常是一个蕴含两个字段的元组(起始块和长度)。将文件块映射到磁盘块(“bmap”)时,“extent”则蕴含三个元素,即一个三元组(偏移量,长度,起始块)。因为调配组(AG)存在上限值,能够用一系列 4 字节的 extent 来形容间断的多达 2M 个数据块,这比 BSD FFS 之前的办法更为高效。
extent 也使 XFS 可能进行大规模 I/O 申请。源于它们形容了间断的块区域,这样能够轻松创立读取或写入多个块的申请。默认状况下,它应用 64 KB 的内存缓冲区进行 I/O 操作,除非有非凡规定应用更大的内存缓冲区。
XFS 通过条带化(striping)来治理底层磁盘构造,并反对同时解决 2 或 3 个并发的 IO 申请。它会查看反压(backpressure),也就是查看应用程序是否实际上在读取数据。如果是的化,文件系统会收回额定的读取申请,以放弃默认状况下最多 3 个申请同时进行,这样能够一次解决 192KB 的数据。
extent 组被组织成一个线性的列表,但这会导致扩展性问题。因而,XFS 应用 B+ 树来解决多个索引块的状况,如果只有一个索引块时,则进化为线性列表。
B+ 树是一种树状数据结构,用于组织和治理 extent 组。它容许在大规模的 extent 组汇合中高效地进行搜寻、插入和删除操作。B+ 树结构可能无效地解决大量的 extent 组,并且具备较好的扩展性和性能。
通常,元组是依据其第一个值进行索引,但对于某些构造(如闲暇列表),会保留多个索引:通过 startblock 索引进行靠近性的空间索引是有用的,但也按 length 索引来适配正确的可用空间。
去掉每个文件的写锁
Posix 锁定内存中的 inode 以保障原子写入。这确保了任何两个大型多块写操作总是按程序进行。
XFS 还去掉了内存中的 inode 锁:Posix 要求对于大型、重叠的多块写操作进行齐全有序的解决。当它们重叠时,不能呈现从写操作 A 和写操作 B 交替呈现的块凌乱景象。
在大多数内核中,默认设置是在内存中的 inode 上搁置一个文件全局锁,以此确保每个 inode 只能有一个写入者。数据库的开发者对此十分不满,因为它将任何单个文件的写并发性限度为 1。这也是为什么 Oracle 倡议将表空间分成多个文件来实现并发性,每个文件的大小不超过 1GB。
在 O_DIRECT
模式下,XFS 打消了这个锁,并容许原子、并发的写操作,数据库开发者对此十分认同。
动静 inode 和闲暇空间跟踪优化
对于大型文件系统,你永远无奈预知:应用程序是须要大量的 inode 来存储许多小文件,还是大量的大文件。此外,inode 和文件数据块之间的间隔是多少也没有一个确定的答案。
对于第一个问题没有一个好的答案,而对于第二个问题,答案是“让它们尽可能靠近”。因而,XFS 依据须要动态创建 inode,每次创立 64 个 inode 的块 。
对于较大的 inode,即 256 字节的 inode(相比于 BSD FFS 的 128 字节和传统 Unix 的 64 字节),XFS 应用的策略是,仅在须要时创立 inode,并将它们搁置在文件的结尾左近来进行弥补。这样能够开释大量的磁盘空间。在具备固定 inode 计数的传统 Unix 文件系统中,高达 3 -4% 的磁盘空间可能被事后调配的 inode 所占用。即便在应用了柱面组(cylinder groups),inode 与第一个数据块之间依然存在相当大的间隔。
因为 inode 能够存在于磁盘上的任何地位,而不仅仅是超级块前面,因而须要对 inode 进行跟踪。XFS 通过每个调配组(AG)应用一棵 B+ 树来实现这一点。该树以起始块为索引,在每个块中记录每个 inode 块是否可用或正在应用。inode 自身并不保留在树中,而是保留在凑近文件数据的块中。
相似地,闲暇空间以块为单位进行跟踪,并在每个调配组(AG)的树中进行两次索引:起始块和长度索引。
预写式日志
零碎解体后要复原一个大型文件系统可能会很慢。复原工夫与文件系统的大小和文件数量成正比,这是因为零碎基本上必须扫描整个文件系统并重建目录树,以确保数据的一致性。 对于 XFS 来说,文件系统更加软弱,因为它提供了可变数量的 inode,并且在磁盘上扩散地非间断存储。复原它们将会有很大的开销 。
应用元数据的预写式日志(write-ahead logging),能够在大部分状况下能够防止这个问题。使得复原工夫与日志的大小成正比,即与解体时正在解决的数据量成正比。
日志中蕴含了日志条目,每个条目包含一个描述符头和所有更改过的元数据结构的残缺镜像:包含 inode、目录块、闲暇 extent 树块、inode 调配树块、调配组块和超级块。因为残缺镜像存储在块中,因而复原过程非常简单:只需将这些新的、更改过的镜像复制到它们本来应该在的地位上,而无需理解它所更改的构造类型。
作者对日志十分信赖:因而 XFS 最后没有 fsck(文件系统一致性查看)程序。然而,事实证明这种设置过于乐观了,因而当初有了 xfs_repair
程序。
元数据更新性能
XFS 会记录元数据更新,这意味着它们须要被写入文件系统日志中。默认状况下,该日志会搁置在文件系统中。但也能够抉择将日志提取进去,搁置在其余介质上,例如闪存存储或带有电池备份的内存。
如果可能的话,对日志的写入是异步进行的,然而对于提供 NFS 服务的分区来说,这种写入只能是同步的。异步写入容许进行写入批处理,从而加快速度。但 NFS 服务器从减速的日志存储中获益很多。
因为所有元数据更新都须要被记录在日志中,因而在进行大量元数据操作时,可能会导致日志被洪水般的元数据更新所占满。例如,执行 rm -rf /usr/src/linux
这样的操作就不会特地快,因为元数据更新流最终会导致日志溢出。而且,因为 XFS 中的其余所有操作都是基于调配组(AG)并行进行的,因而日志是可能引起资源竞争的惟一起源。
大文件和稠密文件
在 FFS(Unix File System)中,文件通过传统的动静数组(dynamic array)进行映射,该数组包含间接块(direct blocks)和最多三级的间接块(indirect blocks)。在 64 位文件大小的状况下,这种形式就变得很蠢笨:会须要超过三级的间接块,同时会须要大量的数据块。因为大量的数据块的存在,块编号基本上造成了一个递增的数字列表。这不仅会减少治理的复杂性,也会增大存储块编号的空间开销。FFS(以及 EFS)须要在每个块被调配到文件系统缓冲池(filesystem buffer pool)时就确定它们在磁盘上的地位。能够看到,FFS 实际上没有尝试在磁盘上间断布局文件,而是独自搁置每个块。XFS 用 extents 取代了这个动静数组。
在文件搁置映射(file placement maps)中,这些映射的 extents 是三元组(块偏移量,长度,磁盘块)。这些 extents 被存储在 inode 自身中,直到溢出。而后,XFS 开始在 inode 中创立一个由映射 extents 组成的 B + 树,通过逻辑块号(logical block number)来索引映射 extents,以便进行疾速查找。
在能够进行间断调配的前提下,这种数据结构容许将大量的块(最多 2M 个块)压缩为一个独自的描述符。因而,即便是大型文件,也能够在非常少的 extents 中存储,最现实的状况是每个调配组(AG)只需一个 extent。
实现间断布局:提早调配和预调配
XFS 引入了一个新概念:提早调配,它能够在文件系统缓冲池中调配虚构 extent。这些预留的 extent 是用来寄存还未写入的数据块,它们在磁盘上还没有确定的物理地位。当进行刷新操作时,这些 extent 会被填充理论的数据,而后依照间断的形式进行布局,并以大块形式进行线性写入。这样的设计能够进步写入操作的效率。
这对文件系统缓冲区的工作形式产生了根本性的扭转。以前,通过应用(设施,物理块号)能够标识缓冲区缓存中的块,以避免反复调配缓冲区。然而,当将 XFS 移植到 Linux 时,如果在一般缓冲区不应用此类标识,最后 Linux 内核无奈适应。因而 XFS 须要一个独自的缓冲区缓存。随着移植工作的进行,这个问题起初失去了解决。
为了确保文件能够在单个 extent 中进行存储空间调配而不会呈现碎片化,当关上文件时,XFS 会踊跃为其预调配存储空间。预调配的磁盘空间量是依据文件系统中的可用空间而确定的,默认状况下可能会预调配相当大的空间。
在互联网上,有很多 XFS 用户问到他们的磁盘空间在哪里,答案是“在 /var/log
的关上文件句柄中。此外,查看手册页中对于 allocsize=
的局部,以及 /proc/sys/fs/xfs/speculative_prealloc_lifetime
。
局部性
在进步“局部性”方面,XFS 并不太依赖调配组(AG)来实现。调配组次要用于并发解决。相同,XFS 更多地通过在目录和以后文件的现有块四周搁置文件来优化数据的局部性。惟一的例外是“新目录”,这些新目录会被搁置在不同的调配组中,远离它们的父目录。
在大文件中,如果须要调配新的 extent,也就是为新的数据块调配空间时,依据论文中提到的规定,“首先凑近 inode,而后凑近左近的块”。这样的设置形式使得 inode 搁置在凑近文件结尾的中央,并将起初增加的块搁置在已有块的左近。
大目录
在传统的 Unix 文件系统和 BSD FFS 中,目录名称查找是线性操作。对于任何类型的路径名到 inode 的转换,大目录会显著减慢这一过程。
XFS 抉择了被宽泛应用的 B+ 树作为目录的数据结构。然而,因为键(文件名)是可变长度的构造,与其余文件系统中的树实现齐全不同。XFS 的作者不喜爱这种状况,因而对文件名进行了哈希解决,将其转换为一个固定长度的 4 字节名称哈希值。而后,将一个或多个目录条目以(名称,inode)对的模式存储在 B+ 树的值中。通过这种形式,XFS 可能高效地治理目录,并在须要时疾速查找和拜访特定的文件。这种哈希解决形式容许 XFS 在 B+ 树结构中应用固定长度的键,而不须要关注键的理论长度。
在这方面进行了一些探讨,作者们发现短键能够让每个块存储许多条目,从而造成宽树,进而实现更快的查找速度。他们骄傲地声称:“咱们能够领有数百万条目标目录”,这在以前的 Unix 文件系统中是难以想象的。
大量的代码
在 1994 年的 XFS 基准测试中,XFS 显示出良好的线性扩大体现,可能很好地利用硬件资源。它在 多核的大型机器上体现良好。
XFS 是一个大型文件系统。Linux 的 ext2 有 5,000 行内核代码(和大概 10 倍这个数量的用户空间代码)。而 XFS 有 50,000 行内核代码,这还不包含 IRIX 卷管理器 XLV(在 Linux 中,XFS 移植应用的是 LVM2)。
XFS 在 1999 年 5 月以 GNU GPL 许可协定公布,并从 2001 年开始移植到 Linux 内核。截至 2014 年,它在大多数 Linux 发行版中失去反对,RHEL(Red Hat Enterprise Linux)将其作为默认文件系统。
笔者认为,XFS 是具备最佳扩展性、最佳并发能力和批改工夫最统一的文件系统,这使得它成为任何类型的数据库应用的首选文件系统。它打消了一些全局锁,这些锁会影响大型文件系统的并发应用和性能,并且应用了具备 O(log(n)) 扩展性的 B+ 树结构,而之前应用的算法扩展性都比拟差。应用 extent 还容许动静减少 I/O 大小,有利于进步吞吐量,并与提早调配的新鲜思维一起促成将文件在磁盘上间断地搁置或存储。
如有帮忙的话欢送关注咱们我的项目 Juicedata/JuiceFS 哟!(0ᴗ0✿)