Contents

在Linux中使用大文件

1. 简介

对于用户和系统管理员来说,大文件以多种形式出现。它们的主要好处是封装、集中化和空间效率。然而,大文件也可能导致许多陷阱

在本教程中,我们探讨了在 Linux 中处理大文件的方法。首先,我们检查这些文件带来的一些常见问题,以及它们的一些解决方案。之后,我们描述了在部分处理大文件时提供帮助的工具。接下来,几个大文件编辑器开始发挥作用,为每个. 最后,我们通过类型优化和特殊物理内存使用深入研究该主题。

我们使用 GNU Bash 5.0.3 和 Xfce 4.16 在 Debian 10.10 (Buster) 上测试了指南中的代码。它是 POSIX 兼容的,应该可以在任何这样的环境中工作。

2. 大文件大小问题

文件大小分类取决于可用的硬件。简而言之,低机器规格可能会导致较小的“大文件”出现问题。我们将使用具有 8GB RAM 和 SSD 的机器。

平均而言,让我们将大文件定义为大于 50MB 的任何文件。此外,我们将 1GB 以上的文件视为大文件。使用此类文件在许多领域都存在挑战。

2.1. 转移

大文件传输需要很长时间。例如,以目前世界平均的宽带下载速度传输一个 5GB 的文件大约需要 8 分钟。相同的文件在移动设备上需要超过 17 分钟。如果我们需要定期下载大文件,那是一个巨大的挫折

显而易见的解决方案是提高下载速度。如果这不是一个选项,我们可以按照下一节中的建议进行操作。

当然,在传输之后,我们必须将文件存储在某个地方。

2.2. 贮存

磁盘 IO 仍然是现代计算机的瓶颈。将大文件从主驱动器部分或全部加载到更快的 RAM 中需要时间

如果我们**用现代技术的固态驱动器替换旧的硬盘驱动器,**我们可以减少这个时间。或者,旧磁盘可以使用RAID

无论存储是什么,操作系统都将其划分为单独的文件系统,这些文件系统处理实际操作。

2.3. 文件系统

旧文件系统有很多限制。例如,FAT 的限制为 4GB。另外,文件越大,它们就越难分布在可用空间的不同分区上。

最后,分配单元大小( )在处理大文件时起着重要作用。当文件存储在物理介质上许多遥远的块中时,它们会导致大量的块,但也会导致碎片。

我们应该使用对大文件有良好支持的现代文件系统,例如BtrfsExtXFS 。相比之下,ReiserFS  针对大量小文件进行了优化。此外,我们应该使用遵循大文件最佳实践的分配单元大小。在广泛处理大文件时,定期进行碎片整理也很重要。

忽略这些指针可能会减慢相关文件的操作速度。一种这样的操作是读取。

2.4. 读取

在执行数据读取时,我们必须考虑可用的RAM交换 大小。例如,如果没有交换,我们无法在 512MB 的物理内存中容纳整个 1GB 文件。即使使用 swap,我们也可能会遇到thrashing 。这些是处理大文件时真正关心的问题。

没有增加内存和配置快速交换空间,我们只能选择合适的工具来读取文件。软件的负担在于正确缓冲和组织文件读取和编辑

2.5. 编辑

大多数时候,编辑面临许多与阅读相同的挑战。此外,它会在初始加载(读取)后导致文件内的搜索。我们需要寻找并更改或添加数据。寻道操作可能代价高昂,尤其是在碎片严重的情况下。

显然,文件内的数据组织可以缓解过度跳转。然而,最终,用户和他们的工具决定了文件编辑会出现多少问题

让我们更深入地研究编辑,因为它们结合了大文件与小文件相比引入的所有因素。

3. 部分处理

存在多种工具,可以毫无问题地读取和编辑任何大小的文件。一些工具仅使用缓冲,而其他工具则利用部分读取,具体取决于可用内存和交换

我们将使用相同的 12GB大文件,由 500,000,000 行组成,格式为“This is line #LINE_NUMBER”

$ time cat hugefile
This is line #0
This is line #1
[...]
This is line #499999999
This is line #500000000
real 106m25.480s
user 0m0.883s
sys 8m27.648s

此外,我们为所有命令time ,以便于比较,如上面的输出所示。这是同一文件的时序,输出到另一个文件而不是屏幕:

$ time cat hugefile > hugefilecopy
real 2m28.119s
user 0m0.461s
sys 0m10.236s

注意时间。我们输出到屏幕的速度非常慢,而输出到文件的速度要快得多(快了将近 50 倍)。这对于大文件至关重要。 重要的是,缓存和交换将在每个命令之后通过一系列简单命令被清除:

