Btrfs 快照与回滚实战:给系统盘一份后悔药
系统盘最怕两种事故:一次失败的升级、一条手滑的命令,机器直接起不来,修复成本按小时计。Btrfs 的子卷快照能在几秒内给整个系统拍一份写时复制(CoW)的「底片」,出事时把根子卷换回快照就能回到事故前的状态,前提是子卷布局在装机时就规划对、回滚动作提前演练过。本文沿着「布局 → 快照 → 回滚 → 空间观察」的顺序把这件事讲完整。环境以已采用 Btrfs 根分区的系统为例(如 openSUSE、部分 Fedora/Rocky 定制安装);需要说明的是,RHEL 9 官方已不再把 Btrfs 列为受支持的根文件系统,生产选用前先确认发行版支持口径,本文讲的机制在各发行版间是通用的。
第一步:子卷布局,@ 与 @home 为什么要分开
Btrfs 的快照是以子卷为单位的,布局决定了回滚的粒度。经典布局是顶层(subvolid=5)下放两个一级子卷:@ 挂载为 /,@home 挂载为 /home。再按需拆出 @/var/log 等子卷的发行版也有,但入门先抓住主线。把 /home 独立成子卷的理由很实际:回滚系统时只换 @,用户数据不受影响;如果 / 和 /home 在同一个子卷里,回滚系统等于连数据一起倒带,那不是后悔药,是时光机事故。查看当前布局:
btrfs subvolume list /
findmnt -o TARGET,SOURCE,OPTIONS / /home
如果你的系统是「整个根只有一个子卷」的布局,建议在下次重装或迁移时改成双子卷布局,再往下做快照才有意义。
第二步:创建快照与命名纪律
快照统一放在顶层下的 snapshots 子卷里(挂载点习惯为 /.snapshots),命名带上日期和用途标签,日后回滚时一眼能认出哪份是哪份:
mount -o subvolid=5 /dev/sda3 /mnt/btrfs-top
mkdir -p /mnt/btrfs-top/snapshots
btrfs subvolume snapshot /mnt/btrfs-top/@ /mnt/btrfs-top/snapshots/@-2026-01-15-before-upgrade
btrfs subvolume list / | grep snapshots
只读快照加 -r 参数更稳妥,能防误删改;系统级例行快照推荐只读,临时调试用的可写快照用完即删。命名纪律三条:带日期、带事由、不用 snap1 这类无信息名字。快照本身在创建瞬间几乎不占空间(共享数据块),占用随后续写入逐渐增长,这一点在第四节的空间观察里再展开。
第三步:手动回滚的完整动作
回滚本质是三件事:把根子卷换成快照、修正 fstab、确认引导项。系统还能进时在系统内操作,进不了时用 Live 介质挂载顶层,动作完全一致。以下假设要回滚到 @-2026-01-15-before-upgrade:
# 1. 挂顶层并备份当前(出问题的)根子卷,不直接删
mount -o subvolid=5 /dev/sda3 /mnt/btrfs-top
mv /mnt/btrfs-top/@ /mnt/btrfs-top/@-broken-2026-01-16
# 2. 由目标快照生成新的根子卷(只读快照先转成可写副本)
btrfs subvolume snapshot /mnt/btrfs-top/snapshots/@-2026-01-15-before-upgrade /mnt/btrfs-top/@
# 3. 核对新 @ 的 fstab:根与 home 的 subvol 名必须与实际一致
grep -E 'btrfs' /mnt/btrfs-top/@/etc/fstab
# 4. 若引导配置写死了子卷路径或默认子卷,同步修正后重建
btrfs subvolume get-default /
# 必要时:btrfs subvolume set-default <新@的subvolid> /
# UEFI/GRUB 环境检查 /boot/grub2/grub.cfg 中的 rootflags=subvol= 是否指向 @
几个关键注意点。其一,如果根子卷是通过 fstab 里的 subvol=@ 显式挂载的,只要新子卷名字仍叫 @,fstab 通常不用改;改子卷名省事一时,后续维护全是坑。其二,如果引导依赖「默认子卷」(set-default),回滚后必须把默认子卷指向新的 @,否则重启还是进旧系统。其三,/boot 若是独立分区不受快照覆盖,内核版本与根系统对不上时要回滚连带处理——这正是很多发行版把 /boot 也放进 Btrfs 或单独管理内核版本的原因,操作前先确认自己这台机器的 /boot 在哪。
第四步:配额与空间观察
btrfs filesystem usage /
btrfs filesystem df /
btrfs qgroup show -reF / # 启用 quota 后查看各子卷/快照的独占与共享占用
看 Btrfs 空间不要只看 df:元数据与数据的配比、未分配空间(Unallocated)才是判断「还能写多少」的依据,btrfs fi us 的输出才是准数。quota(qgroup)能统计每个快照的 Exclusive 占用、也能做限额,但它带来持续的元数据开销,大子卷量场景下会拖慢删除与平衡操作;家庭式少量快照可以开着做观察,海量快照的服务器上建议关掉 qgroup、改用「定期清理 + 按 fi us 巡检」的轻量口径。
验证
- 重启后 findmnt / 显示根挂载的子卷为新的 @,get-default 指向一致。
- 事故前做的标记文件(如升级前 touch 的 /root/pre-upgrade-mark)在回滚后的系统里存在,出问题版本装的包已不在 rpm/dpkg 清单中。
- @home 未受影响,用户目录文件的时间戳与回滚前一致。
- 旧的 @-broken 子卷还在顶层留存,确认新系统稳定运行一周后再删。
常见问题
快照把盘占满了。快照共享的数据块在源文件被删除后仍被快照引用,表现为「文件明明删了、空间不释放」。处理顺序:先删最老的快照,再用 btrfs fi us 复查;日常要给快照定保留策略(如保留最近 7 份),不要无限拍。
回滚后引导失败,进了 emergency shell。十有八九是 fstab 或引导项里的子卷名/默认子卷没对齐。进 Live 介质挂顶层,逐项核对 fstab 的 subvol=、get-default 的指向、grub.cfg 的 rootflags 三者是否都指向同一个新 @。
误删了子卷。btrfs subvolume delete 是不可恢复操作,删前务必用 subvolume list 与 show 二次确认 ID 与路径;误删的是快照就只能靠其他备份重建——这正是下面这条边界要强调的。
小结
最后必须把边界说清:快照不是备份。快照和原数据在同一块盘、同一个文件系统上,盘坏、文件系统损坏、误格式化时快照一起没。快照解决的是「改错了、升坏了」的人为与软件事故,备份解决的是「硬件没了」的事故,两者是互补关系。真要自动化例行快照与清理,可以引入 snapper 这类工具做定时拍与保留策略,但先把本文的手动流程走通,再谈自动化,排障时才知道工具在背后做了什么。






