Btrfs scrub 实用指南:启动、限速、查看进度和速度
Btrfs 的 scrub 是一个很适合定期运行的在线校验工具。它会遍历 Btrfs 文件系统中的数据和元数据,检查校验和错误、读错误、部分元数据头错误和超级块错误。
如果文件系统使用了有冗余副本的 profile,比如 RAID1、RAID10 或 DUP,读写挂载状态下的 scrub 在发现坏副本时,可以尝试从好的副本修复。它不是 fsck,也不能替代 btrfs check;它更像是一次在线的数据完整性巡检。
这篇文章记录一套实用流程:怎么找可 scrub 的挂载点,怎么启动,怎么降低 I/O 影响,怎么查看状态,以及在旧版工具不显示 Rate / ETA / 百分比时,怎么自己算速度和进度。
scrub 的对象是什么?
btrfs scrub 不是按目录工作的。
你传给它的路径可以是一个 Btrfs 文件系统里的路径,但 scrub 的对象是这个路径所属的 Btrfs 文件系统,或者指定的设备。
例如:
sudo btrfs scrub start /volume3
如果 /volume3 是一个 Btrfs 文件系统的挂载点,那么它会 scrub 这个文件系统。它不会只校验 /volume3 下某个目录。
如果同一个 Btrfs 文件系统有多个子卷分别挂载,比如 /、/home、/var/lib/docker,对其中任意一个挂载点执行 scrub,本质上仍然是在 scrub 同一个 Btrfs 文件系统。
如何找可以 scrub 的挂载点
有 findmnt 的系统上,最简单:
findmnt -t btrfs
只看挂载点路径:
findmnt -rn -t btrfs -o TARGET
如果系统没有 findmnt,可以直接读 /proc/mounts:
awk '$3=="btrfs"{print $2}' /proc/mounts
看更完整的信息:
awk '$3=="btrfs"{print $1, $2, $4}' /proc/mounts
也可以用传统 mount 输出粗筛:
mount | grep ' type btrfs '
拿到挂载点后,通常对每个实际 Btrfs 文件系统选一个代表挂载点运行 scrub 即可。
启动 scrub
后台启动:
sudo btrfs scrub start /volume3
前台启动,直到完成才返回:
sudo btrfs scrub start -B /volume3
-B 的意思是不要后台运行。它适合你想在当前终端等待结果的场景。
如果想在前台运行,并在结束时显示每个设备的统计信息:
sudo btrfs scrub start -Bd /volume3
注意这里的 -d 是 start 子命令的参数,并且只在 -B 时有效。它表示完成后按设备打印统计。
后台启动后,常用状态命令是:
sudo btrfs scrub status /volume3
按设备查看状态:
sudo btrfs scrub status -d /volume3
status -d 和 start -d 不一样。status -d 是查看状态时按设备显示,可以单独使用。
暂停、取消和恢复
取消正在运行的 scrub:
sudo btrfs scrub cancel /volume3
恢复上次取消或中断的 scrub:
sudo btrfs scrub resume /volume3
查看状态:
sudo btrfs scrub status /volume3
Btrfs 会把 scrub 状态记录在 /var/lib/btrfs/ 下,文件名通常包含文件系统 UUID。官方文档说明状态文件大约每 5 秒更新一次。
降低 I/O 影响:ionice 优先级
旧版 btrfs scrub start --help 常见参数是:
usage: btrfs scrub start [-BdqrRf] [-c ioprio_class -n ioprio_classdata] <path>|<device>
其中:
-c set ioprio class
-n set ioprio classdata
这相当于内置了类似 ionice 的 I/O 优先级控制。
最保守的后台运行方式:
sudo btrfs scrub start -c 3 /volume3
含义:
-c 3 = idle I/O class
idle 类不需要 -n。它表示只有系统 I/O 比较空闲时才积极执行。
如果想用普通类里的最低优先级:
sudo btrfs scrub start -c 2 -n 7 /volume3
含义:
-c 2 = best-effort
-n 7 = best-effort 里的最低优先级
也可以用外部 ionice 包一层:
sudo ionice -c3 btrfs scrub start /volume3
或者:
sudo ionice -c2 -n7 btrfs scrub start /volume3
需要注意:ionice 和 -c/-n 控制的是 I/O 调度优先级,不是固定 MB/s 限速。并且在现代 Linux 常见 I/O scheduler 下,它不一定都有效。Btrfs 官方文档也提醒,相关优先级机制依赖 I/O scheduler,常见的 mq-deadline 并不一定按预期支持这种优先级控制。
真正按速度限速
较新的 btrfs-progs 支持更直接的 scrub 限速方式,但这里的“新”要看具体版本:
btrfs scrub limit:btrfs-progs 6.6.3引入,用于查看或设置每设备 scrub 限速。btrfs scrub start --limit:btrfs-progs 6.13引入,用于本次 scrub 运行期间临时设置吞吐上限。
所以 btrfs-progs v6.2 没有 scrub limit 和 scrub start --limit 是正常的,即使发行版或 NAS 系统本身已经是最新版本。
如果 btrfs scrub start --help 里有 --limit,可以这样:
sudo btrfs scrub start --limit 100m /volume3
如果有 btrfs scrub limit 子命令,可以查看或设置每个设备的限制:
sudo btrfs scrub limit /volume3
sudo btrfs scrub limit --all --limit 100m /volume3
Linux 5.14 之后,还可以通过 Btrfs 的 sysfs 文件设置每设备 scrub 速度上限:
echo 100m | sudo tee /sys/fs/btrfs/<FSID>/devinfo/<DEVID>/scrub_speed_max
这个设置不是持久化配置,卸载文件系统后会消失。
如果用户态工具还是 btrfs-progs 6.2 这类旧版本,但内核足够新,可以先检查 sysfs 入口是否存在:
ls /sys/fs/btrfs/*/devinfo/*/scrub_speed_max
如果存在,可以绕过 btrfs scrub limit 命令,直接写入该文件。写入前需要确认对应的 FSID 和 DEVID 属于目标文件系统。
另一种通用做法是使用 cgroup v2 / systemd 的 I/O 限制,例如官方文档给出的形式类似:
sudo systemd-run -p "IOReadBandwidthMax=/dev/sdx 10M" btrfs scrub start -B /
这种方式比 -c 3 更接近真正限速,但配置复杂度更高,而且需要确认实际底层设备路径。
查看状态:新版本的理想输出
新版本 btrfs scrub status 可能会直接显示这些字段:
Status: running
Duration: 0:00:05
Time left: 0:00:05
ETA: Wed Apr 10 12:35:01 2023
Total to scrub: 28.32GiB
Bytes scrubbed: 13.76GiB (48.59%)
Rate: 2.75GiB/s
Error summary: no errors found
这种情况下很简单:
- 速度看
Rate - 进度看
Bytes scrubbed和百分比 - 剩余时间看
Time left或ETA - 错误看
Error summary
实时刷新:
watch -n 5 'sudo btrfs scrub status -d /volume3'
如果没有 watch:
while true; do
clear
date
sudo btrfs scrub status -d /volume3
sleep 5
done
旧版本只显示已 scrub 字节数怎么办?
有些 NAS 或旧版 btrfs-progs 的输出比较简略,例如:
scrub status for d1a06ecc-e68a-46c5-9741-7bd9ab592c4b
scrub device /dev/mapper/cachedev_0 (id 1) status
scrub started at Sun Jun 28 09:15:15 2026, running for 00:00:56
total bytes scrubbed: 22.22GiB with 0 errors
这种输出没有 Rate,也没有总进度。平均速度可以手算:
平均速度 = total bytes scrubbed / 已运行秒数
上面的例子:
22.22GiB / 56s ≈ 406MiB/s
但这只是从启动到现在的平均速度,不是瞬时速度。
如果支持 -R,可以查看原始状态:
sudo btrfs scrub status -R /volume3
可能会得到类似:
data_extents_scrubbed: 2809542
tree_extents_scrubbed: 55582
data_bytes_scrubbed: 184036638720
tree_bytes_scrubbed: 910655488
read_errors: 0
csum_errors: 0
verify_errors: 0
uncorrectable_errors: 0
corrected_errors: 0
last_physical: 186868826112
这里可以用:
已 scrub 字节 = data_bytes_scrubbed + tree_bytes_scrubbed
实时速度 = 两次已 scrub 字节差值 / 间隔秒数
last_physical 表示当前扫到的物理位置。它可以用来粗略估算进度,但不是一个完美的百分比分母。
一个简易 scrubtop 脚本
下面这个脚本每隔 5 秒调用一次 btrfs scrub status -R,根据两次采样的字节差计算实时速度。
#!/bin/sh
# usage: scrubtop /volume3
path="${1:-/volume3}"
interval="${INTERVAL:-5}"
prev_bytes=""
prev_time=""
while true; do
out="$(sudo btrfs scrub status -R "$path" 2>/dev/null)"
now="$(date +%s)"
data="$(printf '%s\n' "$out" | awk -F': ' '/data_bytes_scrubbed/ {print $2}')"
tree="$(printf '%s\n' "$out" | awk -F': ' '/tree_bytes_scrubbed/ {print $2}')"
errors="$(printf '%s\n' "$out" | awk -F': ' '/uncorrectable_errors/ {print $2}')"
read_errors="$(printf '%s\n' "$out" | awk -F': ' '/read_errors/ {print $2}')"
csum_errors="$(printf '%s\n' "$out" | awk -F': ' '/csum_errors/ {print $2}')"
last_physical="$(printf '%s\n' "$out" | awk -F': ' '/last_physical/ {print $2}')"
bytes=$((data + tree))
clear
date
printf 'Path: %s\n' "$path"
printf 'Scrubbed: %.2f GiB\n' "$(awk "BEGIN {print $bytes/1024/1024/1024}")"
printf 'Last physical: %.2f GiB\n' "$(awk "BEGIN {print $last_physical/1024/1024/1024}")"
if [ -n "$prev_bytes" ]; then
dt=$((now - prev_time))
db=$((bytes - prev_bytes))
if [ "$dt" -gt 0 ]; then
printf 'Rate: %.2f MiB/s\n' "$(awk "BEGIN {print $db/$dt/1024/1024}")"
fi
else
printf 'Rate: collecting baseline...\n'
fi
printf 'Errors: read=%s csum=%s uncorrectable=%s\n' "$read_errors" "$csum_errors" "$errors"
printf '\nRaw status:\n%s\n' "$out"
prev_bytes="$bytes"
prev_time="$now"
sleep "$interval"
done
保存为 scrubtop 后运行:
chmod +x scrubtop
./scrubtop /volume3
如果想改刷新间隔:
INTERVAL=10 ./scrubtop /volume3
进度百分比怎么估算?
精确进度依赖工具是否能给出 Total to scrub。如果 btrfs scrub status 没有这个字段,只有 -R 的 raw 输出,那么只能估算。
常见估算口径有两个:
1. 用 last_physical / 设备大小
查设备大小:
sudo blockdev --getsize64 /dev/mapper/cachedev_0
估算:
进度 ≈ last_physical / 设备大小
这个口径适合单设备、物理布局相对直观的场景,但不等于 Btrfs 逻辑层面的真实待 scrub 数据量。
2. 用 Btrfs 已分配空间做分母
查看 Btrfs 空间使用:
sudo btrfs filesystem usage -b /volume3
估算:
进度 ≈ 已 scrub 字节 / 已分配空间
这个口径更贴近 scrub 扫描已分配数据/元数据块的事实,但在 RAID、DUP、压缩、快照、多设备场景下仍然只是估算。
结论是:如果工具不显示 Total to scrub,就不要把估算百分比当成精确进度。速度和错误计数更可靠。
错误字段怎么看?
常见字段:
read_errors
csum_errors
verify_errors
super_errors
uncorrectable_errors
corrected_errors
unverified_errors
重点关注:
corrected_errors:发现坏块,并且已经从好副本修复。uncorrectable_errors:发现错误,但没有可用好副本修复,需要进一步处理。csum_errors:数据校验和错误。read_errors:底层读失败,可能涉及磁盘、线缆、控制器或阵列层问题。verify_errors:元数据块头校验类错误。
如果出现 uncorrectable_errors,不要只重跑 scrub。应该先保存状态输出,再检查内核日志、设备 SMART、Btrfs device stats 和备份状态。
dmesg | grep -iE 'btrfs|i/o error|checksum|read error'
sudo btrfs device stats /volume3
推荐实践
日常后台低影响运行:
sudo btrfs scrub start -c 3 /volume3
查看状态:
sudo btrfs scrub status -d /volume3
旧版工具查看 raw 状态:
sudo btrfs scrub status -R /volume3
新版工具如果支持真正限速:
sudo btrfs scrub start --limit 100m /volume3
或:
sudo btrfs scrub limit --all --limit 100m /volume3
sudo btrfs scrub start /volume3
如果要在维护窗口里跑:
sudo btrfs scrub start -c 3 /volume3
到时间后:
sudo btrfs scrub cancel /volume3
下次继续:
sudo btrfs scrub resume /volume3
小结
btrfs scrub 的关键点可以压缩成几句话:
- scrub 面向 Btrfs 文件系统或设备,不面向目录。
- 默认后台运行;
-B才是前台等待完成。 -c 3/ionice -c3是降低 I/O 优先级,不是真正 MB/s 限速。btrfs scrub limit需要btrfs-progs 6.6.3及以上;scrub start --limit需要btrfs-progs 6.13及以上。- Linux 5.14 及以上内核可能支持通过
scrub_speed_max做每设备限速,即使旧版btrfs-progs没有对应子命令。 - 新版
status可能直接显示Rate、ETA和百分比。 - 旧版可用
status -R的data_bytes_scrubbed + tree_bytes_scrubbed计算实时速度。 - 没有
Total to scrub时,进度百分比只能估算,不应当当作精确值。
参考资料
- Btrfs scrub documentation
- Btrfs progs changelog
- 本文示例来源于 2026-06-28 的一次实际排查记录:旧版
btrfs-progs只显示total bytes scrubbed,但status -R能提供data_bytes_scrubbed、tree_bytes_scrubbed和last_physical。






















