跳转到内容

Btrfs

来自 Arch Linux 中文维基

译自 Introduction — BTRFS Documentation

BTRFS 是 Linux 上现代的写时复制(CoW, Copy-on-Write)文件系统,旨在在提供高级功能的同时,注重容错能力、修复能力且便于管理。
注意:与其它一些文件系统类似,Btrfs 仍在持续开发中,因此可能存在某些功能尚未成熟以供日常使用。如需确认特定使用场景是否会受到影响,可查阅 Btrfs 官方文档的状态章节(英文)及本文的 #已知问题部分。

准备

[编辑 | 编辑源代码]

安装用户空间工具 btrfs-progs,这是执行基本操作所必需的。

若需要从 Btrfs 文件系统启动(即内核和 initramfs 位于 Btrfs 分区),请检查引导加载程序是否支持 Btrfs。

创建文件系统

[编辑 | 编辑源代码]

下文展示如何创建一个新的 Btrfs 文件系统。若要将一个 Ext3/4 分区转换为 Btrfs,请参考 #从 Ext3/4 转换。若要使用无分区的配置,请参考 #无分区 Btrfs 磁盘

查阅 mkfs.btrfs(8) 以获取更多信息。

单设备文件系统

[编辑 | 编辑源代码]

要在分区 /dev/分区名 上创建一个 Btrfs 文件系统,执行:

# mkfs.btrfs -L 自定义标签 /dev/分区名

Btrfs 文件系统中,元数据的默认节点大小为 16 KiB,而数据的默认扇区大小等于系统页大小(自动检测)。如需为元数据指定更大的节点大小(必须是扇区大小的整数倍且为 2 的整数次幂,最大允许 64 KiB),可通过 -n 选项设置节点大小(nodesize),如下例使用 32 KiB 的节点大小:

# mkfs.btrfs -L 自定义标签 -n 32k /dev/分区名
注意:根据 mkfs.btrfs(8) § OPTIONS 手册页内容:“较小的节点大小会导致碎片化,但 B 树变得更高,从而能够减少锁竞争。较大的节点大小则能使数据更加紧凑并降低碎片化程度,但在更新元数据块时内存操作开销更大。”

多设备文件系统

[编辑 | 编辑源代码]
警告:

可以为多个设备创建一个 Btrfs 文件系统。Btrfs 支持的 RAID 级别包括 RAID 0、RAID 1、RAID 10、RAID 5 和 RAID 6(详情参阅下文 profiles)。Btrfs 的 RAID1 profile 仅保存 2 个副本,内核 5.5 版本引入了 RAID1C3 和 RAID1C4,分别对应带有 3、4 个副本的 RAID 1 级别。可以使用 -d-m 参数分别为数据和元数据配置 RAID 级别。默认情况下,数据只有一份(使用 single),元数据则镜像存储(使用 raid1),这相当于一个 JBOD 配置⸺多个磁盘会被看做成一个文件系统,但文件不重复。更多有关如何创建一个 Btrfs RAID 卷的信息请参阅 btrfs wiki: 在多个设备上使用 Btrfs

# mkfs.btrfs -d single -m raid1 /dev/分区1 /dev/分区2 ...

必须在 /etc/mkinitcpio.conf 中加入 udevsystemdbtrfs 钩子之一才能使用多设备 Btrfs 文件系统。查阅 Mkinitcpio#常用钩子以获取更多信息。

创建好文件系统之后,建议使用下面的命令扫描并注册多设备 Btrfs 文件系统,这样在挂载多设备文件系统时就仅需指定其中的一个设备:

# btrfs device scan
注意:
  • 可以稍后再将设备添加到多设备文件系统中,详情请参见 btrfs Wiki 上的这篇文章
  • 多个设备可以大小各异。但如果使用 RAID 配置存储数据,当一个硬盘的大小比其他的都大时,多出的空间可能不被使用。在 Btrfs 中确定可用空间仍然很困难,可以使用 Btrfs 磁盘使用计算器确定。
  • 有些引导加载程序不支持多设备文件系统,比如 Syslinux
  • Btrfs 不会自动从速度最快的设备读取,因此混合使用不同类型的磁盘会导致性能表现不稳定。详情请参阅 Stack Overflow 上的这个回答

#RAID 小节中有关于维护多设备上的 Btrfs 文件系统的一些建议。

Profiles

[编辑 | 编辑源代码]

Btrfs 使用 profiles 这个概念来表示镜像、奇偶校验和条带属性,对应 RAID 术语中的“RAID 级别”(若适用)。在同一个 Btrfs 文件系统中,元数据的 profiles(mkfs.btrfs(8)-m 选项)和数据的 profiles(mkfs.btrfs(8)-d 选项)可以不一样。

一些重要的 profiles:

single
没有镜像、条带和奇偶校验,让多个设备映射到单个文件系统中,在 mdadm 术语中被叫做 LINEAR
RAID0
没有镜像、条带和奇偶校验,但允许并行访问设备,不像传统的 mdadm RAID 那样要求设备大小相同。
RAID1
镜像,没有条带和奇偶校验,允许从一个设备失效的情况下恢复访问。

更多 profiles 请参阅手册 mkfs.btrfs(8) § PROFILES

配置文件系统

[编辑 | 编辑源代码]

写时复制(CoW)

[编辑 | 编辑源代码]

默认情况下 Btrfs 总是对所有文件使用写时复制(CoW)。写入操作不会就地覆盖数据,而是将修改后的数据块写入新位置,同时更新元数据指向新位置。请参阅 Btrfs 系统管理指南相关章节以获取实现细节以及其优缺点。

禁用写时复制

[编辑 | 编辑源代码]
警告:在 Btrfs 中禁用写时复制会禁用数据校验和 [1] [2],禁用数据校验和会使 Btrfs 无法得知这些文件是否损坏。与 RAID 1(包括 profile 中的 RAID1RAID1C3RAID1C4)结合使用时,断电或其他导致损坏的原因可能会造成副本间的数据不同步。

