- Btrfs 将文件系统、RAID 和卷管理器与写时复制 (COW)、快照、压缩和原生校验和功能结合在一起。
- 子卷是 Btrfs 中的逻辑系统,允许您分离 /、/home、/var 并按区域管理快照。
- 多设备支持允许您在线添加/删除磁盘、更改 RAID 配置文件和平衡数据。
- snapper 或 btrbk 等工具利用了 Btrfs 快照,但对于真正的备份,仍然需要外部副本。

如果你之前用的是ext4 或 XFS,现在看到 Btrfs感到困惑,这完全正常:它并非“又一个文件系统”,而是文件系统、RAID 和卷管理器的结合体。当你开始接触子卷、快照、负载均衡或数据清理等概念时,一切看起来都很复杂,但实际上它们都是可以无缝衔接的组件。
本文将探讨如何使用 Btrfs 子卷及其在典型安装环境(例如 Arch、Debian、openSUSE 或 Synology/QNAP NAS 设备)中的用途,以及它们与快照、RAID、压缩的关系,并分析它们对磁盘空间的影响。您还将看到创建文件系统、子卷、快照和管理多个磁盘的实际命令示例,以及一些常见问题(例如臭名昭著的 ENOSPC 错误导致的“可用”空间问题)。
Btrfs是什么?它有何不同之处?
Btrfs 是 GNU/Linux 的下一代文件系统,旨在从单个SSD扩展到具有冗余、快照和压缩功能的多磁盘池。它自 2.6.29 版本起已集成到内核中,虽然其开发仍在进行中,但磁盘格式已经稳定,因此今天创建的 Btrfs 系统在未来的内核中仍然可读。
与 ext4 或 XFS等传统文件系统不同,Btrfs 集成了以前分散在文件系统、软件 RAID (mdadm) 和逻辑卷管理器 (LVM) 中的功能。只需一个堆栈,即可实现多设备管理、RAID 配置、快照、子卷、完整性检查等功能。
Btrfs 的关键原则:写时复制 (COW)、快照、RAID 和校验和
Btrfs 的核心是写时复制 (COW) 模型。当您修改数据时,系统不会覆盖原有位置的数据,而是将新数据块写入其他位置并更新引用。这使得文件和子卷的克隆几乎可以瞬间完成,并且快照在时间和空间方面都非常经济。
这种设计依赖于每个数据块和元数据块自身的校验和(通常为 CRC32C)。读取数据块时,Btrfs 会检查校验和;如果校验和不匹配且存在另一个副本(例如,在 RAID 1 或 RAID 10 中),它会读取健康的副本并实时修复错误。这可以防止静默损坏,而 ext4 本身无法检测到这种损坏。
此外,Btrfs 内置支持多种RAID 配置,例如 RAID0、RAID1、RAID10、RAID5 和 RAID6,数据 (-d) 和元数据 (-m) 的配置可以分别进行。其内部机制并非像 mdadm 那样采用传统的块 RAID,而是由文件系统自身管理的“块”级别的复制和条带化。
最后,Btrfs 可以应用透明的即时压缩(zlib、lzo、zstd):数据在写入前进行压缩,读取时进行解压缩,整个过程对应用程序完全透明。这可以减少磁盘空间占用,并且在许多情况下,甚至可以加快在慢速磁盘或机械磁盘上的读取速度。
创建 Btrfs 文件系统
要开始使用子卷,首先需要在设备上安装 Btrfs 文件系统。基本创建过程与任何 mkfs 文件系统非常相似:
mkfs.btrfs /dev/sdb
此命令使用 Btrfs 文件系统初始化 /dev/sdb,不启用 RAID 或任何其他附加功能。您可以使用整个磁盘或分区。然后,您可以将其挂载到指定挂载点,例如:
mount /dev/sdb /mnt/btrfs
如果想要从一开始就构建一个多设备系统,只需在创建命令中添加多个设备即可:
mkfs.btrfs /dev/sdc /dev/sdd
在这种情况下,Btrfs 会创建一个逻辑池,其容量为所有设备的聚合容量(默认情况下,数据采用“single”格式,元数据采用 RAID1 类型的镜像,除非您另行指定)。挂载时,您只需指定其中一个设备,例如 /dev/sdc。
原生 RAID 配置和动态池管理
创建文件系统时,可以使用 `-dy` 和 `-m` 选项指定数据和元数据的冗余配置:例如 raid0、raid1、raid10、raid5、raid6 或 single。Btrfs 中 RAID1 的一个典型示例是:
mkfs.btrfs -d raid1 -m raid1 /dev/sdc /dev/sdd
与传统的 RAID 1 不同,Btrfs并不保证数据块之间完全镜像。它保证每个数据块或元数据在不同的设备上至少有两个副本,但并不存在真正意义上的“孪生”磁盘,并且允许使用不同大小的磁盘。
系统挂载完成后,您可以使用以下命令在系统处于热状态时向存储池中添加和删除磁盘:
btrfs device add /dev/sdb /mnt/btrfs
btrfs device delete /dev/sdc /mnt/btrfs
添加设备后,通常建议执行数据负载均衡,将现有数据块重新分配到所有磁盘上:
btrfs filesystem balance /mnt/btrfs
此平衡过程会根据所选的 RAID 配置重新分配数据块,从而使所有磁盘协同工作,并更均匀地利用空间。这是一个在线操作:文件系统在运行期间始终可访问。
如何在不重新安装系统的情况下从单盘切换到 RAID 1?
Btrfs 的一个优点是,无需卸载或重新安装即可将现有系统转换为不同的 RAID 配置。假设您的 Btrfs 根目录位于单个磁盘 /dev/sda1 上,现在您想通过添加 /dev/sdb1 来切换到 RAID 1。
具体步骤是:添加新设备并运行负载均衡和配置文件转换:
btrfs device add /dev/sdb1 /
btrfs balance start -dconvert=raid1 -mconvert=raid1 /
在此过程中,Btrfs 会重新定位数据块,以便所有数据和元数据在两个磁盘上都得到复制。完成后,“btrfs filesystem show /” 将显示两个使用情况相似的设备,“btrfs filesystem df /” 将显示数据、元数据和系统的 RAID1 配置。
使用 Btrfs 进行 RAID 降级和修复
如果原生 RAID1 配置中的一块磁盘发生故障,系统将进入降级模式。在 Btrfs 文件系统中,要设置此系统,您需要以下选项:
mount -o degraded /dev/sdb /mnt/btrfs
在系统处于这种状态下,Btrfs 将继续从正常副本提供数据,但无法实现完全冗余。正确的做法是添加一块新磁盘,从存储池中移除缺失的设备,然后运行负载均衡来重新复制数据块。
如果出现局部损坏或磁盘问题,您还可以选择运行 scrub 命令,该命令会扫描所有块,检查校验和,并在可能的情况下从正常副本进行修复:
btrfs scrub start /mnt/btrfs
btrfs scrub status /mnt/btrfs
该清理操作会在系统挂载时在后台运行,因此最好在非高峰时段进行,尤其是在服务器或 NAS 设备上。
透明压缩:zlib、lzo、zstd
使用`compress` 或 `compress=<something>`选项设置系统,即可启用从该点开始自动压缩所有写入的数据。例如:
mount -o compress=zstd /dev/sdb /mnt/btrfs
系统会将压缩后的数据块存储在磁盘上,但运行 `ls` 或 `stat` 命令时,仍然会看到未压缩的逻辑大小。这种优势在使用 `df`(占用空间更少)或 `btrfs 文件系统 df` 等工具,或者使用 `compsize` 等实用程序时尤为明显。
可用的算法在压缩比和 CPU 使用率之间提供了不同的平衡:
- ZLIB:压缩率很高,但速度较慢;适用于非常静态的数据。
- 伊佐速度非常快,但压缩率较低;对于写入量很大的系统来说是一个不错的选择。
- 标准:一种非常有趣的平衡,具有多个可配置级别。
您还可以在挂载过程中调整压缩级别,例如,使用 zstd:1 优先考虑速度,或使用 zstd:15 以牺牲 CPU 使用率为代价来最大化压缩。这些参数会反映在 /proc/mounts 文件中,并在系统挂载时显示在 dmesg 日志中。
文件写入时复制:使用引用链接实现即时克隆
例如,`cp` 命令的 `--reflink=always` 选项可以启用文件级写时复制 (COW) 机制。当使用 reflink 将大文件复制到 Btrfs 文件系统时,数据不会在磁盘上重复写入;只会创建对相同数据块的额外引用。
cp --reflink=always imagen.iso copia1.iso
副本会立即生成,并且最初不占用任何额外空间。只有当其中一个文件被修改时,受影响的数据块才会被物理复制(真正的写时复制)。这对于虚拟机模板、基础镜像等非常有用。
同样的逻辑也适用于子卷快照,它们本质上是整个子卷的写时复制(COW)副本。创建时,它们几乎不使用任何元数据;实际的存储消耗是在之后,取决于原始卷和快照之间的内容差异。
Btrfs 子卷:它们是什么以及它们的用途
在 Btrfs 中,子卷实际上是主系统内部的一个“迷你文件系统”。完整的 Btrfs 系统始终至少包含一个子卷(顶层子卷),并且可以在其之上创建其他子卷,这些子卷从外部看起来就像普通的目录。
它的优势在于每个子卷都可以独立挂载,拥有自己的快照、配额(启用配额系统后)和备份策略。所有这些都与父卷共享同一个存储池和 RAID 配置。
要在已挂载到 /mnt/btrfs 的 Btrfs 文件系统上创建多个子卷,您可以这样做:
btrfs subvolume create /mnt/btrfs/subvolumen1
btrfs subvolume create /mnt/btrfs/subvolumen2
btrfs subvolume create /mnt/btrfs/subvolumen3
这些子卷将显示为 /mnt/btrfs 中的目录,但如果您使用 btrfs 列出它们,您将看到它们的ID 和内部路径:
btrfs subvolume list /mnt/btrfs
一旦你获得了子卷 ID(例如,子卷 1 的 ID 为 256),你就可以在另一个路径中挂载该子卷:
mount -o subvolid=256 /dev/sdb /mnt/subvol1
这样,该子卷上的数据可以同时以/mnt/btrfs/subvolume1 和 /mnt/subvol1 的形式访问,但内容完全相同。这对于分离 /、/home、/var 或特定数据集非常有用,而无需采用传统的分区方式。
子卷设计:实际案例(Arch、Debian、NAS)
台式机中一个非常典型的结构可能是:
- @ 根目录 (/),不包含 /home 或 /var/lib/docker。
- @家 将您的个人数据发送到 /home。
- @var 或者特定的子卷,例如 @var-log、@var-cache 等。
- @快照 按日期整理存储快照。
这样一来,像snapper 这样的系统快照工具、Timeshift 或 btrbk等工具就可以只创建系统(子卷 @)的快照来回滚更新,而用户文档(在 @home 中)则受到保护或使用不同的逻辑进行同步。
在群晖或威联通NAS上,实际发生的是,共享文件夹创建在Btrfs子卷上,从而允许快照复制功能对这些文件夹进行快照、克隆或复制到另一台NAS。原理相同,只是以图形界面呈现。
子卷快照:秒内即可回滚和复制
Btrfs 中的快照实际上就是从另一个子卷创建快照。要在另一个子卷中创建 /mnt/btrfs/subvolume1 的快照,请使用 snapshot_sub1:
btrfs subvolume snapshot \
/mnt/btrfs/subvolumen1 \
/mnt/btrfs/subvolumen1_snapshot
如果运行 `btrfs subvolume list` 命令,您将看到此快照的新 ID 和路径。从那时起,它们在逻辑上是独立的:对其中一个的更改不会影响另一个,即使在它们内部共享数据块,直到被修改为止。
这既可用于快速备份(例如,在系统升级之前),也可用于实验室、测试、开发环境等。恢复快照通常涉及挂载快照并将其用作新的默认子卷,或者将数据复制回去。
在多个磁盘上使用 Btrfs 文件系统并启用 RAID
使用 Btrfs,多磁盘管理直接在文件系统层面上进行。创建卷时,您可以为数据和元数据定义不同的配置文件,例如,如果您有三个或更多设备,则可以将数据放在 RAID 5 中,将元数据放在 RAID 1 中。
mkfs.btrfs -d raid5 -m raid1 /dev/sdb /dev/sdc /dev/sdd
这种类型的 RAID 配置为数据提供条带化和奇偶校验(提高了读取速度和容错能力),并为元数据提供强大的冗余,但会占用一些额外的磁盘空间。然而,Btrfs 中的 RAID 5/6 历来存在严重的缺陷;在关键环境中,许多管理员仍然更倾向于使用 RAID 1/10。
如果您最初创建的是 RAID1 池,之后想要更好地利用空间,您可以随时通过均衡功能动态转换配置文件:
btrfs balance start -dconvert=raid5 -mconvert=raid1 /srv
同样,添加或移除设备使用`btrfs device add/remove`命令,然后进行负载均衡以重新分配数据块。`btrfs filesystem show` 命令提供池中每个磁盘的状态、大小和使用情况概览。
空间管理:为什么 Btrfs 中的 du 和 df 是“谎言”?
Btrfs 最令人困惑的方面之一是,传统的 df 和 du 工具无法准确反映实际情况。由于混合使用了块级 RAID、写时复制 (COW)、快照和压缩等技术,因此“写入 1 MiB 等于使用 1 MiB”的假设不再成立。
在 Btrfs 文件系统中,存储空间以大块的形式分配,通常数据块为 1 GiB,元数据块为 256 MiB。每个块都分配有一个对应的配置:单盘、RAID 1、RAID 10 等。在单个块内,写入的 1 MiB 数据确实等于使用的 1 MiB,但由于多个块和数据块副本的组合方式,这种简单的计算变得复杂。
此外,对于写时复制 (COW) 和快照,删除文件并不能保证其占用的空间会被释放:如果这些空间被其他快照或克隆链接共享,它们将继续占用空间,直到没有对它们的引用为止。因此,就会出现“我删除了东西,空间却不会释放”的情况。
因此,Btrfs 提供了诸如“btrfs filesystem df <path>”之类的特定命令,该命令按配置文件显示数据、元数据和系统使用情况的细分,以及“btrfs filesystem show <device>”之类的命令,该命令显示每个磁盘的哪些部分被保留以及哪些部分被原始使用。
日常使用子卷和快照的最佳实践
例如,如果您要在单个固态硬盘上安装带有 Btrfs 文件系统的Arch Linux,强烈建议您采用以下策略:
- 在根分区上创建一个唯一的 Btrfs 文件系统。
- 为 /(系统)、/home、/var/log、/var/lib/libvirt 或 /var/lib/docker 定义单独的子卷。
- 使用适当的压缩选项(zstd 或 lzo)挂载每个子卷,如果需要,对包含许多小文件的加载进行自动碎片整理。
- 使用 Snapper、Timeshift 或 btrbk 来 自动创建系统子卷快照 更新前后的对比。
在配备多块固态硬盘的物理机上,您可以选择使用单个多设备 Btrfs 阵列(支持 RAID 1/10 和逻辑子卷),或者使用多个独立的 Btrfs 阵列。就子卷而言,两者差别不大:它们仍然是各自所在的 Btrfs 阵列中的逻辑实体。关键在于从一开始就明确定义哪些数据需要能够独立进行快照和回滚。
关于完整备份,请记住快照并非外部备份。Snapper 或 Timeshift 通常用于本地快照(主要是系统快照),而要真正备份所有内容(包括 /home 目录),最好使用 borg、restic、rsync、远程 btrbk 或 NAS 自带的快照/复制解决方案等工具。
Btrfs 的基本管理和实用命令
Btrfs 命令行界面可能看起来有点冗长,但它允许您使用唯一的前缀来缩写子命令。例如,如果没有歧义,您可以使用“btrfs fi de /”来代替“btrfs filesystem defragment /”。
您将经常用到的一些基本命令包括:
- mkfs.btrfs创建文件系统,支持 RAID 选项和标签。
- btrfs 文件系统显示/df查看设备、使用情况和配置文件。
- btrfs 设备添加/删除:向存储池中添加或删除磁盘。
- btrfs 平衡启动:重新平衡数据并转换 RAID 配置文件。
- btrfs scrub 开始/状态:验证并修复带有校验和的数据块。
- btrfs 子卷创建/列出/删除/快照管理子卷和快照。
- btrfs 检查:离线检查文件系统(谨慎操作)。
还需注意的是,不应在每次启动时都对 Btrfs执行传统的 fsck 命令;在 fstab 文件中,Btrfs 条目的最后一个字段应为 0,以避免出现不正确的自动检查。
借助这套概念和命令,Btrfs 不再是那个“奇怪的黑盒子”,而变成了一个非常灵活的工具,您可以使用子卷对系统进行逻辑组织,利用真正有用的快照,并将所有这些与冗余和压缩结合起来,无论是在简单的笔记本电脑上还是在多磁盘池上。
对字节世界和一般技术充满热情的作家。我喜欢通过写作分享我的知识,这就是我在这个博客中要做的,向您展示有关小工具、软件、硬件、技术趋势等的所有最有趣的事情。我的目标是帮助您以简单而有趣的方式畅游数字世界。
