Linux运维13 min read

ZFS 存储管理与数据快照实战

为什么选 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

⚠️ 避坑 #5zpool 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/ 直接拷回

⚠️ 避坑 #6zpool import -F 会回滚最后的几个事务,可能丢几秒数据。仅用于池完全无法导入的灾难恢复。

十条避坑清单

  1. ashift=12 不可少:4K 物理 sector 盘用 ashift=9 会严重降速;创建池时 -o ashift=12(或用自动检测的 ashift=auto
  2. raidz1 大盘风险:≥4TB 盘用 raidz1 重建窗口太长,生产必须 raidz2 或 mirror
  3. atime=off 是标配:atime 写入破坏 COW 优化,每次读触发写;所有数据集都应关闭
  4. 快照 ≠ 备份:本地快照不防池故障;必须 zfs send 到异地
  5. 锚点快照别删:增量 send 的起始快照删了,后续全部增量流断裂
  6. SLOG/Special VDEV 必 mirror:单 SSD 故障导致池不可用
  7. VDEV 类型不能混搭:池内所有 VDEV 应同类型(全是 mirror 或全是 raidz2)
  8. zpool add 不可逆:加错的 VDEV 只能销毁池重建
  9. compression=zstd 几乎总是对的:现代 CPU lz4/zstd 开销极低,压缩率可观;别用 gzip(太慢)
  10. 定期 scrub:月度 scrub 是发现静默损坏的唯一手段,不做等于裸奔

总结

ZFS 的端到端校验、秒级快照和增量同步三大能力,让它成为 NAS 和备份场景的最优存储引擎。核心管理原则:

  • VDEV 设计决定池的生死——raidz2/mirror 是生产底线
  • 快shot 是日常回滚工具,send 才是灾难恢复手段
  • scrub 是数据健康的体检,月度必须执行
  • 属性调优按场景——recordsize 对齐工作负载,compression 默认开启

配置好池结构 + 自动快照 + 定期 send + 月度 scrub,存储层就可以放心交付。

分享:

评论区