使用 nodatacow 选项挂载子卷可禁用其上新文件的写时复制特性,注意这只会影响新建的文件,而不会影响已存在的写时复制文件。nodatacow 参数会禁用压缩与数据校验和,参阅 btrfs(5) 以了解细节。

注意:根据 btrfs(5) § MOUNT OPTIONS: “在单个文件系统中,无法让某些子卷使用 nodatacow 参数挂载,而其他的使用 datacow 参数挂载。第一个被挂载子卷的挂载参数将会应用于其他所有子卷。”

要禁用空文件或目录中新文件(同上,不会影响其中已存在的写时复制文件)的写时复制特性,请使用下面的命令:

$ chattr +C 目录或文件路径
注意:
提示:为目录中已存在的所有文件禁用写时复制可以使用下面的方法:
$ mv /path/to/dir /path/to/dir_old
$ mkdir /path/to/dir
$ chattr +C /path/to/dir
$ cp -a --reflink=never /path/to/dir_old/. /path/to/dir
$ rm -rf /path/to/dir_old
需要保证这个过程中未使用涉及到的数据。关于此处 --reflink=never 参数的使用,详见下文 #对复制的影响
仍然会触发写时复制的情况
[编辑 | 编辑源代码]

具有 +C 属性的文件仍然允许有超过一个引用(例如使用 cp reflink 的副本与 Btrfs 中的快照),此时写时复制依然会生效

本文或本章节的语言、语法或风格需要改进。参考:帮助:风格

原因:子章节嵌套过多。(在en:Talk:Btrfs讨论)
对复制的影响
[编辑 | 编辑源代码]

使用 cp 复制文件时,+C 属性并非由原文件决定,而相当于在目标路径新建的文件(从目录继承或由子卷是否使用 nodatacow 参数挂载决定),当原文件与目标文件都具有/不具有 +C 属性时,能够通过 reflink 进行复制,否则不能。具体运行的结果取决于 --reflink 参数,有以下三种情况:

  • 使用 --reflink=auto 参数(默认情况):若原文件与目标文件都具有/不具有 +C 属性,则直接 reflink,否则进行深拷贝。
  • 使用 --reflink(或 --reflink=always)参数:若原文件与目标文件都具有/不具有 +C 属性,则直接 reflink,否则报错。
  • 使用 --reflink=never 参数:强制进行深拷贝。

总的来说,如果确实要避免写时复制,必须显式地使用 --reflink=never 参数(若目标路径没有 +C 属性,先创建一个有 +C 属性的空文件再覆盖它)。有关 --reflink 参数的更多信息请参阅 cp(1)

对快照的影响
[编辑 | 编辑源代码]

如果为禁用了写时复制的文件创建快照,快照会锁定原文件块位置,因此首次写入该文件的块时会触发写时复制。但文件仍保留 +C 属性,故后续对同一文件块的写入仍为原地操作,直至下次创建快照。

频繁快照会减弱 +C 属性的效果,因为在每次快照后的第一次写入都会触发写时复制。要避免这种情况,可以把所有禁用写时复制的文件放在一个单独的子卷中并避免为该子卷创建快照。

压缩

[编辑 | 编辑源代码]

Btrfs 支持透明自动压缩,这不仅能缩减文件体积,还能通过减弱写入放大效应显著延长闪存介质的使用寿命,详见 Fedora:Changes/BtrfsByDefault#Compression[3][4]。在某些情况下(例如单线程繁重文件 I/O 时),压缩可以提升性能,但在其他场景中(如多线程或高 CPU 占用并涉及大量文件读写的任务),性能则会明显下降。通常采用压缩算法越快(zstdlzo)性能越好,一些基准测试提供了详细的性能对比数据。

压缩类型

[编辑 | 编辑源代码]

Btrfs 支持的压缩算法有 zlib(即 zlib 库提供的 DEFLATE 压缩算法)、lzo(即 LZO)和 zstd(即 zstd 实现的 Zstandard 压缩算法)。其中 lzo 无压缩级别,而 zlibzstd 可调整压缩级别(zlib:1~9;zstd:-15~-1、1~15,需为整数),级别越高压缩效果越好而所需时间越长。改变压缩级别将直接影响 CPU 和 I/O 吞吐,因此,建议在变更前后分别执行基准测试以验证优化效果。关于压缩类型的更多信息,可参阅 btrfs(5) § COMPRESSION

警告:如果使用 zstd 算法(尤其是 -15~-1 级别),使用较旧的内核或 btrfs-progs 的系统可能不能读取或修复您的文件系统。

启用压缩

[编辑 | 编辑源代码]
为文件系统启用
[编辑 | 编辑源代码]

挂载时指定 compress=算法[:级别] 挂载选项可自动考虑为文件系统中每个写入启用压缩,其中的 算法 处可以填写 zliblzozstdno(即不压缩),可选的 级别 处填写压缩级别,不填写或填写 0 时取默认值 3(lzo 不适用)。使用该选项时,Btrfs 会检测文件写入的数据的首个数据块经压缩后体积是否缩小。如果缩小,则压缩本次写入的全部内容,否则文件会被标记为 NOCOMPRESS,整个写入过程及该文件后续的所有写入过程都不会触发压缩 [5]。这是为了防止等待所有待写入数据完全交付给 Btrfs 并进行尝试压缩后,磁盘才开始执行写入操作。

注意:
  • 压缩挂载选项会在同一文件系统的所有挂载点(包括不同子卷与绑定挂载)之间共享 [6]
  • 若文件的一次写入无法压缩,此后写入均不会进行压缩。

这篇文章的某些内容需要扩充。

原因:下文陈述的有关试验测试的结果缺少引用来源,且此处关于百分比的原文表述并不准确(故译文不一定正确)。 (在 Talk:Btrfs 中讨论)

若改用 compress-force=算法[:级别] 挂载选项,Btrfs 会检查每次写入每个数据块并分别决定是否压缩。在多个混合用途系统上进行的实证测试表明,使用 compress-force=zstd 相比使用 compress=zstd 能显著提高空间节省率(从 10% 提升到 20%),但这会导致 CPU 占用率无端(略微)升高且拖长写入所需时间。官方 Btrfs 指导手册不推荐使用该选项。