$ echo 3 > /proc/sys/vm/drop_caches
$ swapoff -a
$ swapon -a

3.1. 拆分

处理大文件的最基本方法是一开始就没有它们。这并不一定意味着永久重组数据。我们可以暂时拆分 有问题的文件

$ ls -lh
total 12G
-rwxrwxrwx 1 x x 12G Aug 19 12:53 hugefile
$ time split --bytes=50M hugefile
real    2m19.451s
user    0m0.610s
sys     0m10.708s
$ ls -lh
total 23G
-rwxrwxrwx 1 x x 12G Aug 01 00:00 hugefile
-rwxrwxrwx 1 x x 50M Aug 01 00:00 xaa
[...]
-rwxrwxrwx 1 x x 50M Aug 01 00:03 xir
-rwxrwxrwx 1 x x 39M Aug 01 00:03 xis

在拆分之前,我们可以看到该文件大约为 12GB。拆分为 50MB 的块(–bytes=50M),我们得到多个完整的 50MB 文件和一个 39MB 文件。它们的名称是拆分默认值。 同样,在我们完成处理后,**我们可以通过cat (concatenate) 和redirection **重新加入块:

$ time cat x* > rejoinedhugefile
real 2m26.441s
user 0m0.437s
sys 0m10.016s
$ ls -lh rejoinedhugefile
-rwxrwxrwx 1 x x 12G Aug 01 00:00 rejoinedhugefile

这种方法用于许多层面。例如,文件可以在传输过程中自动拆分。当然,传输过程中的网络数据包采用相同的策略。

正如我们将在下面看到的,我们可以将相同的想法用于读取和查找。

3.2. head 和 tail

*如果我们已经知道要查看的一行或多行,*head tail 只显示它们。例如,让我们只获取文件的第一行(–lines=1)

$ time head --lines=1 hugefile
This is line #0
real 0m0.014s
user 0m0.000s
sys 0m0.005s

同样,我们可以得到最后一行:

$ time tail --lines=1 hugefile
This is line #500000000
real 0m0.014s
user 0m0.003s
sys 0m0.001s

最后,我们还可以链接命令以获取摘录:

$ time tail --lines=100 hugefile | head --lines=3
This is line #499999901
This is line #499999902
This is line #499999903
real 0m0.013s
user 0m0.005s
sys 0m0.002s

在上述几乎所有情况下,使用headtail会产生相似的时间,因为它们仅在少量查找操作上有所不同。

headtail的工作 方式确保了整个文件不会一次加载到内存中。这是许多工具的共同主题。

3.3. grep

我们通常不知道要在文件中使用的数据的确切位置。在这些情况下,grep (全局搜索正则表达式并打印匹配行)可以提供帮助。 让我们做一个复杂的正则表达式搜索:

$ time grep --line-number --extended-regexp '[0-1]0000666[0-1]|126660002' hugefile
100006660:This is line #100006660
100006661:This is line #100006661
126660002:This is line #126660002
real    0m17.770s
user    0m9.192s
sys     0m2.821s

–line-number标志确保我们只看到匹配的行及其行号。此外,–extended-regexp标志允许使用 POSIX 扩展正则表达式。

grep只能搜索,当我们想要执行有针对性的替换时,还有其他工具。

3.4. sed

虽然是一个非常通用的工具 ,但我们也可以使用*sed *(流编辑器)进行就地编辑。例如,要删除多个不同的字符串,我们可以运行:

$ time sed --in-place --regexp-extended 's/[0-1]0000666[0-1]|126660002/xxx666xxx/g' hugefile
real    3m0.502s
user    0m52.071s
sys     0m13.875s
$ grep --line-number 'x' hugefile
100006660:This is line #xxx666xxx
100006661:This is line #xxx666xxx
126660002:This is line #xxx666xxx

当然,尽管很复杂,但这种编辑相当原始,因为它们需要事先了解文件中的数据。换句话说,它们缺乏其他工具可以提供的上下文。

3.5. less

*less *命令是一个终端寻呼机。它的功能其较旧且较简单的亲戚 *more *类似。我们使用它们逐页查看、搜索和滚动文件

终端寻呼机的主要好处是它们在读取和查找操作期间的简单缓冲。从本质上讲,它们允许我们查看任意大的文件,而不会给内存带来超过几页的数据负担。

** lessmore都有一个键盘快捷键 ( v ),它会启动默认的系统编辑器**。此外,编辑器在当前行加载给定文件。

4. 编辑器

我们可以通过*perl pythonawk *等编程语言来实现复杂的目标文件编辑。但是,这样的处理超出了本文的范围。

