Linux Btrfs 文件系统使用指南

2021-08-25 730点热度 0条评论

Linux 支持诸多的文件系统,而这些文件系统有着不同的优点和缺点,最经典的 Linux 文件系统一定是 EXT 系列,它们在以前几乎是每个 Linux 发行版的默认文件系统,也是目前为止最稳定的 Linux 文件系统之一。

但是随着硬件的进步以及用户对文件系统更加多元化的需求,越来越多的 Linux 发行版和用户开始使用像 XFS、Btrfs、ZFS 这些新型文件系统作为其默认文件系统,它们比  EXT 系列功能更丰富,性能更好,对新硬件也更加友好。

熟悉小山的都知道我一直在使用 ArchLinux,而我刚开始使用的文件系统是 EXT4,没过多久切换到了 XFS,用了好长一段时间,感觉也不错。但是某天我想要一个有快照功能的文件系统,可是 XFS 并不支持快照,而且据我所知支持快照并且流行的文件系统只有 ZFS 和 Btrfs。熟悉 Linux 的都知道 ZFS 因为开源许可证没有添加到 Linux 主线内核,想要使用还需要安装树外模块,不是很方便。我也不是很需要 ZFS 提供的其他功能以及性能,所以就选择了 Btrfs。

因为 Btrfs 有着诸多新特性,比如:子卷、快照、压缩、写时复制,用起来和其他文件系统还是有很大不同的,虽然你也可以像其他文件系统那样直接格式化使用,但那样显然不是最佳实践。

Btrfs

这篇文章我会根据使用体验,讲解一下 Btrfs 文件系统的几个核心功能,除了 Btrfs RAID,建议使用之前将 Linux 内核版本更新到 4.19 或以上以获得更好的体验。

Btrfs 格式化命令:mkfs.btrfs ${partPath}mkfs.btrfs -n ${nodesize} ${partPath} (指定节点大小),${partPath} 替换为你要格式化的分区路径,而指定节点大小是可选的,较低的节点大小会增加碎片、降低锁定争用,较高的节点大小会以增加内存操作为代价减少碎片。默认值为 16k (btrfs-progs 3.11 之前为 4k),最大允许值为 64k,值必须是扇区大小倍数和 2 的幂。

如果你当前的文件系统是 EXT3/4,可以使用btrfs-convert工具将其转换为 Btrfs。详情查看:https://btrfs.wiki.kernel.org/index.php/Conversion_from_Ext3

格式化之后不要着急使用,先了解几个核心功能。

Btrfs 分区的大多数操作都由btrfs工具完成,具体用法:https://btrfs.wiki.kernel.org/index.php/Manpage/btrfs

子卷 (SubVolume)

子卷类似 LVM 的逻辑卷,但它并不是块设备,可以看作是一个独立的目录(专业点叫做 POSIX 命名空间),在文件结构中也表现为一个目录。子卷之间独立且互不影响,任何子卷可以被挂载到任何位置。

Btrfs 分区默认都有一个被称作顶级子卷的子卷。新子卷可以创建在任何子卷的下面。

创建子卷命令:sudo btrfs subvolume create ${subvolumePath}${subvolumePath}是新子卷路径。

可以使用subvolidsubvol挂载选项挂载对应子卷,前者使用子卷 ID,后者使用子卷路径(相对于顶级子卷)。如果不指定任何子卷挂载选项,则使用默认子卷,默认是顶级子卷,可以被修改,如果你修改了默认子卷,需要通过顶级子卷的 ID 挂载顶级子卷,顶级子卷的 ID 是 5。推荐使用子卷 ID 挂载子卷,因为这样即使子卷被移动,也可以被正确的挂载,可以使用sudo btrfs subvolume list /命令查看子卷列表。

子卷的使用方式一般分为嵌套子卷和平行子卷,或者两者混用,我更偏向两者混用。

假设有一块硬盘需要分区并安装 Linux,很多人喜欢给系统文件和家目录放在不同的分区里,一般的做法是创建两个分区或者使用 LVM 逻辑卷,但现在我们可以使用一个 Btrfs 分区,然后使用子卷做到这一点。并且子卷相对于普通分区,并不需要担心某个分区空间不足的情况。

嵌套子卷:嵌套子卷就是子卷间是相互嵌套的,因为子卷在文件结构中是一个目录,所以嵌套子卷的管理就像管理目录一样简单,并且支持自动挂载,但相对缺少灵活性。挂载顶级子卷然后安装系统,此时顶级子卷被挂载在/,包含所有的系统文件,当然也包含家目录。不过我们只需要删除或者移动现有的/home目录,然后在/home创建一个子卷:sudo btrfs subvolume create /home,由于是嵌套子卷,不需要手动挂载。此时顶级子卷下的home目录是一个单独的子卷,如果你为顶级子卷创建/还原快照,并不会影响home子卷。

平行子卷:平行子卷就是将所有子卷创建在顶级子卷下面,然后手动把每个子卷挂载到对应位置,这种方式管理起来相对方便,但是操作会复杂一些。首先挂载顶级子卷,然后创建roothome两个子卷,然后卸载顶级子卷,接着把root子卷挂载到/home子卷挂载到/home。虽然挂载后的目录结构看着和嵌套子卷一样,但home子卷并不是root子卷下面,而是在顶级子卷下面,只是被挂载到了/home