只有在添加以上两个挂载选项之一后才会自动压缩新建或修改的文件。

提示:在新的 Btrfs 上安装 Arch Linux 时,如果需要将其压缩后存储,需要在系统安装前就使用 compress 选项挂载,例如:mount -o compress=zstd /dev/sdxY /mnt/,并注意在其 fstab 中为根目录文件系统指定相同的 compress 挂载选项。
为现有文件启用
[编辑 | 编辑源代码]

要为现有文件启用压缩,可使用 btrfs filesystem defragment -c算法 命令,算法 处可选填为 zliblzozstd,但使用此方式指定压缩级别尚未实现,因此采用默认值 3(lzo 不适用)。例如,执行以下命令可用 zstd 重新压缩所有文件:

# btrfs filesystem defragment -r -v -czstd /
警告:对具有 CoW 副本(例如快照副本和使用 cp 复制的文件)的文件进行碎片整理会生成两个不相关的文件,可能大幅增加磁盘使用量。

使用上面的方式压缩文件不具有持久性,其他写入操作将应用原有压缩设置。

以下两种方式可以持久性启用对单个文件的压缩:

$ chattr +c 文件
$ btrfs property set 文件 compression zstd

前一条命令使用的是从 ext2 文件系统继承的文件属性旧接口,灵活性不足,只能够使用默认的 zlib 压缩算法。后一条命令则可以指定压缩算法,但指定压缩级别尚未实现,因此采用默认值 3(lzo 不适用)。

查看压缩类型和压缩率

[编辑 | 编辑源代码]

compsize 接收一个包含文件或目录(可为整个 Btrfs 文件系统)的列表,输出其使用的压缩类型以及实际压缩率(压缩后大小/压缩前大小)。压缩前大小可能与其他程序(如 du(1))显示的数值不一致,因为每个 extent 都会被统计且只统计一次⸺即使有多个 reflink,或其部分内容已不再使用但尚未被垃圾回收。

-x 选项可限制在单一文件系统内执行,这在类似于 compsize -x / 的场景中非常有用,可以避免程序因尝试访问非 Btrfs 的子目录而导致整个运行的失败。

子卷

[编辑 | 编辑源代码]

“Btrfs 子卷是可独立挂载的 POSIX 文件树,而不是也不能看作是块设备。大多数其他 POSIX 文件系统只有一个可挂载根,而 Btrfs 不仅为整个卷(称作‘顶层子卷’)提供了可挂载根,每个子卷也拥有独立的可挂载根;一个 Btrfs 卷可以包含不止一个文件树⸺包含一个‘文件森林’。可以将 Btrfs 子卷理解为一个 POSIX 文件命名空间。”[7]

每个 Btrfs 文件系统都有一个 ID 为 5 的顶层子卷。这个子卷不能被移除或被其他子卷替代。顶层子卷在文件系统中的路径为 /,其他子卷则“嵌套”在顶层子卷之下。子卷(不包括顶层子卷)可以在文件系统中移动,可以嵌套在任意其他子卷下,即其路径可变,但子卷 ID 不可更改。

挂载文件系统时默认情况下挂载的是顶层子卷(除非更改默认子卷),可以通过选项来挂载指定子卷

子卷的主要用途之一是 #快照

更多详细信息请参阅以下链接:

创建子卷

[编辑 | 编辑源代码]

要创建子卷,必须先挂载其上层子卷。

# btrfs subvolume create 子卷父目录/子卷名

例如,在 /mnt 所在子卷下创建子卷 @sub(路径为 /mnt/@sub):

# btrfs subvolume create /mnt/@sub
注意:
  • 使用 --parents 选项可以自动创建不存在的父目录。
  • Btrfs 中子卷名可以是任何有效的目录名,以 @ 符号开头只是一个常见的命名约定。

列出子卷

[编辑 | 编辑源代码]

查看路径所属文件系统的所有子卷列表:

# btrfs subvolume list -t 路径

-t 选项会触发更易读的表格视图。

删除子卷

[编辑 | 编辑源代码]

删除子卷:

# btrfs subvolume delete 子卷路径

如果子卷包含其他子卷,需要添加 -R/--recursive 选项递归删除包含的所有子卷。子卷也可以当作常规目录删除(使用 rm -rrmdir)。

警告:确保在删除子卷之前未挂载该子卷。删除已挂载的子卷可能导致文件系统不一致。

挂载子卷

[编辑 | 编辑源代码]

将需挂载的子卷设置为默认子卷或使用 subvol=子卷路径(相对于顶层子卷)subvolid=子卷ID 标志挂载文件系统,可以将子卷像文件系统分区一样挂载。例如,可以将一个名为 subvol_root 的子卷挂载为 /。通过在顶层子卷下创建各种子卷,然后挂载到适当的挂载点,模拟传统的文件系统分区。因为子卷 ID 在恢复 #快照时可能会改变,建议使用 subvol 参数挂载子卷。

提示:
  • 子卷 ID 可通过 #列出子卷显示。
  • 不使用默认做法(将顶层子卷作为 /)可以简化子卷布局的更改。因此,考虑创建一个子卷挂载为 /
注意:
  • 如果要将子卷挂载为 /,需要在 rootflags 内核参数中指定挂载子卷的选项(一些引导加载程序可以自动添加,例如 GRUBgrub-mkconfig 命令),并编辑 /etc/fstab。或者,可以 #更改默认子卷
  • 译自 btrfs(5) § MOUNT OPTIONS:“大多数挂载选项应用于整个文件系统,只有第一个挂载的子卷的选项才会生效。这是由于实现缺失,将来可能会改变。这意味着(例如)你无法使用挂载选项为每个子卷单独设置 nodatacownodatasumcompress。这最终应该会被修复,但事实证明在 Linux VFS 框架内正确实现是困难的。”

