100 GB 数据、10 个快照、每个快照后改 1 GB:全量复制要 1100 GB,Btrfs 只要 110 GB——1.1 倍。快照几乎免费,代价是碎片、df 看不懂、满盘难操作。
7.6 讲了写时复制的原理,这一节看把它做成产品的样子:Btrfs 把【快照】从备份任务变成了日常操作。
一、Btrfs 是什么
Btrfs 是一个【写时复制】的 Linux 文件系统,它的野心是把【卷管理】与【文件系统】合成一层。
它带来的四件事:快照、子卷、校验和、透明压缩。这四件事都建立在同一个基础上:写时复制。
因为写时复制,任何时刻盘上都有一棵完整的树,所以拍一个快照只需要【复制根指针】;因为每个块只被写一次,所以可以顺手算校验和;因为新块本来就要写,顺手压缩一下也不额外费力。
所以这四件事不是四个功能,是一个特性的四种用法。这也是为什么 Btrfs 的逻辑必须整体理解,只记住【能拍快照】会在遇到 df 迷惑时手足无措。
二、快照为什么是日常操作
算一笔账:100 GB 数据,做 10 个快照,每个快照之后改 1 GB。
全量复制:11 份 × 100 GB = 1100 GB。
Btrfs:100 GB + 10 × 1 GB = 110 GB。只多占 10%。
因为快照之间共享所有没改过的块,只有改过的那部分各存一份。快照个数完全不影响到【没改过的那 90 GB】。
所以快照的用法变了:以前【备份】是要计划、要窗口、要停机的事;现在【升级前先拍一张】只是敲一行命令,几秒钟。
这带来一个很实用的工作方式:snapshot / before-upgrade → 升级 → 有问题就反向拍一张回去。不用恢复备份,不用等拷贝。
但记住 7.7 的那句话:快照不是备份。它在同一块盘上,盘坏了两个一起没。
三、子卷、校验和、压缩
子卷(subvolume):在一个卷里划出多个【独立的树】,各自可以单独拍快照、单独设配额、单独挂载。它不像分区那样固定大小,空间是共享的。
校验和:每个数据块都有 CRC32C(可选更强的算法)。btrfs scrub 能全盘重算一遍,主动发现坏块。配 RAID1 时还能自动从副本修复。代价是每个块多几十字节,以及每次读写都要算一遍。
透明压缩:写的时候压,读的时候解。对文本、日志、代码通常能压到一半以下,代价是 CPU。它还有个意外好处:压缩后写入量变少,在慢盘上反而更快。
这四件事加起来,Btrfs 更像【一个带文件接口的存储管理器】,而不是传统意义上的一张【文件系统】。
四、三个必须知道的坑
① 碎片。写时复制意味着每次改都写到新地方,长期反复改的文件会碎成一片,顺序读退化成随机读。虚拟机镜像、数据库文件是重灾区——这类文件建议关掉 COW(chattr +C)。
② df 的数字很迷惑。因为块是共享的,df 说【用了 30 GB】,但删掉一个快照可能一个字节都没释放(别的快照还指着)。要看真实占用得用 btrfs filesystem usage /。
③ 满盘时几乎无法操作。COW 文件系统在接近满时需要空间来做元数据更新,写满之后连删除都可能失败。所以官方建议留 5%~10% 不要用满。这一条最容易在【备份盘写满】的时候咬人。
一句话:Btrfs 把【快照】从【备份功能】变成了【日常操作】,代价是【你得重新学怎么判断磁盘还剩多少】。
五、动手 Demo:Btrfs——把【快照】做成日常操作
这一节算两笔账:10 个快照的空间代价,以及为什么 df 在 Btrfs 上会给出让人误解的数字。
GB = 1024 ** 3
base = 100 * GB # 基础数据 100 GB
per_change = 1 * GB # 每个快照后改 1 GB
n = 10 # 10 个快照
full = (n + 1) * base / GB # 全量复制
cow = (base + n * per_change) / GB # 写时复制共享
print("全量复制 %.0f GB(%.1fx)" % (full, full / (base / GB)))
print("COW 快照 %.0f GB(%.1fx)" % (cow, cow / (base / GB)))
print("只多占 %.0f%%" % ((cow / (base / GB) - 1) * 100))
实跑输出:
--------------------------------------------------------------------
Btrfs——把【快照】做成日常操作
--------------------------------------------------------------------
① 十个快照,占多少空间
做法 要多少空间 相对
----------------------------------------------
每个快照都全量复制 1100 GB 11.0x
Btrfs 写时复制快照 110 GB 1.1x
**10 个快照只多占 10%。**
**因为快照之间【共享所有没改过的块】,只有改过的那部分各存一份。**
② Btrfs 的四件事
功能 干什么用 代价
---------------------------------------------------------
快照 一瞬间拍下整个子卷 几乎没有
子卷 在一个卷里划出多个独立树 没有额外代价
校验和 每个数据块都有 CRC32C 空间与 CPU
透明压缩 写时压缩,读时解压 CPU 换空间
**快照最实用的场景是【升级前先拍一张】。**
**升级搞坏了,反向再拍一张就回去了**——
**不用恢复备份,不用等拷贝,几秒钟。**
③ 但 Btrfs 不是免费的午餐
**① 碎片。**
**写时复制意味着每次改都写到新地方**,
**长期反复改的文件会碎成一片,顺序读退化成随机读。**
**② `df` 的数字很迷惑。**
**因为块是共享的,`df` 说【用了 30 GB】,**
**但删掉一个快照可能一个字节都没释放**(别的快照还指着)。
**③ 满盘时几乎无法操作。**
**COW 文件系统在接近满时需要空间做元数据更新,写满之后连删除都可能失败。**
**所以官方建议留 5%~10% 不要用满。**
**一句话**:**Btrfs 把【快照】从【备份功能】变成了【日常操作】**,
**代价是【你得重新学怎么判断磁盘还剩多少】。**
📋 **Linux 上验证**:`btrfs filesystem df /`;
`btrfs subvolume list /`;
`btrfs filesystem usage /`(这个比 df 准)。
1100 GB 对 110 GB。10 个快照只多占 10%——因为快照之间共享所有没改过的块。
注意这个模型假设【每次只改 1 GB】。如果每次改动占基础数据的比例很大,COW 就不省了——它省的永远是【没改的那部分】。
真实 Btrfs 还要算元数据与校验和的开销,以及压缩带来的节省,所以实际数字会在这个量级上下浮动。
最容易踩的坑是【用 df 判断 Btrfs 还剩多少】——它算不清共享块,要用 btrfs filesystem usage。
📋 Linux 上验证:btrfs filesystem df /;
📋 btrfs subvolume list /;
📋 btrfs filesystem usage /(这个比 df 准)。
30 秒实验
btrfs filesystem usage /与df -h /各跑一次,看两个数差多少。btrfs subvolume list /,看你这台机器有几个子卷。- 想一想:为什么虚拟机镜像放 Btrfs 上要
chattr +C关掉 COW?
常见误区
- 【快照就是备份】。它在同一块盘上,盘坏了两个一起没。它是【版本】不是【备份】。
- 【df 显示还有空间就能继续写】。Btrfs 的
df算不清共享块;接近满时它会突然无法写入,要留 5%~10% 的余量。 - 【Btrfs 什么场景都比 ext4 好】。高并发写、虚拟机镜像、数据库文件这些场景,COW 带来的碎片是实打实的负担;ext4 在这些场景仍然更稳。
自测三问(点开对答案)
1. 10 个快照为什么只多占 10%? —— **因为快照之间共享所有没改过的块**;**只有改过的那部分各存一份。** 2. 为什么 Btrfs 上 `df` 的数字不可信? —— **因为它算不清【被多个快照共享的块】**——**删掉一个快照可能一个字节都没释放**。**要看真实占用得用 `btrfs filesystem usage`。** 3. 虚拟机镜像为什么要关掉 COW? —— **因为镜像文件会被反复改同一片区域**,**COW 每次都写到新位置,很快碎成一片**,**性能会明显下降。**- ☑ (第七章完;下一章 8.1 HDD的数据读写机制已写完)
相关笔记
- 写时复制 — 原理那一节
- 防止不了的情况 — 快照不等于备份
- 文件系统不一致的对策 — 另一条路线:日志
- 🐧 Linux是怎样工作的 · 索引