两者混用就是在平行子卷的基础上使用嵌套子卷,比如我可以在home子卷里创建个vms子卷,由于是在home子卷下面,所以挂载home子卷的时候会自动挂载它。

删除子卷可以使用sudo btrfs subvolume delete ${subvolumePath}命令,从 Linux 内核 4.18 开始,删除子卷目录也可以删除子卷。

如果你不需要快照功能,其实怎样使用子卷都问题不大。

Btrfs 子卷并不能替代 LVM 逻辑卷,它缺少 LVM 一些功能,以及它不是块设备。

我已经尽可能的将子卷如何使用给大家进行了讲解,如果还有不懂的地方,可以查看官方的子卷指南:https://btrfs.wiki.kernel.org/index.php/SysadminGuide#Subvolumes

写时复制 (CoW)

Btrfs 支持拷贝文件时不拷贝数据而只创建引用链接,引用链接和硬链接类似,都是不同文件指向同一份数据,和硬链接不同的是,引用链接不共享源文件的 Inode,所以修改引用链接并不会影响源文件。

只有当其中一个文件被修改时,另一个文件的数据会被复制并写入到新的块,这就是写时复制,只有当相同项目的某一项被修改时才会创建新的副本。

如果一个文件的数据不会被更改,即使创建几万个引用链接,它们实际占用的只是一个源文件的大小(以及引用链接的元数据大小)。

对于某些重复文件很多的场景,比如 Wine,引用链接可以显著减少空间的实际占用。

某些文件管理器支持复制文件时引用链接,如果你需要手动复制文件为引用链接,只需在cp命令加上--reflink=always参数即可。

写时复制虽然可以减少空间占用,但也会影响性能,对于某些写入较为频繁的目录或文件,比如虚拟机的虚拟硬盘,可以使用chattr命令给文件或目录添加C属性,添加此属性后默认会禁用写时复制,只有当存在引用链接时才会使用写时复制。目录添加此属性后默认会影响新创建的文件,建议为需要此属性的目录单独创建一个子卷。你还可以通过使用nodatacow挂载选项禁用写时复制。

压缩 (Compress)

Btrfs 支持透明和自动数据压缩,文件写入时会自动压缩,文件读取时会自动解压,此特性以较少的 CPU 使用率换取较高的空间利用率。

默认不会启用数据压缩,可以使用compress挂载选项启用数据压缩或更改压缩方式和压缩等级,默认压缩方式是 zlib,压缩等级是 3。可用压缩方式和等级:https://btrfs.wiki.kernel.org/index.php/Manpage/btrfs(5)#MOUNT_OPTIONS

启用数据压缩后,当一个新文件被写入时,Btrfs 会使用一些方法检查文件是否可以被压缩,如果不可以,Btrfs 将会永久标注此文件为不可压缩。可以使用compress-force挂载选项让 Btrfs 尝试压缩每个新写入的文件,但是已经创建的文件默认不会被压缩。

你还可以在不使用compress挂载选项的情况下,使用chattr命令给文件或目录加上c属性为其启用压缩。

快照 (Snapshot)

Btrfs 支持为子卷创建快照。快照本身实际上也是一个子卷,只不过利用了写时复制和引用链接,将源子卷的内容全部复制为引用链接到一个新的子卷。

创建快照命令:sudo btrfs subvolume snapshot root root_backup,此命令为root子卷创建了一个名为root_backup的快照,但是root_backup本身又是一个可用的子卷,所以如果需要还原快照,只需要挂载对应的快照子卷,或者将原子卷删掉,重命名快照子卷。如果你使用subvolid挂载子卷,需要注意快照子卷 ID 和源子卷 ID 不一样。

如果为禁用写时复制的子卷创建快照,那么创建快照后可能会重新启用源子卷的写时复制。

更多 Btrfs 常见问题:https://btrfs.wiki.kernel.org/index.php/FAQ

更多 Btrfs 挂载选项:https://btrfs.wiki.kernel.org/index.php/Manpage/btrfs(5)#MOUNT_OPTIONS

注意事项:

虽然 Btrfs 的子卷在挂载时可以看作是不同的分区或挂载项,但是在 Linux 内核看来它们依然是指向同一分区的项目。所以大多数挂载选项将用于整个分区,只有第一个挂载的子卷选项才会生效。

Btrfs 由于存在种种新特性,某些分区统计工具可能无法精准统计。要获取分区的精准使用情况,可以使用sudo btrfs filesystem usage ${mountPath}查看具体信息,${mountPath}是任意子卷的挂载路径。

如果修改了默认子卷,需要在顶级子卷下创建新的子卷时,需要先挂载顶级子卷,然后在顶级子卷挂载点下创建子卷,而不是在/挂载点下创建子卷。

如果你对系统分区使用平行子卷,内核可能需要设置挂载子卷选项才能正常启动,可以通过rootflags内核选项设置。

如果你使用 systemd 的 GPT 分区自动发现并且设置了内核选项root=gpt-auto,这种情况下即使设置了rootflags,systemd 也无法挂载对应子卷,除非更改默认子卷或gpt-auto,建议使用后者。


这篇文章某些地方可能表达的不够通俗易懂,有任何问题欢迎加入 QQ 群与我讨论。

微信公众号二维码

微信扫描二维码关注我们

如果觉得文章有帮助到你,可以点击下方的打赏按钮赞助下服务器费用。

小山

一个什么都不会但要装作很厉害的人

文章评论

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据