有关目前及计划可以为每个子卷单独应用的挂载选项,请参阅 Btrfs Wiki FAQ

请参阅 Snapper#建议的文件系统布局Btrfs SysadminGuide#Managing SnapshotsSysadminGuide#Layout,了解使用子卷的示例文件系统布局。

有关 Btrfs 特有的挂载选项的完整列表,请参阅 btrfs(5) § BTRFS SPECIFIC MOUNT OPTIONS

更改默认子卷

[编辑 | 编辑源代码]

如果没有提供 subvolsubvolid 选项,将挂载默认子卷。要将子卷设置为所在文件系统的默认子卷,执行:

# btrfs subvolume set-default 子卷路径

或者使用:

# btrfs subvolume set-default 子卷ID 路径

路径所在文件系统的默认子卷设置为 ID 为子卷ID 的子卷。

提示:子卷 ID 可通过 #列出子卷显示。

使用 btrfs subvolume set-default 更改默认子卷后,顶层子卷需要使用 subvol=/subvolid=5 挂载选项挂载 [8]

注意:GRUBgrub-mkconfig 脚本遵守 Btrfs 的默认子卷设置⸺如果 / 是顶层子卷,始终不会指定 subvol=/subvolid=5 挂载选项,可能导致无法启动;如果 / 非顶层子卷,始终会指定相应的 subvol 选项,导致无法仅通过设置默认子卷回退(bug 报告)。目前可以通过手动修改 grub.cfgrootflags 内核参数解决(参见 #挂载子卷),或者使用工具自动在 GRUB 中为快照创建启动项(参见 #引导进入快照)。另外,如果 grub.cfg 的实际位置发生改变,例如包含 /boot 的子卷改变,需要再次运行 grub-install。详见这个论坛帖子

配额

[编辑 | 编辑源代码]
警告:尽管 Btrfs 的 Qgroups(配额组)已被认为在一般用途下是稳定的,但启用配额,尤其是在频繁或存在大量快照的设置中,仍可能导致性能下降,特别是在删除快照时。Qgroup 计账会引入额外的元数据开销,在快照操作频繁的系统中可能影响响应速度。

近年来的内核改进(5.15 及以后版本)已解决许多历史问题,但在某些高级用例中仍存在一些限制,例如深度嵌套的子卷或复杂的 Qgroup 层级结构。从 2016 年起提出的建议——在启用配额前应仔细评估你的使用场景——至今仍然适用。请务必查阅最新版的 Btrfs 文档 以了解当前详情。

Btrfs中的配额支持是通过使用配额组或 qgroup 在子卷级别实现的:默认情况下,每个子卷都以 0/subvolume_id 的形式分配配额组。 但是,如果需要的话,可以使用任意数字创建配额组。

要使用 Qgroup,你首先需要启用它:

# btrfs quota enable path

从此时开始,新创建的子卷将由这些配额组控制。 为了能够为已创建的子卷启用配额,首先正常启用配额,然后使用它们的 subvolume_id 为每个子卷创建一个配额组,再重新扫描它们:

# btrfs subvolume list path | cut -d' ' -f2 | xargs -I{} -n1 btrfs qgroup create 0/{} path
# btrfs quota rescan path

Btrfs 中的配额组形成树层次结构,其中 Qgroup 附加到子卷。大小限制由每个 Qgroup 独立配置且在并在包含给定子卷的树中达到任何限制时应用。

配额组的限制可以应用于总数据使用,非共享数据使用,压缩数据使用或全部。文件复制和文件删除可能都会影响限制,因为如果删除原始卷的文件并且只剩下一个副本,则另一个 Qgroup 的非共享限制可能会更改。例如,新快照几乎与原始子卷共享所有块,对子卷的新写入将向专用限制提升,一个卷中的公共数据的删除将升高到另一个卷中的专用限制。

要对 Qgroup 应用限制,请使用命令 btrfs qgroup limit。根据你的使用情况,使用总限制,非共享限制( -e)或压缩限制( -c)。

显示文件系统使用中给定路径的使用情况和限制:

# btrfs qgroup show -reF path

提交间隔

[编辑 | 编辑源代码]

将数据写入文件系统的频率由 Btrfs 本身和系统的设置决定。Btrfs 默认设置为 30 秒检查点间隔,新数据将在 30 秒内被提交到文件系统。 这可以通过在 /etc/fstab 增加 commit 挂载参数来修改:

LABEL=arch64 / btrfs defaults,noatime,compress=lzo,commit=120 0 0

系统范围的设置也会影响提交间隔。它们包括 /proc/sys/vm/* 下的文件,这超出了本文的范围,因此不再赘述。 它们的内核文档位于 https://docs.kernel.org/admin-guide/sysctl/vm.html

固态硬盘 TRIM

[编辑 | 编辑源代码]

Btrfs 文件系统能够从支持 TRIM 命令的 SSD 驱动器中释放未使用的块。可使用挂载参数 discard=async 启用异步丢弃(asynchronous discard)支持,linux 6.2 版本已经默认启用该功能。已释放的空间范围不会被马上丢弃,它们会被集中起来并在稍后由一个单独的工作线程进行 TRIM,这将能改善提交延迟。

异步丢弃可以安全地同定期TRIM使用[9]

有关启用和使用 TRIM 的更多信息,请参阅固态硬盘#TRIM

使用

[编辑 | 编辑源代码]

交换文件

[编辑 | 编辑源代码]
注意:跨设备文件系统上的交换文件不受支持,参见btrfs(5) § SWAPFILE SUPPORT以了解所有限制。

创建交换文件的正确方法是首先创建一个非快照子卷以存储交换文件,

# btrfs subvolume create /swap 
提示:考虑直接在顶层子卷上创建子卷,例如@swap。然后将子卷挂载/swap(或其他可访问路径)。

创建交换文件:

# btrfs filesystem mkswapfile --size 4g --uuid clear /swap/swapfile

如果未指定 --size 选项,交换文件默认大小为2 GiB。

启用交换文件:

# swapon /swap/swapfile

最后编辑fstab,添加交换文件的配置:

/etc/fstab
/swap/swapfile none swap defaults 0 0

更多信息参见Fstab#用法

注意:也可以手动创建交换文件,通过chattr设置整个子卷的No_COW属性,然后按照Swap#建立交换文件的内容创建交换文件。参见btrfs(5) § SWAPFILE SUPPORT

要使用休眠功能,除了配置交换文件外,你还需要完成电源管理/挂起与休眠中描述的额外步骤。

显示已使用的/空闲空间

[编辑 | 编辑源代码]

df(1) 这样的用户空间工具可能不会准确的计算剩余空间 (因为并没有分别计算文件和元数据的使用情况) 。推荐使用 btrfs filesystem usage 来查看使用情况。比如说:

# btrfs filesystem usage /
注意:btrfs filesystem usageRAID5/RAID6 设备上可能无法正常工作。

或者使用btrfs filesystem df在非root权限下快速检查已分配空间的使用情况:

$ btrfs filesystem df /

请参阅 [10] 以获取更多信息。

du(1)ncdu(1) 这类分析文件系统子集空间使用的工具同样受到影响,因为其没有考虑reflink,快照和压缩。可以考虑使用btduAURcompsize这类可识别btrfs特性的工具。

碎片整理

[编辑 | 编辑源代码]

Btrfs 支持通过配置挂载参数 autodefrag 来实现在线的碎片整理,参见 btrfs(5) § MOUNT OPTIONS 。要手动整理你的根目录的话,可以使用:

# btrfs filesystem defragment -r /

使用不带 -r 开关的上述命令将导致仅整理该目录的子卷所拥有的元数据。这允许通过简单地指定路径进行单个文件碎片整理。

警告:对具有 CoW 副本(快照副本或使用cp或 bcp 创建的文件)进行碎片整理以及使用带压缩算法的 -c 开关进行碎片整理可能会导致生成两个不相关的文件从而大幅增加磁盘使用量。

Btrfs 提供对 RAID 一类的 #多设备文件系统的原生支持。自修复冗余阵列和在线数据在线平衡是使btrfs RAID 与mdadm区分开来的重要特性。参阅 the Btrfs wiki page 获得更多信息。Btrfs 管理员手册提供了一些额外技术背景信息。

警告:奇偶校验 RAID(RAID 5/6)代码中存在多个严重的数据丢失错误。 检查 Btrfs 文档的 RAID5/6 页面 (英文) 和 BUG 汇报 (linux-btrfs 邮件列表 (英文)) 以获取更多信息。2020 年 6 月, 有人提出了一个 通俗易懂的现存问题列表 (英文) 和一个 有用的恢复指南 (英文)

检修 (Scrub)

[编辑 | 编辑源代码]

这篇文章的某些内容需要扩充。

Btrfs Wiki 术语表中写到,Scrub 是一种"在线文件系统检查工具"。它能读取文件系统中的文件和元数据,并使用校验值和 RAID 存储上的镜像区分并修复损坏的数据。

注意:运行 scrub 会阻止系统待机, 详见 这个讨论
手动启动
[编辑 | 编辑源代码]

启动一个(后台运行的)包含 / 目录的文件系统在线检查任务:

# btrfs scrub start /

检查该任务的运行状态:

# btrfs scrub status /
通过服务或者定时器启动
[编辑 | 编辑源代码]

btrfs-progs 软件包带有 btrfs-scrub@.timer 系统单元,用来每月运行 scrub 命令。通过添加挂载点的参数来启用它,例如btrfs-scrub@-.timer (/) 或者 btrfs-scrub@home.timer (/home)。你可以使用systemd-escape -p /path/to/mountpoint来转义路径,参见systemd-escape(1)

也可以通过启动 btrfs-scrub@.service 来手动运行 scrub (使用同样的转移后路径),相较 (以 root 用户身份运行) btrfs scrub,这么做的优点是输出内容会记录在 Systemd 日志中。

数据平衡 (Balance)

[编辑 | 编辑源代码]

"Balance 将会通过分配器再次传递文件系统中的所有数据。它主要用于在添加或删除设备时跨设备重新平衡文件系统中的数据。如果设备出现故障,余额将为冗余 RAID 级别重新生成缺失的副本。"[11]。参阅上游的 FAQ.

在单设备文件系统上,数据平衡对于(临时)减少分配但未使用(元)数据块的数量也是有用的。有时候这对于解决 "filesystem full" 故障来说也是必须的。

# btrfs balance start --bg /
# btrfs balance status /

快照

[编辑 | 编辑源代码]

"快照是和其它子卷共享数据和元数据的简单子卷, 利用了 btrfs 的写时复制特性。" 详见 Btrfs Wiki SysadminGuide#Snapshots

要创建一个快照:

# btrfs subvolume snapshot source [dest/]name

source为要创建快照的对象,[dest/]name为快照安放路径。

加入 -r 参数可以创建一个只读快照. 为只读快照创建一个快照可以获得一个只读快照的可写入版本.

注意:
  • 将一个只读快照通过btrfs property set -f -ts '/path/to/snapshot' ro false原位转换为可写快照是可能的。但并不推荐这样做,因为之后任何增量发送/接受(send/receive)操作会造成问题。创建一个新的可写快照可以预防这类问题。
  • 快照不是递归包含的,这意味着子卷内的子卷在快照里是空目录。

发送和接收

[编辑 | 编辑源代码]

可以通过 send 命令发送一个快照,通常会与 btrfs 中的 receive 组成管道.例如将快照 /root_backup (也许是/的备份) 发送到 /backup:

 # btrfs send /root_backup | btrfs receive /backup

只能发送只读快照,上面的命令在将子卷复制到外部设备 (例如备份驱动器) 时会很有用。

接受结束后将创建对应子卷,无需手动再创建。

还有一个例子创建了/mnt/arch-v2/subvolumes/@var子卷:

# btrfs send --proto 2 --compressed-data '/mnt/arch/snapshots/@var' | btrfs receive '/mnt/arch-v2/subvolumes/'

参数--proto 2--compressed-data在本例中能够更有效地发送快照(假设数据可压缩)。

也可以只发送两个快照间发生变化的部分,例如如果你已经发送了快照 root_backup ,然后又建立了一个新的只读快照 root_backup_new ,可以这样完成增量发送:

 # btrfs send -p /root_backup /root_backup_new | btrfs receive /backup

现在你 /backup 的快照会是 root_backup_new

参阅 Btrfs Wiki 上关于增量备份的页面 (英文) 获得更多信息 (例如使用工具自动化这一过程)。

去重

[编辑 | 编辑源代码]

使用写时复制,Btrfs 能够复制文件或整个子卷而无需实际复制数据。但是,无论何时更改文件,都会创建一个新的“真正的”副本。重复数据删除更进一步,通过主动识别含有相同内容的数据块并将它们通过 CoW 合并到同一个区(extend)内。

专用于 Btrfs 分区去重的工具包括 duperemovebees。也许你还希望基于文件的级别对数据进行重复数据删除,比如 rmlint-gitrdfindjdupesAUR 或者 dduper-gitAUR。有关这些程序的可用功能的概述和其他信息,请参阅上游 Wiki 条目 (英文)

调整大小

[编辑 | 编辑源代码]
警告:为避免数据丢失,确保在调整大小前备份好你的数据。

你可以增加文件系统的大小到设备的最大可用空间,或到一个指定大小。确保你在尝试增加文件系统大小前设备或逻辑卷有足够空间。 当指定一个设备上的文件系统到指定大小时,不论增加或减少,确保新的大小满足下面的条件:

  • 新的大小必须大于已有数据的大小,否则会导致数据损失。
  • 新的大小必须等于或小于当前设备的可用空间。
注意:如果你还计划减小文件系统所在的逻辑卷大小,请先减小文件系统大小后再尝试减小逻辑卷的大小。

将文件系统扩展到设备的最大可用大小:

# btrfs filesystem resize max /

将文件系统扩展到特定大小:

# btrfs filesystem resize size /

size 替换为你需要的大小(按字节计算)。也可以为大小指定单位,如K(千字节),M(兆字节)或G(吉字节)。 或者也可以通过在大小前添加前缀指定增加(+)或减少(-)的变化量:

# btrfs filesystem resize +size /
# btrfs filesystem resize -size /

已知问题

[编辑 | 编辑源代码]

一些在尝试之前应该知道的限制。

加密

[编辑 | 编辑源代码]

Btrfs 暂未内置加密支持,但当前正进行一项基于 Fscrypt英语Fscrypt 的加密集成工作 [12]

不过用户仍可在运行 mkfs.btrfs 前加密分区,具体参见 dm-crypt/加密整个系统。另一种方案是采用栈式文件系统加密

检查 btrfs 文件系统问题

[编辑 | 编辑源代码]

btrfs check 工具目前有一些已知问题,在继续深入阅读了解之前,您不应该直接运行它, 参见 #检查 Btrfs 文件系统小节。

提示和技巧

[编辑 | 编辑源代码]

无分区 Btrfs 磁盘

[编辑 | 编辑源代码]
警告:这种配置不建议用于启动设备,推荐设置一个单独的EFI 系统分区和Btrfs分区。 此外,GRUB 强烈反对安装GRUB到无分区磁盘。

Btrfs 能占用整个存储设备,使用子卷模拟分区表来替代 MBRGPT 分区表。虽然无分区磁盘不需要使用其它方法创建分区,然后在一个分区上创建 Btrfs 文件系统,但在单个磁盘上使用无分区配置存在一些限制:

  • 不能在同一磁盘上的不同分区上创建其它的文件系统
  • 受上一条影响,无法在该磁盘上创建EFI 系统分区。需要额外的储存设备用于 UEFI 启动。

运行下面的命令把整个设备的分区表替换成 Btrfs:

# mkfs.btrfs /dev/sdX

如果设备上存在分区表,则需要使用:

# mkfs.btrfs -f /dev/sdX

例如, 指定/dev/sda 而不是 /dev/sda1。后一种形式会格式化现有的分区而不是替换掉原有的分区表。由于根分区是 Btrfs 文件系统,请确保已将 btrfs 编译进内核, 或者将 btrfs 放入 Mkinitcpio#模块(MODULES)中并且重新生成 initramfs

像使用普通的 MBR 分区表存储设备一样安装引导加载程序, 参考 Syslinux#手动安装GRUB/技巧和窍门#安装到分区上或者无分区磁盘上。 如果你的内核因为 Failed to mount /sysroot. 错误无法启动, 请在 /etc/default/grub 里添加 GRUB_PRELOAD_MODULES="btrfs" 并重新生成 GRUB 配置文件 。

从 Ext3/4 转换

[编辑 | 编辑源代码]
警告:Btrfs 的邮件列表中报告了多起转换不完整/损坏/失败的案例。在开始之前请确定您有可用的备份并且愿意承担丢失数据的风险。 参见Btrfs维基的Convert页。

从安装 CD 启动,然后转化分区:

# btrfs-convert /dev/partition

挂载转换后的分区并修改 /etc/fstab 文件,指定分区类型 (type 为 btrfs,并且 fs_passno[最后一列] 要修改为0,因为 Btrfs 在启动时并不进行磁盘检查)。 还要注意的是分区的 UUID 将有改变,所以使用 UUID (指定分区) 时,请更新 fstab 中相应的条目。 chroot 到系统并重建你的引导加载程序(如果对此过程不熟悉,参考从现有 Linux 发行版安装 Arch Linux )。 如果转换了根文件系统,还需要在 chroot 环境中重建初始化内存盘以确保系统正确启动。

注意:
  • 如果转换过程中有任何异样,不管是无法挂载新转换的 Btrfs 文件系统或是无法往其中写入数据,只要备份子卷 /ext2_saved 还在,就可以进行回滚。请使用 btrfs-convert -r /dev/partition 命令进行回滚,这将会丢弃任何对新转换 Btrfs 文件系统的更改。
  • 如果挂载已转换的 ext 分区时出现如下错误:ERROR: dev extent devid 1 physical offset XXX len XXX is beyond device boundary XXX,那么在分区末尾预留一些空闲空间后,转换可能会成功。请先回滚转换,然后使用 resize2fs 将分区缩小 50000 个块,再扩大 5000 个块(从而创建出 45000 个空闲块)。然后再次尝试转换。参见 上游 bug 报告

确认没有问题后,通过删除 ext2_saved 备份子卷完成转换的最后一步。请注意,如果没了它 (备份子卷),你将没办法还原回 ext3/4 文件系统。

# btrfs subvolume delete /ext2_saved

最后通过数据平衡回收空间。

别忘了先前安装的一些应用需要额外设置来适配Btrfs。

注意:Ext3/4 转换到 Btrfs 的过程很耗时。一个普通机械硬盘上的4TB文件系统转换耗时可高达10小时。

损坏恢复

[编辑 | 编辑源代码]
警告:btrfs check 工具有一些已知问题,参见 #检查 Btrfs 文件系统 小节。

btrfs-check 不能在一个已挂载的文件系统上工作。为了能够在不从 Live USB 启动的情况下使用 btrfs-check,需要将其添加到初始内存盘:

/etc/mkinitcpio.conf
BINARIES=(btrfs)

然后重新生成 initramfs

之后如果启动时出现问题,则可以使用该实用程序进行修复。

注意:如果 fsck 进程必须使空间缓存 (和/或其他缓存?) 无效 (invalidate cache),那么随后的引导会挂起一段时间,这是正常的(进程可能会给出关于 btrfs-transaction 挂起的控制台消息)。系统应该在一段时间后从中恢复正常。

查阅 btrfs-check(8) 以获取更多信息。

引导进入快照

[编辑 | 编辑源代码]

要引导进入快照,因为快照可以像子卷那样被挂载,所以请像挂载子卷为根分区那样进行同样的流程 (已交代于#挂载子卷为根挂载点的一段中)。

  • 如果使用 GRUB,则可以在 grub-btrfsgrub-btrfs-gitAUR 的帮助下,在重新生成配置文件时使用 Btrfs 快照自动填充启动菜单。
  • 如果使用 rEFInd,则可以在 refind-btrfsAUR 的帮助下,启用 refind-btrfs.service后,在重新生成配置文件时使用 Btrfs 快照自动填充启动菜单。

搭配 systemd-nspawn 使用 Btrfs 子卷

[编辑 | 编辑源代码]

可查阅 Systemd-nspawn#使用Btrfs子卷作为容器的根Systemd-nspawn#在 systemd-nspawn 中运行 docker 等文章。

减少访问时间元数据的更新

[编辑 | 编辑源代码]

由于Btrfs的写时复制性质,访问文件就能够触发元数据的写时复制。减少访问时间的更新频率可能会消除这种不希望的硬盘使用并提高性能。参见Fstab#atime 参数

增量备份到外置设备

[编辑 | 编辑源代码]

下列包利用 btrfs sendbtrfs receive 将备份增量发送到外置设备上。 参考它们的文档来查看实现,功能和需求的不同。

  • btrbk — Btrfs子卷的快照和远程备份工具
https://github.com/digint/btrbk || btrbk
  • snap-sync — 使用Snapper快照并备份到外置设备或远程机器
https://github.com/wesbarnett/snap-sync || snap-sync

下列工具运行备份snapper快照到非Btrfs文件系统。

  • snapborg — 一个类似borgmatic的工具,将snapper快照与borg整合起来
https://github.com/enzingerm/snapborg || snapborgAUR

自动快照

[编辑 | 编辑源代码]

要管理并自动创建快照,你可以使用像SnapperTimeshiftYabsnap这样的快照管理器。

自动通知

[编辑 | 编辑源代码]

桌面通知能帮助您及时察觉严重的 Btrfs 问题,相较于完全不提供通知,能提供更好的问题感知。

btrfs-desktop-notificationAUR 为以下事件提供桌面通知:

  • 启动进入任何只读快照或系统时
  • dmesg 日志中出现 Btrfs 警告、错误或致命消息

更多信息及配置方法,请参见 https://gitlab.com/Zesko/btrfs-desktop-notification

疑难解答

[编辑 | 编辑源代码]

请查阅 Btrfs 疑难解答Btrfs 常见问答集(英文)以获得排除一般问题的信息。

分区偏移

[编辑 | 编辑源代码]
注意:当您试图将 core.img 嵌入到已分区磁盘上时,可能会发生偏移问题。这意味着可以将 GRUB 的 corg.img 直接嵌入到无分区磁盘 (例如 /dev/sdX) 上的 Btrfs 存储池中。

GRUB 可以引导启动 Btrfs 分区,但是因为模块可能会比其它文件系统大,grub-install 生成的 core.img 文件超过了 MBR 与第一个分区之间的空间大小 (63 扇区/31.5KiB)。最新版的 fdiskgdisk 等磁盘工具会通过第一个分区前空出 1-2MiB 的空间来避免此问题。

根丢失

[编辑 | 编辑源代码]

本文或本章节的事实准确性存在争议。

原因: Suggests editing a non-configuration file manually.(在 Talk:Btrfs#Should not suggest to edit files in /usr/share 中讨论)


当从一个RAID配置的系统中启动用户可能会遇到错误:error no such device: root。编辑/usr/share/grub/grub-mkconfig_lib,移除行echo " search --no-floppy --fs-uuid --set=root ${hints} ${fs_uuid}"中的两个引号。重新生成grub的配置文件,后系统应该能够正常启动。

警告:不建议覆盖软件包提供的文件,因其会在下次更新时被覆盖。

挂载超时

[编辑 | 编辑源代码]

有时(尤其在大型 RAID1 阵列上)系统启动时会出现挂载超时的现象,并带有如下日志信息:

Jan 25 18:05:12 host systemd[1]: storage.mount: Mounting timed out. Terminating.
Jan 25 18:05:46 host systemd[1]: storage.mount: Mount process exited, code=killed, status=15/TERM
Jan 25 18:05:46 host systemd[1]: storage.mount: Failed with result 'timeout'.
Jan 25 18:05:46 host systemd[1]: Failed to mount /data.
Jan 25 18:05:46 host systemd[1]: Startup finished in 32.943s (firmware) + 3.097s (loader) + 7.247s (kernel)>
Jan 25 18:05:46 host kernel: BTRFS error (device sda): open_ctree failed

这可以通过 fstab 中特定的 systemd 挂载选项 x-systemd.mount-timeout 提供系统以更长的超时时间来轻松解决。例如:

 UUID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx  /data  btrfs  rw,relatime,x-systemd.mount-timeout=5min  0 0

出现 BTRFS: open_ctree failed 错误

[编辑 | 编辑源代码]

本文或本章节的事实准确性存在争议。

原因: 内容过时。自从systemd和udev都使用systemd-udevd后,用systemd替换udev钩子没有任何意义。(在 Talk:Btrfs 中讨论)


截至 2014 年 11 月,一个似乎存在于 systemdmkinitcpio 中的 Bug 可能会导致在 mkinitcpio.conf 中使用 btrfs 钩子(hook)的用户在启动多设备文件系统的 Btrfs 卷时遇到以下错误:

BTRFS: open_ctree failed
mount: wrong fs type, bad option, bad superblock on /dev/sdb2, missing codepage or helper program, or other error

In some cases useful info is found in syslog - try dmesg|tail or so.

You are now being dropped into an emergency shell.

一种解决办法是,在/etc/mkinitcpio.conf中,将 HOOKS 中的 btrfs 移入 MODULES 中,然后重新生成 initramfs 并重新启动。

另外,如果在挂载 RAID 卷组缺少某个卷时,也有可能会发生这个错误。这种情况下,需要把 degraded 加入到 /etc/fstab 中;如果根目录在卷组上,需要同时加入内核参数 rootflags=degraded

接上文,截至 2016 年 8 月,针对这一问题,一个可能的解决方案是在 /etc/fstab 中仅靠单个硬盘来挂载阵列,然后让 Btrfs 自动发现并追加其它硬盘。UUID 和 LABEL 等基于组的标识符似乎是导致出现错误的原因。比如说,由“disk1”(磁盘 1) 和“disk2”(磁盘 2)组成的双设备 RAID1 阵列会被分配到一个 UUID。但是请在 /etc/fstab 中只使用 /dev/mapper/disk1 来指定磁盘阵列,而不要使用 UUID。更多解释,参见这个博客文章

另一个可能的解决方法是在 mkinitcpio.conf 中移除 udev 钩子并替换为 systemd 钩子。此时 btrfs 不应出现在 HOOKSMODULES 列表中。

请查阅原论坛讨论FS#42884 获得更多的讨论内容和信息。

检查 Btrfs 文件系统

[编辑 | 编辑源代码]

本文或本节内容已经过时。

原因: "大开发阶段"状态已经过时。 (在Talk:Btrfs讨论)
警告:Btrfs(特别是 btrfs check 命令工具)仍处在大开发阶段,强烈建议在加上 --repair 参数运行 btrfs check 前先做一个备份,并提前查阅 btrfs-check(8)

btrfs-check(8) 可以检查并修复一个未挂载的 Btrfs 文件系统。但是由于它尚未开发完成,它并不能修复某些错误(即使这些错误没有导致文件系统无法挂载)。

持续的硬盘活动

[编辑 | 编辑源代码]

内核版本6.2开始, mount(8) 默认启用 discard=async 选项。 这个设置被报告会造成硬盘的持续活动(甚至是待机状态下),因为丢弃队列填充的速度快于其处理速度。这会造成电源使用增加,特别是NVMe设备。

到了内核版本6.3后,问题的解决办法是将iops_limit默认值从100改为1000。在旧版内核上,你可以手动设置到一个需要的值,例如:

# echo 1000 > /sys/fs/btrfs/uuid/discard/iops_limit

其中uuid是btrfs文件系统的UUID。限制值1000需要通过实验进行调整。

要在启动时设置该参数,可能要用到Systemd#systemd-tmpfiles - 临时文件,例如创建下列文件:

/etc/tmpfiles.d/btrfs-discard.conf
w /sys/fs/btrfs/uuid/discard/iops_limit - - - - 1000

或者在fstab中使用nodiscard挂载选项禁用异步丢弃功能,使用定期TRIM来替代。

Device total_bytes should be at most X but found Y

[编辑 | 编辑源代码]

若驱动器从其他计算机迁移或设备顺序变更,且报告的大小差异极小(至多几MB),可能是启用了 HPA(主机保护区域)

可使用 hdparm 验证 HPA 是否启用:

# hdparm -N 设备

输出将显示两个数值:可见扇区数与实际扇区数。若两者不同则表明HPA已启用。

若主板强制设置此功能且固件未提供关闭选项,唯一解决方案是收缩受影响文件系统。此操作最易在原始计算机或未应用HPA的机器上执行。

设备空间不足

[编辑 | 编辑源代码]

博客文章《解决Btrfs文件系统空间不足问题》建议并解释了以下检查步骤:

  1. 立即清理空间(删除历史快照)
  2. 文件系统是否真满?元数据和/或数据块分布不均(运行 btrfs balance
  3. 文件系统是否真满?数据块分布不均
  4. 文件系统是否真满?元数据分布不均
  5. 因空间不足无法执行平衡操作(运行 btrfs balance 前,先用 btrfs device add 临时添加设备如U盘或环回设备)

另可参阅 btrfs 元数据满了怎么办 - 依云's Blog

有关 ENOSPC(“磁盘空间不足”)的最新处理指南,请参阅 ENOSPC - No available disk space | Forza's Ramblings

另请参阅

[编辑 | 编辑源代码]