Linux 是否需要整理磁盘碎片?

TL;DR

Yes and no. 取决于文件是怎么写入的。

对于 RSS BT 资源站然后长时间保种的公网分流机来说,需要;
对于刷流量的盒子,不需要;
其他工作负载,看写入和读取的 pattern。

Linux 文件系统

如果去查一些有关 Linux 碎片整理的资料,无论是英文的还是中文的,有大概率会看到这样的结论:Linux 上的现代文件系统,例如 ext4/XFS 这些,它们有一些很智能的文件块分配方法,因此从设计上来说就不需要碎片整理,因此 Linux 基本上一辈子不需要做碎片整理这件事。

这个结论不能完全说是错的,但是或许改成 ”Linux 对磁盘碎片的容忍度更高“ 会更加准确一些,在我几台大量 BT 下载的盒子上,即使是 ext4 文件系统,也观察到了大量的碎片现象。因此如果对这些碎片做些整理,无疑对于分流这种一次写入,长期读取的任务来说是有相当大的好处的。

Ext4 的文件分配方式

如果学过 Ext2/3 的话,我们会知道在这两个文件系统中磁盘的分配方式是 block,包括 inode metadata 中的 direct block 和最多三层(如果没记错的话)的 indirect block。如果是小文件,会被直接放到 metadata 的 direct block 中;如果文件较大放不下,则会采用 indirect block 以指针的形式指向对应的磁盘块。

在 ext4 中,引入了一种新的文件分配方法:extent。一个 extent 是一个单一的标识符,标志着在逻辑上和物理上都连续的一系列 4KiB block,一个 extent 最大可以到 128MiB。以 extent 而不是 block 的方式进行文件的存储,可以降低 indirect block metadata 的读取,进而提高大文件的存取效率。

除了 extent 之外,ext4 还引入了其他两项功能:multiple block allocation 和 delayed block allocation。前者允许一次性分配大量的连续文件块,以降低碎片并且有利于 RAID 设备的并行写入;后者可以将 block 的分配延迟要写入时进行,这样就有机会在写入时合并之前分配的若干个块,以降低碎片。后者简单来说就是,用户连续对同一文件调用了两次 fallocate() 或者 posix_fallocate() 分配若干的块,但是都没有进行写入,那么在后续写入的时候可以直接将这两次分配进行合并以获取一大块连续的区域。

这两项功能,加上对 allocation without initialization 的支持(也就是可以直接向文件系统要一块空间,啥也不干就是先要着),就是很多地方说 ext4 不需要碎片整理的理论基础。但是如果我们仔细想一想 BT 下载的写入方式:先下载进写入缓存,然后成批写回磁盘。如果没有在客户端中启用 preallocation,那么实际上这些引入的新技术完全不会发挥任何作用,BT 客户端不可避免地需要对已下载的文件进行大量改写,进而导致碎片。而且,分流的文件有不少都是需要多于一个 extent 存储的大文件,而且在下载时也是大量文件同时进行,这时进行的分配几乎一定是低效的。

只有 BT 下载会导致碎片吗?

很遗憾,并不是。另一个我们经常会用到的工具 rsync 默认也没有进行 preallocation,因此在我们进行老盒子到新盒子的迁移时,rsync 默认的行为同样会在新盒子上带来大量的碎片。这个其实和直觉相悖,通常我们会觉得,把老盘上的东西往新盘上复制一遍,碎片就自然消失了。这个可能在 Windows 上成立,rsync 默认的行为并没有考虑到这一点。也就是说,刚配置好且新鲜迁移过来的新盒子,可能那些复制过来的文件已经碎成渣了,这一点在我的几个分流盒子上也得到了证实。

那么,我们怎么解决这个问题?

NEC 的 Akira Fujita 和 Takashi Sato 在十年前就为 ext4 写了在线整理碎片的工具,因此我们直接拿来用就好了。在 Ubuntu 上,我们需要安装 e2fsprogs 这个包,然后使用 e4defrag -v <path> 进行在线的碎片整理。

在整理之前,我们可以先使用 -c 命令运行,通常它都会告诉你不需要整理,但是实践证明,对于分流的文件有时甚至可以将上百个 extent 合成两三个,这个对于机械硬盘寻道时间的降低是会有很大作用的。e4defrag 汇报认为不需要整理其实也很好理解,如果在一些视频文件中交织有大量的扫图、字幕等等小文件,这些小文件通常都只需要一个 extent 就可以放下,而且数量其实比视频多得多,因此在总量上看,自然不是那么的碎。

对于分流这个任务来说,除了应该定期运行一下碎片整理以外,我们也应该打开 BT 客户端的 preallocation 功能(qBittorrent 不是默认开启的),在 RSS 时尽量降低碎片的数量。在使用 rsync 迁移盒子时,带上 –preallocate 参数,使得新盒子以一个真正全新的状态投入工作。

我这个盒子是拿来刷流量的,你说的这些有用吗?

从我的角度来看,如果是刷流量的盒子,那就当作没看过这篇文章吧。因为刷流量的行为主要是 Hit&Run,一个种子下载好了以后很快就删了,下载过程的碎片并不会长期影响硬盘 IO 速度,打开 preallocate 又在下载前需要多一道工序,提升也很有限,因此并不需要遵循本文的建议。

关于分流任务的其他一些东西

我个人认为,跑一堆分流的种子,这些种子一般会遵循这样的一种模式:新发布的种子有很多请求,然后热度逐渐下降,然后保持一个低频率但是比较稳定的请求数量,如果有什么热点事件,比如某番出第二季了,那么可能第一季会突然有不少人来下。Preallocate 主要是为了解决发布初期的分流效率,将一个种子内的文件集中在一个小区域可以减少大量的随机寻道时间,而定期整理碎片则可保证其他种子请求内部的效率。我曾经有过这样的看法:分流任务挂大量的种子本就是大量随机请求,因此整理碎片并不能降低请求的随机性,让 IO Scheduler 去管理这些请求就已足够,但实际上,各个种子的请求频率之间并不均衡,因此以贪心的方式提高每个文件的连续性,降低其内部请求的成本,在整体上看来依然有足够的好处。

如果你拿 SSD 盒子跑分流……那当我没说

参考资料

  1. Ext4: The Next Generation of Ext2/3 Filesystem [Usenix],其实仔细看看会发现曹子德从来没说过 ext4 不要碎片整理,反而提到了在线磁盘整理的开发进展
  2. Ext4 online defragmentation [LWN][Takashi Sato]
  3. rsync 导致大量碎片的一个报告
  4. rsync 参数
  5. posix_fallocate()
  6. Linux 的 fallocate()
  7. ext4 extent tree 的解析,其中有提到作者的 /var/log/message 有 340 个 extent 之巨,但是这个日志文件并没有很大
  8. e4defrag 参数

发表评论

电子邮件地址不会被公开。