ZFS 文件系统快照和压缩

FreeGuideOnline 最新 2026-07-11

bash

为 tank/mydata 创建名为 before_update 的快照

sudo zfs snapshot tank/mydata@before_update

递归创建快照,包含所有子文件系统(-r)

sudo zfs snapshot -r tank/mydata@recursive_snap


ZFS 允许快照名称包含字母、数字、下划线、连字符、冒号和句点。建议使用时间戳或描述性标签命名:

```bash
sudo zfs snapshot tank/mydata@$(date +%Y-%m-%d_%H-%M-%S)

列出快照

使用 zfs list -t snapshot 查看所有快照。

# 列出所有数据集的所有快照
zfs list -t snapshot

# 仅列出特定数据集的快照
zfs list -t snapshot -r tank/mydata

# 添加更多字段,如占用空间、引用大小
zfs list -t snapshot -o name,used,refer,creation

关键字段说明

  • USED:该快照独有的空间(删除它会释放此空间)。
  • REFER:快照创建时数据集所引用的数据量,它不随后续修改变化。
  • AVAIL:对快照无意义,通常显示为 -

删除快照

使用 zfs destroy,指定完整快照名。

# 删除单个快照
sudo zfs destroy tank/mydata@before_update

# 删除某个数据集的全部快照(使用 % 通配符)
sudo zfs destroy tank/mydata@%

# 递归删除所有子数据集的快照
sudo zfs destroy -r tank/mydata@%

回滚到快照

回滚会将数据集完全恢复到快照时的状态,丢弃该快照之后的所有修改

# 回滚到指定快照
sudo zfs rollback tank/mydata@before_update

# 如果快照之后还创建了其他快照,需要 -r 强制覆盖
sudo zfs rollback -r tank/mydata@before_update

回滚前的最佳实践:为避免误操作,可以在回滚前先创建一个当前状态的快照。

sudo zfs snapshot tank/mydata@pre_rollback_safety
sudo zfs rollback tank/mydata@before_update

访问快照中的文件

每个快照在数据集的根目录下都有一个隐藏的 .zfs/snapshot 目录,可以直接从中复制文件,无需完整回滚。

# 查看某个快照的内容
ls /tank/mydata/.zfs/snapshot/before_update/

# 复制单个文件
cp /tank/mydata/.zfs/snapshot/before_update/report.txt /tmp/old_report.txt

如果 .zfs 目录不可见,可以通过设置 snapdir 属性启用:

sudo zfs set snapdir=visible tank/mydata

快照高级应用:增量发送与接收

ZFS 的 sendreceive 允许在不同存储池或机器之间传输快照或增量差异,是实现备份的核心。

完整发送一个快照

# 发送快照到文件
sudo zfs send tank/mydata@backup_2025 > /backup/mydata_full.zfs

增量发送两个快照之间的差异

# 从快照 A 到快照 B 的增量
sudo zfs send -i tank/mydata@baseline tank/mydata@update_1 > /backup/incr.zfs

接收快照(在目标端)

# 接收并创建文件系统(本地示例)
sudo zfs receive tank/restored < /backup/mydata_full.zfs

# 结合管道直接发送到远程主机
sudo zfs send tank/mydata@snap1 | ssh user@backuphost "zfs receive backuppool/data"

增量复制可以定期执行,极大节省备份时间和带宽。

ZFS 透明压缩

ZFS 压缩是在数据写入磁盘之前将数据压缩,读取时自动解压,对于应用完全透明。它能够节省磁盘空间、减少 I/O,甚至在某些场景下提升整体吞吐量。

支持的压缩算法

ZFS 提供了多种压缩算法,在压缩率和 CPU 开销之间取得平衡。可以通过 zfs get compression 查看当前设置,zfs set compression=<algorithm> 进行修改。

算法 说明
lz4 推荐默认值。极高的压缩和解压速度,压缩率适中,对 CPU 几乎无影响。
zstd 现代算法,支持更多压缩等级(zstd-1zstd-19),平衡速度与比率。默认 zstd 等效 zstd-3
gzip 经典算法,可指定级别 1(最快)~ 9(最高压缩)。普遍支持但速度较慢。
zle 零长度编码,仅压缩连续零块,速度极快但仅对稀疏数据有效。
lzjb 旧版算法,性能一般,通常不推荐使用。

设置压缩

压缩是每个数据集的属性,且对现有数据不追溯生效,仅影响新写入的数据。

# 启用 lz4 压缩
sudo zfs set compression=lz4 tank/mydata

# 启用 zstd 压缩(默认级别3)
sudo zfs set compression=zstd tank/logs

# 使用 zstd-6 获得更高压缩比
sudo zfs set compression=zstd-6 tank/archives

# 对整个存储池递归启用压缩
sudo zfs set compression=lz4 -r tank

查看压缩效果

使用 zfs get compressratio 查看压缩率的累积值,它表示逻辑数据大小与实际占用物理空间的比值。

# 查看单个数据集压缩比
zfs get compressratio tank/mydata

# 列出所有数据集及压缩比
zfs list -o name,used,logicalused,compressratio

logicalused 是未压缩时数据的总量,compressratio 等于 logicalused / used。例如 1.50x 表示空间节省了约 33%。

也可以使用 zpool get capacity 查看整池的空间节省情况。

何时应该禁用压缩?

虽然压缩利远大于弊,但在以下情况可考虑关闭:

  • 数据已被压缩(如视频、JPEG、ZIP 文件),再压缩效果差且浪费 CPU。
  • 极低延迟场景下,CPU 压缩/解压产生的微秒级延迟不可接受(通常 lz4 和 zstd 的延迟已极低)。
sudo zfs set compression=off tank/videos

快照与压缩协同:备份策略实战

结合两者可以构建高效、节省空间的本地备份体系。下面演示一个典型的脚本化轮转备份流程。

目标

  • tank/data 启用 lz4 压缩。
  • 每 4 小时创建一次快照,保留最新的 6 个快照。
  • 每日将增量快照发送到备份池 backup/data

步骤

  1. 启用压缩
sudo zfs set compression=lz4 tank/data
  1. 创建快照脚本 /usr/local/bin/snap.sh
#!/bin/bash
DATASET="tank/data"
SNAPNAME="${DATASET}@hourly_$(date +%Y%m%d_%H%M%S)"
sudo zfs snapshot "$SNAPNAME"

# 保留最近6个快照,删除更早的
KEEP=6
snaps=( $(zfs list -H -t snapshot -o name -s creation "$DATASET" | grep "@hourly_") )
count=${#snaps[@]}
if [ $count -gt $KEEP ]; then
  delete=${snaps[@]:0:$((count - KEEP))}
  for snap in $delete; do
    sudo zfs destroy "$snap"
  done
fi
  1. 设置定时任务(crontab)
0 */4 * * * /usr/local/bin/snap.sh
  1. 每日增量发送(夜晚执行,假设已存在同名的基准快照可用于增量)

可在备份端维护一个最近的常见快照作为增量基准。首次执行完整发送后,即可持续使用增量。

#!/bin/bash
LOCAL="tank/data"
REMOTE_HOST="backupserver"
REMOTE_POOL="backup/data"
# 获取本地最新快照
latest=$(zfs list -H -t snapshot -o name -s creation "$LOCAL" | tail -1)
# 发送增量(假设远端已有同名快照)
sudo zfs send -i "$REMOTE_POOL"@"$latest" "$latest" | ssh "$REMOTE_HOST" zfs receive -F "$REMOTE_POOL"