摘要
固态硬盘(SSD)的性能和寿命不仅取决于当前的输入工作负载,还取决于随着时间的推移形成的内部介质碎片,因为陈旧的数据分布在SSD中广泛的物理空间中。最近提出的流为主机系统提供了一种方法来控制数据如何放置在物理介质上(由流抽象),并有效地减少介质碎片。本项工作提出了FStream,一种利用该技术的文件系统方法。FStream在文件系统层次上提取流,避免了复杂的应用程序级数据到流的映射。实验结果表明,FStream将filebench性能提高了5%~35%,并将写放大因子(Write Amplification Factor)降低了7%~46%。对于NoSQL数据库基准测试,性能提高了38%,WAF降低了81%。
1. 引言
固态硬盘(SSD)正在迅速取代企业数据中心的磁盘(HDD)。SSD在内部软件的帮助下维护传统的逻辑块设备抽象,通常称为闪存转换层(FTL)。FTL支持SSD以代替HDD,无需对操作系统的块设备接口进行复杂的修改。
然而,之前的工作表明,这种兼容性是有代价的;当底层介质随着设备老化而碎片化时,由于垃圾收集开销的影响,SSD的运行效率会急剧下降。更具体地说,用户写I/O转换为数倍的实际介质写,这将缩短设备生命周期并妨碍性能。实际介质写入与用户I/O的比值称为写放大因子(WAF)。
为了解决写放大问题和SSD磨损问题,已经进行了大量的前期工作。为此,我们重点讨论如何利用SSD的多流机制。这种机制提供了一种方式来指示数据如何放置在SSD的底层物理介质上,由流抽象。原则上,如果主机系统完美地将具有相同生命周期的数据映射到相同的流,SSD的写放大就成为1,完全消除了介质的碎片问题。
先前的研究揭示了利用流的两种策略。第一种策略是基于对这些数据的预期生命周期的理解将应用程序数据映射到不同的流。例如,日志结构合并树的不同层的文件可以分配给单独的流。案例研究表明,这种策略对于像Cassandra和RocksDB这样的NoSQL数据库非常有效。不幸的是,这种应用程序级定制策略要求系统设计人员非常好地理解目标应用程序的内部工作,这对设计人员来说仍然是一个挑战。另一种策略旨在“自动化”地将写I/O操作映射到SSD流的过程,而不改变应用程序。例如,最近提出的AutoStream方案根据过去LBA访问模式的预估生命周期为每个写请求分配一个流。然而,该方案在复杂的工作负载场景下,特别是在工作负载动态变化的情况下,还不能很好地工作。此外,当文件数据以不合时宜的方式更新时(如在写时复制和日志结构的文件系统中),基于LBA的模式检测是不实用的。以上概述的策略抓住了设计空间中的两个极端——应用级定制与块级全自动化定制。
在这项工作中,我们采用了另一种策略,在文件系统层分离流。我们的方法是基于这样的观察:文件系统元数据和日志数据是短期的,并且是与用户数据分离的良好目标。当然,当应用到像ext4和xfs这样的日志文件系统时,我们方案的主要组件是分别为元数据和日志数据分配一个单独的流。作为我们方案的一个必然组成部分,我们还提出在文件系统层将数据库重做/撤消日志文件作为一个独立的流分开。我们在Linux ext4和xfs系统中实现了我们的方案FStream,并使用各种工作负载和支持流的NVMe SSD进行了实验。我们的实验表明,在我们评估的工作负载中,FStream稳健性地实现了接近最优的WAF(接近1)。我们在这项工作中作出了以下贡献:
- 我们提供了不同类型的文件系统生成的数据的自动多流,这些数据与它们的生命周期有关;
- 我们以最小化的修改增强了现有的日志文件系统ext4和xfs,以使用多流SSD;
- 我们使用文件系统层信息实现应用数据的流分类。
本文的其余部分组织如下。首先,我们在第二节中描述了我们的研究背景,关于以前的多流方案的问题。第三节描述了FStream及其实现细节。第四节给出实验结果,第五节给出结论。
2. 背景
2.1 SSD中的写放大
闪存存储有一个固有的特性,即编程前擦除。写操作,也称为“编程”操作,发生在NAND页面粒度上。除非已被“擦除”,否则页面不能被重新写入。由于这种写前擦除的特性,就地更新是不可能的。因此,覆盖写是通过将数据放在新页面中并使前一个页面失效来处理。擦除操作以NAND块为单位完成,一个NAND块是多个NAND页的集合。在擦除NAND块之前,它的所有有效页面需要复制到其他地方,这是在一个名为垃圾回收(GC)的过程中完成的。这些有效页面的移动会导致额外的写操作,从而消耗带宽并导致性能下降和波动。由于NAND块的程序擦除周期受到限制,这些额外的写操作也降低了持久性。一种测量GC开销的方法是通过写放大因子(WAF),它被描述为闪存上执行的写操作与主机系统请求的写操作的比率。
WAF = Amount of writes committed to flash / Amount of writes that arrived from the host
当SSD经历老化时,WAF可能会更频繁地飙升。
2.2 多流SSD
多流SSD致力于通过关注数据的放置来控制WAF。它允许主机在发送写请求时传递一个提示(流ID)。流ID提供了一种方式来传递关于正在写入的数据的生存期的提示。多流SSD将具有相同流ID的数据分组存储在同一个NAND块中。通过避免不同生存期的数据混合,减少了NAND块内部的碎片。图1显示了使用多流的数据放置如何减少媒体碎片的示例。
多流现在是T10 (SCSI)标准的一部分,NVMe (NVM Express)工作组正在讨论。NVMe 1.3规范以“指令”的形式引入了对多流的支持。NVMe写命令具有携带流ID的条款。
2.3 利用流
虽然多流SSD提供了将数据分离成流的功能,但它的好处很大程度上取决于主机对流的利用程度。确定什么应该和不应该进入同一流是至关重要的。以前的工作显示了应用程序分配流的好处。这种方法的好处是可以准确地确定数据的生存期,但它涉及修改目标应用程序的源代码,从而增加部署工作。此外,当多个应用程序尝试分配流时,需要进行集中的流分配,以避免冲突。不同于直接分配流id,最近Linux(从4.13内核)支持fcntl()接口,来向文件系统发送数据生存期提示,以利用多流ssd。AutoStream将流管理带到NVMe设备驱动程序层。它监视来自文件系统的请求并估计数据生存期。然而,驱动层中只有有限的信息(例如,请求大小,块地址)可用,更糟糕的是,基于地址的算法在写即拷贝文件系统中可能是无效的。
我们的方法,FStream,实现了文件系统层的流管理智能。文件系统有关于文件系统生成的数据的信息,如元数据和日志。为了检测用户数据的生命周期,我们采用了一种简单而有效的方法,即使用文件的名称或扩展名。为了简洁起见,我们不在文件系统层详细讨论用户数据生命周期的估计。
3. FStream
我们首先强调在文件系统中使用多流的动机。接下来是ext4和xfs的盘上布局以及日志记录方法的概述。然后我们深入研究ext4stream和xfstream的细节,它们分别是ext4和xfs的流感知变体。
3.1 动机
应用程序可以比文件系统更好地了解它们所写数据的生存期和更新频率。但是,应用程序不知道文件系统元数据的生存期和更新频率。文件系统元数据的更新频率通常与应用程序数据的更新频率不同,并且经常受到磁盘上布局和特定文件系统的写策略的影响。通常,文件系统在逻辑上保持数据和元数据的分离,但它们在SSD上可能不会保持“物理”分离。在执行文件操作时,元数据写可能与同一NAND块中的数据写混在一起,或者一种元数据可能与另一种元数据混在一起。具有流分离功能的文件系统可以减少应用程序数据和文件系统元数据的混合,降低WAF并提高性能。
3.2 Ext4元数据和日志
ext4文件系统将盘区域划分为多个大小相同的区域,称为“块组”,如图2所示。每个块组包含数据及其相关的元数据,这有助于减少对HDD的查找。ext4引入flex-bg特性,它影响一系列块组,这些块组的元数据被合并到第一个块组中。每个文件/目录需要一个inode,默认大小为256字节。这些inode存储在inode表中。Inode bitmap和block bitmap分别用于分配Inode和数据块。组描述符包含块组中其他元数据区域(inode表、块位图、inode位图)的位置。另一种类型的元数据是目录块。
文件系统一致性是通过在写前日志来实现的。日志是一种特殊的文件,它的块位于用户数据区,在文件系统格式化时预先分配。ext4有三种日志记录模式:数据回写(元数据日志记录)、数据排序(元数据日志记录+在元数据之前写数据)和数据日志(数据日志记录)。默认模式是数据排序模式。
ext4以块粒度记录日志,即使一个inode的几个字节被更改,包含许多inode的整个块(通常是4KiB)也会记录日志。对于日志记录,它需要另一个名为日志块设备(JBD)的组件的帮助,JBD有自己的内核线程jbd2。日志区是用循环的方式写的。图3显示了有序模式下的日志记录操作。在事务期间,ext4会更新内存缓冲区中的元数据,并通知jbd2线程提交事务。jbd2维护一个计时器(默认为5秒),在该计时器到期时,除事务相关的簿记数据外,还将修改后的元数据写入日志区域。一旦更改变得持久,事务就被认为已提交。然后,回写线程将内存中的元数据更改刷新到它们的原始位置,这称为检查点。
3.3 Xfs元数据与日志
与ext4类似,xfs也将磁盘区域划分为多个大小相同的区域,称为分配组,如图4所示。与EXT4块组不同,分配组的主要目标是增加并行性而不是磁盘局部性。每个分配组维护自己的超级块和其他结构,用于空闲空间管理和inode分配,从而允许并行元数据操作。分配组中的空闲空间管理是通过使用B+树来完成的。inode是按64个块分配的。这些块在另一个B+树中管理,专门用于分配索引节点。
为了事务安全,xfs实现了元数据日志记录。在创建文件系统(mkfs.xfs)期间会创建一个名为“log”的单独区域。在执行事务时以循环方式写入日志。xf在内存中维护许多日志缓冲区(默认为8个),这些缓冲区可以记录多个事务的更改。默认提交时间间隔为30秒。在提交期间,修改的日志缓冲区被写入磁盘上的日志区域。提交后,修改后的元数据缓冲区将被安排刷新到它们的实际磁盘位置。
3.4 Ext4Stream:ext4中的多流
表1列出了我们在ext4中引入的流。可以使用表中列出的相应挂载选项启用这些流。
journal_stream挂载选项用于分离日志写入。我们在journal_s数据结构中添加了j_streamid字段。当使用journal_stream选项挂载ext4时,将分配一个流ID并存储在j_streamid字段中。jbd2在使用submit_bh和相关函数在日志区域写入脏缓冲区和描述符块时传递此流ID。
ext4为各种元数据缓冲区(包括inode、位图、组描述符和目录数据块)使用了buffer-head (bh)结构。我们在buffer-head里添加了一个新的字段streamid来存储流ID,并且修改了submit_bh。当从buffer-head形成一个I/O时,该字段也在bio中设置,将流ID信息带到更低的层。ext4stream在其超级块中维护不同元数据区域的流id,并根据元数据buffer-head的类型相应地设置bh->streamid。
inode_stream挂载选项用于分离inode写入。默认的inode大小是256字节,所以单个4KiB FS块可以存储16个inode。修改一个inode将导致写入整个4KiB块。当ext4stream修改inode buffer时,它还将bh->streamid设置为stream ID表示inode流。
dir_stream挂载选项是将目录块保存在它自己的流中。当在目录中创建一个新文件或子目录时,需要添加一个新的目录条目。这将触发属于目录的现有数据块的更新或新数据块的添加。目录块组织在一个htree中;叶节点包含目录条目,而非叶节点包含索引信息。我们为这两种类型的目录块分配相同的流ID。
misc_stream将inode/block位图块和组描述符块保存在流中。这些区域在创建/删除文件/目录期间以及在将数据块分配给文件/目录时接收更新。我们把这些区域归为一个单一的流,因为它们面积小。
fname_stream帮助将某些特殊文件的数据放到不同的流中。动机是用它来分离SQL和NoSQL数据库的undo/redo日志。
extn_stream用于支持基于文件扩展名的流识别。某些文件(如多媒体文件)的数据块可以被认为是冷的。Ext4Stream可以在创建文件时解析文件的扩展名。如果它与一些知名的扩展名匹配,文件将被分配一个不同的流ID。这有助于防止热数据块或冷数据块与其他类型的数据/元数据块混合。
3.5 XFStream:xfs中的多流
表2列出了我们引入xfs的流。可以使用表中列出的相应挂载选项启用这些流。
Xfs实现了自己的元数据缓冲,而不是使用页面缓存。xfs_buf_t数据结构用来表示一个缓冲区。除了元数据,日志的内存缓冲区是通过xfs_buf_t实现的。当缓冲区被刷新时,一个bio根据xfs_buf_t被提出。我们在xfs_buf_t中添加了一个名为streamid的新字段,并使用它在bio中设置流信息。
log_stream使Xfstream可以使用自己的流ID在日志区域中执行写操作。每个挂载的xfs卷由一个xfs_mount_t数据结构表示。我们在其中添加了log_streamid字段,这是在用log_stream挂载xfs时设置的。该字段用于传递流信息,以表示日志缓冲区。
inode_stream挂载选项使Xfstream能够将inode写入流。在xfs_mount_t中将一个新的字段ino_streamid设置为用于inode的流ID。该字段用于在表示inode缓冲区的xfs_buf_t中传递流信息。
最后,使用fname_stream使Xfstream能将一个不同的流分配给具有特定名称的文件。
4. 评估
我们的实验系统配置如下。
- 系统:Dell Poweredge R720服务器,32核32GB内存,
- 操作系统:支持io-stream的Linux内核4.5,
- SSD:三星PM963 480GB,分配粒度1.1GB,
- 基准:filebench [12], YCSB (Yahoo!云服务基准)0.1.4 [4],Cassandra 1.2.10[10]。
等等。。。
5. 总结
在本文中,我们提出在文件系统层应用多流的方法,以解决SSD寿命问题和GC开销问题。以前使用多流的建议要么是应用级定制,要么是块级完全自动化。我们的目标是在这些建议的基础上迈出新的一步。我们在文件系统级别分离流。我们主要关注文件系统生成的数据的属性,比如日志和元数据,因为它们是短期的,因此适合于流分离。我们实现了对那些文件系统生成的数据的自动分离,不需要用户干预。我们不仅提供了完全自动化的元数据和日志分离,而且我们还提出将应用程序使用的重做/撤销日志分离到不同的流。我们的流分离方案FStream实现了物理数据分离,这有助于FTL减少GC开销,从而提高SSD的性能和生命周期。我们将FStream应用于ext4和xfs,并在模拟数据中心中真实服务器的各种工作负载下获得了令人鼓舞的结果。实验结果表明,该方案提高了filebench性能5%~35%,降低了WAF 7%~46%。对于Cassandra工作负载,性能提高高达38%,WAF降低高达81%。
我们的建议可以在SSD性能和寿命方面带来相当大的好处。作为未来的工作,我们考虑将FStream应用到日志结构的文件系统,比如f2fs,以及写时复制的文件系统,比如btrfs。我们还计划评估分配粒度和最优写大小如何影响性能和持久性。