Btrfs Scrub 使用指南:数据校验、自动修复与定期巡检

整理 Btrfs scrub 的作用、命令用法、自动修复条件、NOCOW 文件风险、只读 scrub 注意事项、定期巡检周期和带宽限制方法。

Btrfs scrub 是 Btrfs 日常维护里最重要、也最容易被误解的功能之一。它不是传统意义上的 fsck,而是一种在线校验流程:读取文件系统中的数据和元数据,验证校验和、超级块、元数据块头以及磁盘读错误,并在有可靠副本时尝试修复损坏。

如果你的 Btrfs 用在 NAS、家用服务器、备份盘或多盘阵列上,scrub 应该成为定期巡检的一部分。它的价值不是“等出事再修”,而是在磁盘还能读、阵列还有好副本的时候,尽早发现静默损坏。

scrub 到底检查什么

根据 Btrfs 官方文档,scrub 会遍历文件系统数据和元数据,主要检查:

  • 数据块校验和错误。
  • 基础 super block 错误。
  • 基础 metadata block header 错误。
  • 磁盘读取错误。

在使用复制型 block group profile 的文件系统上,例如 RAID1,读写挂载下的 scrub 可以自动修复部分损坏。修复方式不是“凭空恢复”,而是从另一个已验证正确的副本复制好数据回来。

这点很关键:scrub 的修复能力依赖“存在可用的好副本”。如果单盘上只有一份数据,scrub 可以发现校验错误,但通常无法自行恢复原始内容。

常用命令

对挂载点启动 scrub:

1
sudo btrfs scrub start /

以前台方式运行,适合手动观察结果:

1
sudo btrfs scrub start -B /

查看状态:

1
sudo btrfs scrub status /

取消正在运行的 scrub:

1
sudo btrfs scrub cancel /

恢复被中断的 scrub:

1
sudo btrfs scrub resume /

如果指定的是 Btrfs 挂载路径,Btrfs 会并行 scrub 该文件系统里的所有设备。如果指定的是某个设备,则只 scrub 该设备;但当指定设备上的副本读取或校验失败时,Btrfs 仍会尝试从其他设备读取好副本。

scrub 不是 fsck

这是最容易踩坑的地方。scrub 不是 btrfs check,也不是传统意义上的文件系统检查器。

scrub 能做的是:

  • 利用校验和发现数据或元数据损坏。
  • 在有其他可靠副本时自动修复。
  • 发现磁盘读错误和部分基础结构错误。

scrub 不能做的是:

  • 重建没有好副本的数据。
  • 替代离线文件系统检查。
  • 修复所有复杂的树结构损坏。
  • 保证应用层文件内容一定正确。

如果文件系统结构已经严重损坏,通常需要在专家指导下使用 btrfs check 等工具。不要把 scrub 当成“万能修复命令”。

NOCOW 文件的风险

Btrfs 官方文档特别提醒:对文件设置 NOCOW 属性,也就是常见的 chattr +C,当前实现中会隐式启用 NODATASUM。这意味着这些文件的数据本身没有校验和。

scrub 仍然可以校验和修复这些文件的元数据,但不能校验文件数据内容。问题在多副本场景里尤其明显:如果某个 NOCOW 文件的一个副本坏了,Btrfs 没有数据校验和判断哪个副本是好的,就可能把坏内容返回给用户空间工具。

需要特别注意的是,一些软件会默认使用 +C 来提升性能。例如 systemd journal 和部分 libvirt 存储池场景可能会设置 NOCOW。对于虚拟机镜像、数据库、日志目录,这种做法有性能理由,但也意味着你不能期待 scrub 像保护普通 COW 文件那样保护它们的数据内容。

只读 scrub 也可能写入

另一个反直觉点是:在读写挂载的文件系统上执行 read-only scrub,仍然可能导致一些写入。

官方文档说明,这是为了避免 block group 标记只读和写回 block group items 之间的竞态。换句话说,如果你希望 scrub 过程中完全没有写入,就需要在只读挂载的文件系统上运行只读 scrub,而不是在读写挂载上加一个只读 scrub 选项就完事。