方便文件更改的常用工具是成熟的编辑器。例如,他们支持:

  • 读取,包括分页
  • 写,包括编辑
  • 寻找,包括搜索
  • 用于复杂操作链接的宏
  • 鼠标/键盘快捷键绑定

重要的是,并非所有此类编辑器都适合处理大型文件。我们将讨论下面的一些内容。

4.1. vi和 gvim

vi (可视)编辑器是许多 Linux 发行版的标准配置。它的改进版本是vim,它有一个图形界面gvim。为了我们的目的,所有功能都等效。

使用 SSD 存储在 ( g ) vi ( m ) 中打开大文件(12GB)大约需要三分钟。即使没有交换,编辑器使用的机器上可用的 8GB RAM 也不到 60%。这可以通过编辑器自己的交换文件来实现

我们可以自由地滚动文件,寻找一行,或者定位和修改内容。编辑器在我们请求时从文件中加载更多数据。因此,有足够的物理内存可供处理。

然而,限制确实存在。例如,vi仅搜索已从磁盘加载的文件部分。我们必须选择性能或可用性。实际上,在稍作编辑后将文件保存回磁盘大约需要 5 分钟。

考虑到硬件限制,任何编辑器都无法避免这种权衡。

4.2. joe

joe 编辑器也很常用。由于它自己的交换文件,它被宣传为能够再次编辑大于物理内存的文件。它与vi有很多重叠的功能

由于这个和等效的硬件,joe在操作期间具有与vi相似的统计信息:在大约 3 分钟内打开大文件并在大约 6 分钟内保存它。

当然,类似的功能意味着类似的限制。相比之下,有些编辑器允许有限的一组操作,但是就地操作,就像上面的sed一样。

4.3. 十六进制编辑器

不能在文件中追加或删除数据是可以接受的性能牺牲。默认情况下只允许读取、搜索、查找和修改的编辑器通常是十六进制(来自十六进制)编辑器。

它们被称为十六进制编辑器,因为它们通常表示数据的方式——使用原始十六进制代码。尽管如此,大多数十六进制编辑器都有分屏模式,其中数据也被解释和编辑为ASCII

此外,十六进制编辑器的主要好处是,默认情况下,它们不会将整个文件缓冲到内存中。因此,我们的操作集有限,但性能优化。保存文件非常快,因为只有编辑的部分被直接(就地)替换

或者,例如, hexedit (十六进制)编辑器可以使用*–buffer*标志将整个文件缓冲到内存中 。以这种方式缓冲允许追加和删除操作,但需要足够的物理内存来保存整个文件。

5. RAM 覆盖率

一些常用的编辑器要求整个文件适合物理内存

由于性能原因,使用这些编辑器可能是最佳选择:它们比使用(基于磁盘的)交换快得多。当我们想要处理的文件有足够的 RAM 可用时,瓶颈只会是磁盘 IO。在这种情况下,我们可以使用一种特殊的方法来利用整个文件缓冲——RAM 驱动器

RAM 驱动器用于加速处理特别大的文件的操作。他们分配部分物理内存并将其安装在文件系统上。在最简单的情况下,要创建一个 1GB ( -o size=1g ) RAM 驱动器并将其挂载到*/mnt/ramdisk上,我们可以使用mount.tmpfs *(虚拟内存文件系统):

$ mkdir /mnt/ramdisk
$ mount -t tmpfs -o size=1g tmpfs /mnt/ramdisk

存储在/mnt/ramdisk*的任何文件都将保存在物理内存中*。将任何创建或修改的文件从该位置直接移动到磁盘上的另一个位置可以被视为“保存”。

6. 大文件类型

如果我们知道它与给定的编辑无关,许多讨论的工具可以跳过大块数据。了解文件的内容可以大大降低复杂性并提高任何操作的性能

为此,我们正在处理的文件类型非常重要。让我们来探讨一些例子。

6.1. 日志

日志文件通常具有固定格式。他们围绕日期/时间和严重性的组织允许轻松排序,这在手动编辑时非常有用。

6.2. 数据库

数据库很少存放在一个单一的文件中,但它们可以。即使它们不是单个文件,它们的单独部分也可能很大。数据库组织针对特定类型的访问进行了优化,因此为这种特定访问使用工具可以大大减少文件大小的负担。

6.3. 带标题的文件

通常,许多文件的开头都有标题。本质上,它们包含有关文件的信息,例如大小、格式和结构等。这些标头的工作方式类似于磁盘分区或文件系统信息。**元数据标头在遍历和编辑文件时可以证明是无价的,**因为它们可以使我们在不进行大量资源搜索的情况下查明必要的信息。