我的设备上有两张致钛的 1T
硬盘,分别作为系统盘与数据盘,在它们上面都使用了 Btrfs文件系统
。
系统盘上因为存放了操作系统和软件等与个人数据无关的内容,所以子卷结构更为复杂,让我们先来聊一聊它。
另请参阅
Why Btrfs?
Btrfs 是一种现代的写时复制(COW)Linux 文件系统,致力于实现一些高级功能,同时着重于容错性、修复性以及易于管理性。它于 2007 年诞生,是 Linux 内核的一部分,并仍在积极开发中。
相比于 Ext3
, Ext4
和 ntfs
等文件系统,btrfs
提供了一系列丰富的现代化功能和特性,且经过多年的开发和优化,已经足够稳定高效。使其几乎成为了当下最适合日常使用的文件系统。
- 基于
B-Tree
数据结构的元数据管理而非表,且无日志设计- 在底层数据结构原理上带来更高的性能和更小的开销。
- 最大
16EB
的单文件大小和单文件系统大小支持 - 写时复制(
CoW
)- 每次写入硬盘数据时,先将更新数据写入一个新的
block
,当新数据写入成功之后,再更新相关的数据结构指向新block
。 - 以此保证文件系统的一致性,避免突然掉电导致的数据损坏。
- 每次写入硬盘数据时,先将更新数据写入一个新的
- 针对
SSD
的优化- 将多次硬盘空间分配请求聚合成一个大小为
2M
的连续的块。大块连续地址的I/O
能够让固化在SSD
内部的微代码更好的进行读写优化,从而提高I/O
性能。 CoW
技术从根本上避免了对同一个物理单元的反复写操作。- 集成的闲时异步
TRIM/Discard
来释放块以供复用。
- 将多次硬盘空间分配请求聚合成一个大小为
- 数据校验和与自动校错
Btrfs
在读取数据时会同时读取相应的checksum
。如果最终从硬盘读取出来的数据和checksum
不相同,btrfs
会首先尝试读取数据的镜像备份,如果数据没有镜像备份,btrfs
将返回错误。- 以此确保了数据的可靠性,使硬件层面错误存储/读取的数据能够被上层软件感知到,进而避免
silent corruption
现象,并在一定程度上自我修正。同时也使硬件损坏导致的部分数据丢失能够提前直接被感知到。
- 子卷
Subvolume
是很优雅的一个概念。即把文件系统的一部分配置为一个完整的子文件系统,称之为subvolume
。- 采用
subvolume
,一个大的文件系统可以被划分为多个子文件系统,这些子文件系统共享底层的设备空间,在需要空间时便从底层设备中分配。这种模型有很多优点,比如可以充分利用硬盘的带宽,可以简化硬盘空间的管理等。 - 在逻辑上提供了传统分区的使用体验,但在物理上又没有传统分区的诸多限制。如此在使用体验上,我们获得了多个文件系统分区,可以将它们挂载在不同的位置,分配不同的挂载策略,对某个分区单独操作而不会影响整个
btrfs
文件系统。同时它们又都共享着同一份底层设备空间,不用像传统分区一样担心分区的大小和位置是否合理等。
- 快照与克隆
Btrfs
能够对文件系统的子卷完成快照操作,并在需要时轻松将文件系统回滚到快照所在状态,或直接对快照进行读写操作。- 借助
CoW
技术,btrfs
的快照仅需要最长几秒时间即可完成,同时由于快照存储的是增量变化以及共享存储block
的特性,因此快照所占的空间也近乎可以忽略不计。 - 相比于不具有该特性的文件系统下只能全量备份的方案在空间和时间的代价上都大大减小。
- 可读写快照甚至可以直接引导启动,当作一个系统某一状态的
LiveCD
使用。
- 透明压缩
- 通常人们认为将数据写入硬盘前进行压缩会占用大量
CPU
计算时间,必然导致文件系统读写效率的降低。但随着技术的发展,CPU
处理速度和硬盘I/O
速度间的差距不断加大,此时情况发生了改变。假如一个文件不经过压缩的情况下需要100
次硬盘I/O
。但花费少量CPU
时间进行压缩后,只需要10
次硬盘I/O
就可以将压缩后的文件写入磁盘。在这种情况下,I/O
效率反而提高了。 Btrfs
提供了多种压缩算法供使用,通过在硬盘I/O
前进行自动压缩/解压,来提高相同理论硬盘空间的实际可使用空间(~10%
),同时提高了I/O
性能,还减少对硬盘的读写来延长寿命。Btrfs
的压缩还具有启发式特性,当对一个文件的若干block
尝试压缩后发现压缩率不高时,便会放弃对余下部分的继续压缩。- 所谓“透明”即这些操作是文件系统的内部行为,对用户使用而言是无感的。
- 通常人们认为将数据写入硬盘前进行压缩会占用大量
- 原生多设备支持
Btrfs
支持动态添加设备。用户在系统中增加新的硬盘之后,可以使用 btrfs 的命令将该设备添加到文件系统中。同时文件系统空间的多chunk
设计也为用户提供了灵活的配置可能。- 原生
RAID
支持。
- 高效的元数据内联文件
- 通过将小文件的数据直接内联到元数据节点上,来避免独立数据块的分配,提高效率。
- 目录索引、空间预分配、延迟分配、
extent
、动态inode
分配……- 借助
B-Tree
的结构、extent
和动态inode
等特性,使btrfs
整体性能不会随着硬盘容量的增加而降低,具有卓越的扩展性。
- 借助
结构布局
我的系统盘采用扁平化布局,所有子卷(subvolume
)均是 ID
为 5 的顶级子卷的子卷(children
),再分别挂载在相应的目录下。
子卷 | 挂载点 | 额外挂载参数 |
---|---|---|
@ | / | noatime, compress=zstd:1 |
@home | /home | noatime, compress=zstd:1 |
@root | /root | noatime, compress=zstd:1 |
@log | /var/log | noatime, compress=zstd:1 |
@cache | /var/cache | noatime, compress=zstd:1 |
@tmp | /var/tmp | noatime, compress=zstd:1 |
@swap | /swap | |
@snapshots | /.snapshots |
我的子卷划分主要分为四类。
- 首先是系统数据,也就是
@
子卷,将其分出用于容纳操作系统和软件本身,也是系统结构的基石,描述了系统本身的状态。 - 然后是用户数据部分,
@home
和@root
,它们分别对应了普通用户
和root用户
的数据目录,将其单独分出能够避免用户数据被不必要的快照和回滚时造成损失。 - 接下来是运行时数据部分,也就是
@log
,@cache
和@tmp
,因为是运行时数据的缓存和日志,因此他们也不应该随着系统被快照留存状态,同时将日志独立分卷能够使通过快照维护系统时,还保留着最新的日志文件用于分析,而不至于被回滚掉。 - 最后便是其他部分,
@swap
存放着交换文件,@snapshots
存放着所有快照,它们都是有着特殊用途的目录,因此从根目录的子卷中分离出来。
相比于模拟它们在文件系统中的结构进行组织的嵌套化布局,所有子卷均直接组织在顶级子卷之下的扁平化布局,使得子卷结构更加扁平直观,可以对不同的子卷采用独立的挂载参数,便于后续维护和快照。
挂载参数
对于除去两个特殊子卷的所有子卷,我们在挂载时都额外添加了 noatime
参数,这能够禁止更新访问记录来减少不必要的写操作以提高性能,在读密集型工作负载的场景下效果更加明显。虽然可能会导致破坏部分依赖于atime
工作的程序,但这个选项几乎已经成为了 btrfs
的标配。
对于透明压缩的算法我们使用了目前最快的我们以在LiveCD
中首次安装系统时的格式化与分卷操作为例。
据。过高的压缩等级在带来明显性能开销的同时并没有提高多少压缩率,1 级足够。
除了这两个手动指定的参数外,还有应用一些当前 btrfs
版本下的默认参数,这些参数可能随着版本更新导致默认值发生变更。它们分别是:
ssd
- 当 btrfs 识别到设备为固态硬盘时就会默认启用,提供了对
SSD
的特殊分配优化。
- 当 btrfs 识别到设备为固态硬盘时就会默认启用,提供了对
discard=async
- 在闲时异步释放硬盘中未使用的区块,也就是
TRIM
,对于SSD
的工作非常有帮助。交由文件系统维护也更加稳定,异步的模式使性能开销几乎可以忽略不计。
- 在闲时异步释放硬盘中未使用的区块,也就是
space_cache=v2
- 采用新版的空间高速缓存,以少量硬盘空间为代价极大提高读取性能。
操作过程
我们以在LiveCD
中首次安装系统时的格式化与分卷操作为例。
格式化
|
|
布置
|
|
其中@swap
子卷为swapfile
预留,@snapshots
子卷为snapper
快照预留。
挂载
|
|
|
|
对于/var/log
, /var/cache
和/var/tmp
三个可能存在大量文件就地更新的目录,我们禁用了其CoW
特性。如果有数据库/虚拟机硬盘存在的目录,也建议禁用,以减少硬盘碎片的产生。
可视化管理
|
|
快照与备份
对于系统日常的快照维护上,我选择了 snapper
。配合 snap-pac
和 grub-btrfs
,可以在安装软件包前后,以及固定周期时间自动生成快照,并在 grub 菜单中加入启动到 btrfs
快照的选项,在不回滚的情况下启动到特定状态下的系统,既可以当LiveCD
使用,也可以用来验证和排查操作的影响。维护利器!再也不怕我系统玩崩了(
首先安装相关软件包
|
|
在我的 btrfs
子卷布局下,与用户数据和运行时数据无关的操作系统和软件都在@
子卷下,因此只需对它创建备份配置即可。我们为根目录创建一个snapper
配置并命名为root
。
|
|
snapper
在使用前会自动生成一个/.snapshots
子卷并挂载在/.snapshots
目录下用于存储快照。但这与我们的子卷布局规范并不匹配,显得有些突兀。因此我们将其删去,并由我们之前分配时预留的@snapshots
子卷代替。
|
|
接下去对 snapper
的 root
配置进行一些调整。
|
|
- 在
ALLOW_USERS
与ALLOW_GROUPS
后加入用户名- 允许用户列出和查看快照
- 修改
TIMELINE_MIN_AGE
和TIMELINE_LIMIT_*
- 设置 snapper 定时快照保留的每小时/每日/每周/每月/每年快照数量,超过最小时长的过期快照将会被自动清除。
- 修改
NUMBER_MIN_AGE
,NUMBER_LIMIT
和NUMBER_LIMIT_IMPORTANT
- 设置数字备份的最小清理时长和保留数量。
- 由
snap-pac
在软件包安装前后进行的快照属于此类。
最后启动相关的服务和定时器即可完成配置
|
|
默认情况下,snapper
创建的快照为只读快照,可以浏览和回滚但可能不能直接引导启动:(
我们可以通过 grub-btrfs
提供的钩子将其挂载为可读写的,这样就能愉快地直接引导启动进行运维了。
|
|
...