为什么选 ZFS
传统文件系统(ext4/xfs)+ LVM 的组合能做基础存储,但面对 NAS 和备份场景的三个核心诉求——数据完整性、快照秒级回滚、跨机增量同步——就显得力不从心:
| 需求 | ext4/xfs + LVM | ZFS |
|---|---|---|
| 数据静默损坏检测 | 无(靠应用层) | 端到端校验(checksum on read) |
| 快照创建时间 | 分钟级(LVM snapshot 需冻结) | 秒级(COW,零额外空间) |
| 增量跨机同步 | 无原生方案 | zfs send -i 增量流 |
| RAID 重建风险 | 逐块拷贝,静默错误不可知 | 校验重建,自动纠错 |
| 空间去重 | 无 | 原生 dedup(可选) |
OpenZFS 2.x 已合并 Linux/FreeBSD 分支,内核模块稳定,生产环境大规模部署(Ubuntu Server、TrueNAS、Proxmox)。
池创建与 VDEV 设计
磁盘拓扑原则
ZFS 池由 VDEV(虚拟设备)组成,每个 VDEV 是一组磁盘的冗余组合。池的可靠性取决于最弱的 VDEV——一个 VDEV 挂掉,整个池不可用。
# 生产级配置示例:3 个 raidz1 VDEV = 9 盘池
# 容量:9 × 4TB × (2/3) ≈ 24TB 可用
# 可靠性:允许每个 VDEV 1 盘故障(共 3 盘冗余)
zpool create -f tank raidz1 sda sdb sdc raidz1 sdd sde sdf raidz1 sdg sdh sdi
常见 VDEV 类型选择:
| 类型 | 最少盘数 | 可靠性 | 读性能 | 适用场景 |
|---|---|---|---|---|
| mirror | 2 | N-1 盘冗余 | 2× 单盘 | 高 IOPS、小规模 |
| raidz1 | 3 | 1 盘冗余 | ≈ 单盘 | 读多写少、容量优先 |
| raidz2 | 4+ | 2 盘冗余 | ≈ 单盘 | 生产推荐、大池 |
| raidz3 | 5+ | 3 盘冗余 | ≈ 单盘 | 关键数据、大容量 |
⚠️ 避坑 #1:raidz1 在大盘(≥4TB)场景下重建窗口过长,重建期间第二盘故障即丢池。生产环境推荐 raidz2 或 mirror。
特殊 VDEV
ZFS 支持 3 种特殊 VDEV 加速池性能:
# L2ARC(读缓存 SSD)—— 加速随机读
zpool add tank cache sdj
# SLOG(同步写日志 SSD)—— 加速 NFS/SMB 同步写
zpool add tank log sdk
# Special VDEV(元数据专用 SSD)—— 加速小文件/目录遍历
zpool add tank special sdl
⚠️ 避坑 #2:SLOG 和 Special VDEV 故障会导致池挂掉。必须用 mirror 保护:
zpool add tank log sdk sdl mirror。
属性调优
# 创建时指定关键属性
zpool create -o ashift=12 -O compression=zstd-3 -O atime=off -O xattr=sa tank mirror sda sdb
# 后续调整
zfs set compression=zstd-6 tank/data # 压缩算法升级
zfs set recordsize=1M tank/media # 大文件场景(视频/备份)
zfs set recordsize=16K tank/mysql # 数据库场景(8K/16K)
zfs set primarycache=all tank/hotdata # 活跃数据全缓存
zfs set redundant_metadata=most tank/cold # 冷数据省空间
compression 算法选择(实测参考):
| 算法 | 压缩率 | CPU 开销 | 适用 |
|---|---|---|---|
| lz4 | 1.5-2× | 极低 | 默认推荐 |
| zstd-3 | 2-2.5× | 低 | 通用生产 |
| zstd-6 | 2.5-3× | 中 | 文档/日志 |
| zstd-19 | 3-4× | 高 | 归档 |
数据集(Dataset)与 Quota
ZFS 数据集相当于独立文件系统,可单独设置属性、快照和配额——这是 NAS 场景的核心管理单元。
# 创建层级数据集
zfs create tank/data
zfs create tank/data/home
zfs create tank/data/shared
zfs create tank/data/backup
# 配额控制
zfs set quota=500G tank/data/home # 用户目录上限
zfs set refquota=200G tank/data/shared # 仅限自身数据(不含快照)
zfs set reservation=100G tank/data/backup # 预留空间保关键数据
# 精细权限(NAS 共享场景)
zfs set acltype=posixacl tank/data/shared
zfs set aclmode=restricted tank/data/shared
快照:秒级备份与零成本回滚
快照原理
ZFS 快照基于 Copy-On-Write(COW):创建时只记录当前指针,零额外空间;后续修改才产生差量块。这意味着:
- 创建耗时:毫秒级(不拷数据)
- 空间占用:仅存变更块(1 天快照通常 < 5% 原数据量)
- 读写性能:零影响(快照不参与读写路径)
# 手动快照
zfs snapshot tank/data/home@20260716
zfs snapshot -r tank/data@20260716 # 递归:所有子数据集
# 查看快照
zfs list -t snapshot -o name,used,avail,refer
# NAME USED AVAIL REFER
# tank/data/home@20260716 12M - 280G
# 快照回滚(秒级恢复)
zfs rollback tank/data/home@20260716 # 丢弃之后的所有修改
# 查看快照差异
zfs diff tank/data/home@20260716 tank/data/home@20260715
# M /tank/data/home/docs/report.xlsx # 修改
# + /tank/data/home/newfile.txt # 新增
# - /tank/data/home/oldcache.tmp # 删除
自动快照策略
使用 zfs-auto-snapshot 或自写 systemd timer 实现分级快照:
# /etc/systemd/system/zfs-snapshot-daily.timer
[Unit]
Description=ZFS Daily Snapshot
[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true
[Install]
WantedBy=timers.target
# /etc/systemd/system/zfs-snapshot-daily.service
[Unit]
Description=ZFS Daily Snapshot
[Service]
Type=oneshot
ExecStart=/usr/local/bin/zfs-auto-snap daily 7
# 保留 7 天日快照
推荐保留策略(NAS 备份标准):
| 级别 | 频率 | 保留数量 | 空间预估 |
|---|---|---|---|
| frequent | 每 15 分钟 | 4 | 极小 |
| hourly | 每小时 | 24 | 小 |
| daily | 每天 | 7-14 | 中 |
| weekly | 每周 | 4-8 | 中 |
| monthly | 每月 | 6-12 | 较大 |
⚠️ 避坑 #3:快照不是备份。快照和原始数据在同一池,池挂了快照也没了。必须
zfs send到异地。
增量发送接收:跨机备份利器
全量 + 增量流
# 初次全量发送(一次性)
zfs send tank/data@initial | ssh backupnas "zfs recv backup/tank/data"
# 增量发送(日常)
zfs send -i @initial tank/data@daily-01 | ssh backupnas "zfs recv backup/tank/data"
# 连续增量链
zfs send -i @daily-01 tank/data@daily-02 | ssh backupnas "zfs recv backup/tank/data"
增量流的压缩加速
# 带压缩的发送(适合 WAN 传输)
zfs send -i @snap1 tank/data@snap2 | lz4c | ssh backupnas "lz4c -d | zfs recv backup/tank/data"
# 大流量的缓冲发送(避免网络波动中断)
zfs send -i @snap1 tank/data@snap2 | mbuffer -s 128k -m 1G | ssh backupnas "mbuffer -s 128k -m 1G | zfs recv backup/tank/data"
加密发送
# 中间加密层(WAN/公网传输)
zfs send -i @snap1 tank/data@snap2 | \
openssl enc -aes-256-cbc -pass pass:KEY | \
ssh backupnas "openssl enc -d -aes-256-cbc -pass pass:KEY | zfs recv backup/tank/data"
⚠️ 避坑 #4:增量发送依赖快照链连续性。中间快照被删,后续增量流断裂。务必保留发送源的锚点快照直到接收端确认。
数据完整性验证
Scrub(池级校验)
ZFS 的杀手级特性——读每块数据并比对 checksum,自动纠错:
# 手动触发 scrub
zpool scrub tank
# 查看进度
zpool status tank
# scan: scrub in progress since Thu Jul 16 02:00
# 12.5T scanned out of 24.0T at 180M/s, 10h30m to go
# 0 repaired, 0 errors
# 定期 scrub(推荐每月)
# systemd timer 或 cron:
# 0 2 1 * * /sbin/zpool scrub tank
Scrub 发现静默损坏时:ZFS 自动从冗余副本重建正确数据并写回——这是 ext4/xfs 做不到的。
损坏检测与修复
# 查看池错误
zpool status -v tank
# errors: Permanent errors have been detected
# tank/data/home/docs/contract.pdf # 文件级定位
# 修复方式:
# 1. 冗余自动修复(scrub 期间自动)
# 2. 无冗余时从备份恢复:zfs recv 或手动替换文件
# 3. 清除已知无法修复的错误记录:zpool clear tank
池扩容与设备替换
添加 VDEV 扩容
# 添加新 VDEV(容量立即可用)
zpool add tank raidz2 sdk sdl sdm sdn
# ⚠️ 新 VDEV 不参与已有数据重平衡
# 数据会按新比例写入新 VDEV
⚠️ 避坑 #5:
zpool add后无法撤销。添加的 VDEV 将永远属于此池。确保 VDEV 类型和冗余级别与已有的一致。
替换故障盘
# 标记故障盘离线
zpool offline tank sda
# 物理替换后执行 resilver
zpool replace tank sda /dev/disk/by-id/newdisk
# 监控 resilver 进度
zpool status tank
# scan: resilver in progress since ...
# 8.2T scanned, 1.2T resilvered, 14.5% done
# resilver 完成后自动 detach 旧盘
zpool detach tank olddisk-id
监控与告警
# ZFS 事件守护进程(zed)—— 自动邮件告警
apt install zfs-zed
vim /etc/zfs/zed.d/zed.rc
# ZED_EMAIL_ADDR="ops@example.com"
# ZED_EMAIL_VERBOSE=1
systemctl enable --now zed
# 关键监控项
zpool get health tank # 池健康
zpool get free tank # 可用空间
zfs get used,usedbydataset,usedbysnapshots tank/data # 快照空间占比
zpool get frag tank # 空间碎片率(>70% 需关注)
告警阈值建议:
| 指标 | 警告 | 严重 | 动作 |
|---|---|---|---|
| 池使用率 | 80% | 90% | 扩容/清理 |
| 冗余降级 | 1 盘 offline | 2 盘 offline | 立即换盘 |
| Scrub 错误 | >0 | >10 | 检查硬件 |
| 空间碎片 | >70% | >85% | 重写数据集 |
常见故障恢复
| 故障 | 诊断 | 恢复 |
|---|---|---|
| 单盘故障 | zpool status 显示 DEGRADED |
zpool replace |
| 池导入失败 | zpool import 抐错 |
zpool import -f(强制);zpool import -F(回滚日志) |
| 快照链断裂 | 增量 send 失败 | 从最近全量快照重新 send |
| 空间耗尽 | zpool list 显示 0 free |
删快照/清 quota;紧急 zfs rollback 释放 |
| 静默损坏 | scrub 发现 error | 冗余自动修复;无冗余从 recv 恢复 |
| 误删文件 | 用户报告丢失 | zfs rollback @last-snap 或 .zfs/snapshot/ 直接拷回 |
⚠️ 避坑 #6:
zpool import -F会回滚最后的几个事务,可能丢几秒数据。仅用于池完全无法导入的灾难恢复。
十条避坑清单
- ashift=12 不可少:4K 物理 sector 盘用 ashift=9 会严重降速;创建池时
-o ashift=12(或用自动检测的ashift=auto) - raidz1 大盘风险:≥4TB 盘用 raidz1 重建窗口太长,生产必须 raidz2 或 mirror
- atime=off 是标配:atime 写入破坏 COW 优化,每次读触发写;所有数据集都应关闭
- 快照 ≠ 备份:本地快照不防池故障;必须
zfs send到异地 - 锚点快照别删:增量 send 的起始快照删了,后续全部增量流断裂
- SLOG/Special VDEV 必 mirror:单 SSD 故障导致池不可用
- VDEV 类型不能混搭:池内所有 VDEV 应同类型(全是 mirror 或全是 raidz2)
- zpool add 不可逆:加错的 VDEV 只能销毁池重建
- compression=zstd 几乎总是对的:现代 CPU lz4/zstd 开销极低,压缩率可观;别用 gzip(太慢)
- 定期 scrub:月度 scrub 是发现静默损坏的唯一手段,不做等于裸奔
总结
ZFS 的端到端校验、秒级快照和增量同步三大能力,让它成为 NAS 和备份场景的最优存储引擎。核心管理原则:
- VDEV 设计决定池的生死——raidz2/mirror 是生产底线
- 快shot 是日常回滚工具,send 才是灾难恢复手段
- scrub 是数据健康的体检,月度必须执行
- 属性调优按场景——recordsize 对齐工作负载,compression 默认开启
配置好池结构 + 自动快照 + 定期 send + 月度 scrub,存储层就可以放心交付。