对普通用户来说,这意味着:

  • 日常在线 scrub 可以在读写挂载上运行。
  • 如果你在做取证、故障分析或极度保守的只读检查,要先明确挂载状态。
  • 不要把 read-only scrub 误解成绝对零写入。

中断与恢复

新内核中,scrub 可能被多种事件中断,例如系统挂起/休眠、文件系统冻结、cgroup freezing、pending signals 等。被中断后,正在运行的 scrub 会取消,但可以通过 btrfs scrub resume 从保存的位置继续。

scrub 状态会记录在:

1
/var/lib/btrfs/

文件名通常类似:

1
2
scrub.status.UUID
scrub.progress.UUID

状态文件会定期更新。恢复 scrub 时,会从最后保存的位置继续,而不是完全从头再来。

建议多久跑一次

官方建议的周期是每月一次,实际可以根据数据重要性和磁盘状态调整。

比较常见的安排是:

  • 家用 NAS:每月一次。
  • 备份盘:每次长期接入后或每月一次。
  • 重要多盘阵列:每月一次,必要时更频繁。
  • 新盘迁移或怀疑坏盘:迁移后立即跑一次。

scrub 在空闲文件系统上的设备带宽利用率可能接近 80%,所以不要在业务高峰期跑。对机械盘阵列,scrub 期间延迟可能明显升高;对 SSD,也会增加读放大和后台压力。

限制 scrub 带宽

过去可以用 ionice 尝试降低 scrub 对前台 I/O 的影响,但官方文档提醒,并不是所有 I/O 调度器都支持这种方式。CFQ 已经不再是主流;BFQ 支持相关优先级,但使用前应先理解其行为。对 mq-deadline 等常见调度器,更推荐使用 cgroup2 I/O controller 或 Btrfs 自带的限速接口。

使用 systemd 限制读取带宽的例子:

1
sudo systemd-run -p "IOReadBandwidthMax=/dev/sdx 10M" btrfs scrub start -B /

Linux 5.14 以后,可以通过 Btrfs 专用 sysfs 接口设置每设备 scrub 限速:

1
echo 100m | sudo tee /sys/fs/btrfs/FSID/devinfo/DEVID/scrub_speed_max

查看当前限制:

1
sudo btrfs scrub limit /

注意这个设置不是永久的,文件系统卸载后会失效。实际路径里的 FSIDDEVID 要按你的系统替换,可以先查看:

1
2
sudo btrfs filesystem show /
ls /sys/fs/btrfs/

实用维护流程

一个比较稳妥的 Btrfs 巡检流程可以是:

1
2
3
4
sudo btrfs scrub start -B /
sudo btrfs scrub status /
sudo btrfs device stats /
dmesg -T | grep -Ei "btrfs|checksum|i/o error|read error"

如果 scrub 报告有 corrected errors,说明已经从好副本修复过数据,但这不代表可以忽略。你应该继续检查磁盘 SMART、线缆、电源、控制器和 Btrfs device stats。

如果 scrub 报告 uncorrectable errors,说明 Btrfs 找不到可用好副本,必须尽快备份仍能读取的数据,定位具体文件或设备,并根据情况替换硬盘或从备份恢复。

总结

Btrfs scrub 的定位很明确:它是在线数据巡检和副本修复工具,不是 fsck,也不是备份。

它最适合用在有校验和、有冗余副本的 Btrfs 文件系统上,定期发现静默损坏并自动从好副本恢复。它不能保护没有校验和的 NOCOW 文件数据,也不能在没有好副本时凭空恢复损坏内容。

如果你使用 Btrfs 存重要数据,建议每月跑一次 scrub,配合 SMART、device stats、备份和告警一起使用。真正可靠的数据安全,永远是校验、冗余、监控和备份一起工作,而不是只依赖某一个命令。

参考链接:

记录并分享
使用 Hugo 构建
主题 StackJimmy 设计