ZFS 文件系统快照和压缩
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 的 send 和 receive 允许在不同存储池或机器之间传输快照或增量差异,是实现备份的核心。
完整发送一个快照
# 发送快照到文件
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-1 到 zstd-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。
步骤
- 启用压缩
sudo zfs set compression=lz4 tank/data
- 创建快照脚本
/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
- 设置定时任务(crontab)
0 */4 * * * /usr/local/bin/snap.sh
- 每日增量发送(夜晚执行,假设已存在同名的基准快照可用于增量)
可在备份端维护一个最近的常见快照作为增量基准。首次执行完整发送后,即可持续使用增量。
#!/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"