16.4 ZFS 管理
16.4.1 创建和销毁数据集
与传统磁盘和卷管理器不同,ZFS 不会预先分配空间。传统文件系统在分区并分配空间后,若不添加新磁盘,便无法创建新的文件系统。而 ZFS 允许随时创建新的文件系统。每个 数据集 都具备压缩、去重、缓存和配额等特性,此外还有只读、大小写敏感、网络文件共享和挂载点等实用属性。数据集可以嵌套,子数据集会继承父数据集的属性。每个数据集都可以委托管理、复制、创建快照、纳入 jail,也可以直接销毁。为每种文件类型或文件集合创建单独的数据集是一种推荐做法。然而,数据集过多亦存在不足:zfs list 等命令会变慢,挂载数百乃至数千个数据集也会拖慢 FreeBSD 的启动速度。
查看目前的数据集:
# zfs list
NAME USED AVAIL REFER MOUNTPOINT
zroot 916M 49.0G 96K /zroot
zroot/ROOT 886M 49.0G 96K none
zroot/ROOT/default 886M 49.0G 886M /
zroot/home 27.8M 49.0G 96K /home
zroot/home/ykla 27.7M 49.0G 27.7M /home/ykla
zroot/tmp 128K 49.0G 128K /tmp
zroot/usr 288K 49.0G 96K /usr
zroot/usr/ports 96K 49.0G 96K /usr/ports
zroot/usr/src 96K 49.0G 96K /usr/src
zroot/var 636K 49.0G 96K /var
zroot/var/audit 96K 49.0G 96K /var/audit
zroot/var/crash 96K 49.0G 96K /var/crash
zroot/var/log 156K 49.0G 156K /var/log
zroot/var/mail 96K 49.0G 96K /var/mail
zroot/var/tmp 96K 49.0G 96K /var/tmp创建新的数据集并启用 LZ4 压缩:
# zfs create -o compression=lz4 zroot/usr/mydataset
# zfs list
NAME USED AVAIL REFER MOUNTPOINT
zroot 916M 49.0G 96K /zroot
zroot/ROOT 886M 49.0G 96K none
zroot/ROOT/default 886M 49.0G 886M /
zroot/home 27.8M 49.0G 96K /home
zroot/home/ykla 27.7M 49.0G 27.7M /home/ykla
zroot/tmp 128K 49.0G 128K /tmp
zroot/usr 384K 49.0G 96K /usr
zroot/usr/mydataset 96K 49.0G 96K /usr/mydataset # 注意此行
zroot/usr/ports 96K 49.0G 96K /usr/ports
zroot/usr/src 96K 49.0G 96K /usr/src
zroot/var 636K 49.0G 96K /var
zroot/var/audit 96K 49.0G 96K /var/audit
zroot/var/crash 96K 49.0G 96K /var/crash
zroot/var/log 156K 49.0G 156K /var/log
zroot/var/mail 96K 49.0G 96K /var/mail
zroot/var/tmp 96K 49.0G 96K /var/tmp由于此操作不涉及扫描文件和更新相应的元数据,销毁数据集比删除数据集中的文件要快得多。
警告
zfs destroy将永久删除数据集及其所有数据,且无法撤销。请务必确认操作对象正确无误,必要时可先用zfs destroy -n -v预览待销毁的数据集和快照。
销毁创建的数据集:
# zfs destroy zroot/usr/mydataset
# zfs list
NAME USED AVAIL REFER MOUNTPOINT
zroot 916M 49.0G 96K /zroot
zroot/ROOT 886M 49.0G 96K none
zroot/ROOT/default 886M 49.0G 886M /
zroot/home 27.8M 49.0G 96K /home
zroot/home/ykla 27.7M 49.0G 27.7M /home/ykla
zroot/tmp 128K 49.0G 128K /tmp
zroot/usr 288K 49.0G 96K /usr
zroot/usr/ports 96K 49.0G 96K /usr/ports
zroot/usr/src 96K 49.0G 96K /usr/src
zroot/var 636K 49.0G 96K /var
zroot/var/audit 96K 49.0G 96K /var/audit
zroot/var/crash 96K 49.0G 96K /var/crash
zroot/var/log 156K 49.0G 156K /var/log
zroot/var/mail 96K 49.0G 96K /var/mail
zroot/var/tmp 96K 49.0G 96K /var/tmp在现代版本的 ZFS 中,zfs destroy 是异步的,释放的空间可能几分钟后才会在池中体现。使用 zpool get freeing 存储池 查看 freeing 属性:
NAME PROPERTY VALUE SOURCE
zroot freeing 0 -该属性会显示哪些数据集正在后台释放其块。如果有子数据集(例如快照或其他数据集),则无法直接销毁父数据集。要销毁数据集及其所有子数据集,可使用 -r 递归销毁。使用 -n -v 可以列出此操作将销毁的数据集和快照,但不会实际销毁任何数据。销毁快照时,回收的空间也会一并显示。
16.4.2 创建和销毁卷
卷是一种特殊的数据集类型。它不作为文件系统挂载,而是以块设备的形式暴露于 /dev/zvol/poolname/dataset 路径下。因此,卷可用于其他文件系统、充当虚拟机磁盘,或通过 iSCSI 及 HAST 等协议提供给网络上的其他主机。
卷可以格式化并使用任何文件系统,也可以不格式化而直接存储原始数据。对用户而言,卷与普通磁盘无异。在这些 zvol 上放置普通文件系统,可以获得普通磁盘或文件系统所不具备的功能。例如,对一个 250 MB 的卷启用压缩属性,即可创建一个压缩的 FAT 文件系统。
创建 3 GB 的 ZFS 卷并启用压缩:
# zfs create -V 3G -o compression=on zroot/fat32技巧
如果设置的 FAT32 容量过低,由于文件簇不足,将无法正常格式化。
确认卷已创建并查看其空间占用:
# zfs list zroot/fat32
NAME USED AVAIL REFER MOUNTPOINT
zroot/fat32 3.05G 49.0G 56K -卷以块设备形式出现在 /dev/zvol/ 路径下:
# ls -al /dev/zvol/zroot/fat32
crw-r----- 1 root operator 0x75 Apr 16 12:21 /dev/zvol/zroot/fat32警告
newfs_msdos将格式化指定的 ZFS 卷,卷上已有数据将永久丢失。请确认设备路径/dev/zvol/正确无误,避免误格式化其他设备。
将卷格式化为 FAT32 文件系统:
# newfs_msdos -F32 /dev/zvol/zroot/fat32
newfs_msdos: cannot get number of sectors per track: Operation not supported
newfs_msdos: cannot get number of heads: Operation not supported
/dev/zvol/zroot/fat32: 6288320 sectors in 98255 FAT32 clusters (32768 bytes/cluster)
BytesPerSec=512 SecPerClust=64 ResSectors=64 FATs=2 Media=0xf0 SecPerTrack=63 Heads=255 HiddenSecs=0 HugeSectors=6291456 FATsecs=768 RootCluster=2 FSInfo=1 Backup=2挂载卷并确认挂载成功:
# mount -t msdosfs /dev/zvol/zroot/fat32 /mnt
# mount | grep fat32
/dev/zvol/zroot/fat32 on /mnt (msdosfs, local)复制一些文件以测试:
# cp /home/ykla/Philosophische-untersuchungen.pdf /mnt/
# df -h /mnt | grep fat32
/dev/zvol/zroot/fat32 3.0G 36M 3.0G 1% /mnt销毁卷的过程与销毁常规文件系统数据集类似:
# zfs destroy zroot/fat32该操作几乎即时完成,但空闲空间可能需要几分钟才能在后台回收。
16.4.3 重命名数据集
要更改数据集的名称,可使用 zfs rename 命令。要更改数据集的父级,同样使用该命令。将数据集重命名至不同的父级下,会改变其从父级继承的属性值。重命名数据集时,会卸载该数据集并将其挂载到新位置(该位置从新父级继承)。要阻止此行为,可使用 -u 选项。
将数据集重命名并移动到不同的父级:
首先创建用于演示的数据集并确认其位置:
# zfs create zroot/usr/mydataset
# zfs list
NAME USED AVAIL REFER MOUNTPOINT
zroot 3.94G 46.0G 96K /zroot
zroot/ROOT 886M 46.0G 96K none
zroot/ROOT/default 886M 46.0G 886M /
zroot/fat32 3.05G 49.0G 31.0M -
zroot/home 27.8M 46.0G 96K /home
zroot/home/ykla 27.7M 46.0G 27.7M /home/ykla
zroot/tmp 128K 46.0G 128K /tmp
zroot/usr 384K 46.0G 96K /usr
zroot/usr/mydataset 96K 46.0G 96K /usr/mydataset # 注意此行
zroot/usr/ports 96K 46.0G 96K /usr/ports
zroot/usr/src 96K 46.0G 96K /usr/src
zroot/var 636K 46.0G 96K /var
zroot/var/audit 96K 46.0G 96K /var/audit
zroot/var/crash 96K 46.0G 96K /var/crash
zroot/var/log 156K 46.0G 156K /var/log
zroot/var/mail 96K 46.0G 96K /var/mail
zroot/var/tmp 96K 46.0G 96K /var/tmp给数据集创建快照,演示重命名后快照仍会保留:
# zfs snapshot zroot/usr/mydataset@01
# zfs list -t snapshot
NAME USED AVAIL REFER MOUNTPOINT
zroot/usr/mydataset@01 0B - 96K -执行重命名,将数据集从 zroot/usr 移至 zroot/var 下:
# zfs rename zroot/usr/mydataset zroot/var/newname
# zfs list
NAME USED AVAIL REFER MOUNTPOINT
zroot 3.94G 46.0G 96K /zroot
zroot/ROOT 886M 46.0G 96K none
zroot/ROOT/default 886M 46.0G 886M /
zroot/fat32 3.05G 49.0G 31.0M -
zroot/home 27.8M 46.0G 96K /home
zroot/home/ykla 27.7M 46.0G 27.7M /home/ykla
zroot/tmp 128K 46.0G 128K /tmp
zroot/usr 288K 46.0G 96K /usr
zroot/usr/ports 96K 46.0G 96K /usr/ports
zroot/usr/src 96K 46.0G 96K /usr/src
zroot/var 732K 46.0G 96K /var
zroot/var/audit 96K 46.0G 96K /var/audit
zroot/var/crash 96K 46.0G 96K /var/crash
zroot/var/log 156K 46.0G 156K /var/log
zroot/var/mail 96K 46.0G 96K /var/mail
zroot/var/newname 96K 46.0G 96K /var/newname # 注意此行
zroot/var/tmp 96K 46.0G 96K /var/tmp快照无法更改父级。再创建一个快照验证:
# zfs snapshot zroot/var/newname@02
# zfs list -t snapshot
NAME USED AVAIL REFER MOUNTPOINT
zroot/var/newname@01 0B - 96K -
zroot/var/newname@02 0B - 96K -重命名快照也使用相同的命令。
# zfs rename zroot/var/newname@02 zroot/var/newname@03
# zfs list -t snapshot
NAME USED AVAIL REFER MOUNTPOINT
zroot/var/newname@01 0B - 96K -
zroot/var/newname@03 0B - 96K -要递归重命名快照,可使用 -r 选项,这将重命名所有子数据集中具有相同名称的快照。-r 同样适用于递归重命名数据集及其所有子数据集。
16.4.4 设置数据集属性
每个 ZFS 数据集都有一组控制其行为的属性。大多数属性默认从父数据集继承,但可以单独覆盖。使用 zfs set 命令设置数据集属性,语法为 <property=value dataset>。大多数属性的有效值范围有限,zfs get 会列出每种属性及其有效值。通过 zfs get all <数据集> 可查看数据集当前的所有属性(包括继承值和本地设置值),方便问题诊断与状态确认。使用 zfs inherit 可以将大多数属性恢复为继承值。此外,还可以定义用户自定义属性,这些属性成为数据集配置的一部分,可提供关于数据集或其内容的附加信息。为区分自定义属性与 ZFS 内置属性,可使用冒号(:)创建自定义命名空间。
设置自定义属性并查看:
# zfs set custom:costcenter=1234 zroot/var/newname
# zfs get custom:costcenter zroot/var/newname
NAME PROPERTY VALUE SOURCE
zroot/var/newname custom:costcenter 1234 local要删除自定义属性,使用 zfs inherit 并加上 -r 选项。如果自定义属性在任何父级数据集中未定义,此选项将删除它(但池的历史记录仍然会记录该更改)。
删除自定义属性并验证:
# zfs inherit -r custom:costcenter zroot/var/newname
# zfs get custom:costcenter zroot/var/newname
NAME PROPERTY VALUE SOURCE
zroot/var/newname custom:costcenter - -
# zfs get all zroot/var/newname | grep custom:costcenter
#16.4.4.1 获取和设置共享属性
两个常用且实用的数据集属性是 NFS 和 SMB 共享选项。设置这些属性可以定义 ZFS 是否通过网络共享数据集以及如何共享。目前,FreeBSD 仅支持设置 NFS 共享。要获取共享的当前状态,可输入:
# zfs get sharenfs zroot/home
NAME PROPERTY VALUE SOURCE
zroot/home sharenfs off default要启用数据集的共享,可以输入:
# zfs set sharenfs=on zroot/home
# zfs get sharenfs zroot/home
NAME PROPERTY VALUE SOURCE
zroot/home sharenfs on local可以为通过 NFS 共享的数据集设置其他选项,如 -alldirs、-maproot 和 -network。要设置共享选项,可以输入:
# zfs set sharenfs="-alldirs,-maproot=root,-network=192.168.1.0/24" zroot/home
# zfs get sharenfs zroot/home
NAME PROPERTY VALUE SOURCE
zroot/home sharenfs -alldirs,-maproot=root,-network=192.168.1.0/24 local16.4.5 管理快照
ZFS 快照采用写时复制(COW)机制,创建过程瞬时完成且不占用额外磁盘空间。快照创建后,原文件系统中的数据块发生修改时,ZFS 将新数据写入新位置,旧数据块原地保留,从而捕获文件系统在快照创建时刻的时间点状态。快照提供了数据集的只读、时间点副本。快照作用于整个数据集而非单个文件,在创建时不额外占用空间,随其引用的块发生变化而消耗空间。
快照的典型用途是,在执行软件安装或系统升级等高风险操作之前,快速备份当前文件系统状态。如果操作失败,可回滚到快照恢复系统状态;如果升级成功,则删除快照释放空间。快照回滚速度很快,几乎不会造成停机。快照不能替代完整的池备份,但在特定时刻保存数据集副本方面极为高效。
FreeBSD 中文社区(CFC)提供了 ZFS 脚本,可用于查看、创建、删除和恢复 ZFS 快照(ZFS snapshot)。ZFS 脚本项目地址。该脚本已部署至 https://docs.bsdcn.org/zfs.sh,可在 FreeBSD 系统上直接使用 fetch 命令下载。
16.4.5.1 创建快照
在默认情况下,使用 Auto ZFS 布局创建的分区结构如下:
# zfs list # 列出所有 ZFS 文件系统及其属性
NAME USED AVAIL REFER MOUNTPOINT
zroot 3.94G 46.0G 96K /zroot
zroot/ROOT 886M 46.0G 96K none
zroot/ROOT/default 886M 46.0G 886M /
zroot/fat32 3.05G 49.0G 31.0M -
zroot/home 27.8M 46.0G 96K /home
zroot/home/ykla 27.7M 46.0G 27.7M /home/ykla
zroot/tmp 128K 46.0G 128K /tmp
zroot/usr 288K 46.0G 96K /usr
zroot/usr/ports 96K 46.0G 96K /usr/ports
zroot/usr/src 96K 46.0G 96K /usr/src
zroot/var 732K 46.0G 96K /var
zroot/var/audit 96K 46.0G 96K /var/audit
zroot/var/crash 96K 46.0G 96K /var/crash
zroot/var/log 156K 46.0G 156K /var/log
zroot/var/mail 96K 46.0G 96K /var/mail
zroot/var/newname 96K 46.0G 96K /var/newname
zroot/var/tmp 96K 46.0G 96K /var/tmp要创建快照,可以使用 zfs snapshot <数据集>@<快照名> 命令。添加 -r 选项可以递归创建快照,在所有子数据集上使用相同的名称。
创建整个池的递归快照:
# zfs snapshot -r zroot@test # 为 zroot 池及其所有子文件系统递归创建快照 test快照不会在正常的 zfs list 操作中显示。要列出快照,必须在 zfs list 命令后加上 -t snapshot 选项。-t all 会显示文件系统和快照。
# zfs list -t snap # 列出所有 ZFS 快照
NAME USED AVAIL REFER MOUNTPOINT
zroot@test 0B - 96K -
zroot/ROOT@test 0B - 96K -
zroot/ROOT/default@test 0B - 886M -
zroot/fat32@test 0B - 31.0M -
zroot/home@test 0B - 96K -
zroot/home/ykla@test 0B - 27.7M -
zroot/tmp@test 0B - 128K -
zroot/usr@test 0B - 96K -
zroot/usr/ports@test 0B - 96K -
zroot/usr/src@test 0B - 96K -
zroot/var@test 0B - 96K -
zroot/var/audit@test 0B - 96K -
zroot/var/crash@test 0B - 96K -
zroot/var/log@test 0B - 156K -
zroot/var/mail@test 0B - 96K -
zroot/var/newname@01 0B - 96K -
zroot/var/newname@03 0B - 96K -
zroot/var/newname@test 0B - 96K -
zroot/var/tmp@test 0B - 96K - # 注意此行技巧
在命令中,
snapshot可以缩写为snap,其他命令也有对应的缩写形式,可自行查阅文档。
快照不会直接挂载,因此在 MOUNTPOINT 列中不会显示路径。由于快照在创建后是只读的,因此 ZFS 不会在 AVAIL 列中显示可用空间。可以通过以下命令比较快照与原数据集:
# zfs list -rt all zroot/home
NAME USED AVAIL REFER MOUNTPOINT
zroot/home 27.8M 45.9G 96K /home
zroot/home@test 0B - 96K -
zroot/home/ykla 27.7M 45.9G 27.7M /home/ykla
zroot/home/ykla@test 0B - 27.7M -同时显示数据集和快照可以揭示快照如何以写时复制(COW)方式工作。它们只保存更改的部分(delta),而不是重新保存整个文件系统的内容。这意味着快照在发生变化时占用的空间极小。
通过将文件复制到数据集后再创建第二个快照,可以更清楚地观察空间占用情况:
# cp /COPYRIGHT /var/tmp
# zfs snapshot zroot/var/tmp@test2查看创建第二个快照后各快照的空间占用变化:
# zfs list -rt all zroot/var/tmp
NAME USED AVAIL REFER MOUNTPOINT
zroot/var/tmp 164K 45.9G 100K /var/tmp
zroot/var/tmp@test 64K - 96K -
zroot/var/tmp@test2 0B - 100K -第二个快照只包含复制操作后数据集的更改,因此极大节省了空间。
注意
快照
zroot/var/tmp@test的大小在USED列中也发生了变化,这反映的是它与之后创建的快照之间的差异。
16.4.5.2 销毁快照
销毁快照时,可以使用 -r 参数递归删除:
# zfs destroy -r zroot@test # 递归删除 zroot 池及其子文件系统的 test 快照
# zfs list -t snap # 列出所有 ZFS 快照
no datasets available16.4.6 快照保持
有时需要防止关键快照被误删——例如在备份流程尚未完成之前,或因法规要求必须保留的特定时间点副本。ZFS 的保持(hold)机制可对快照施加命名的轻量级引用,使其在保持解除前无法被 zfs destroy 删除。
为快照添加保持标签:
# zfs hold keeptest zroot/var/tmp@test技巧
keeptest只是便于识别的标签,可以修改为其他名称。
查看快照上的所有保持:
# zfs holds zroot/var/tmp@test
NAME TAG TIMESTAMP
zroot/var/tmp@test keeptest Wed May 13 13:18 2026尝试销毁被保持的快照会返回错误:
# zfs destroy zroot/var/tmp@test
cannot destroy snapshot zroot/var/tmp@test: it's being held. Run 'zfs holds -r zroot/var/tmp@test' to see holders.使用 -r 选项可递归地对所有子数据集的同名快照施加保持:
# zfs hold -r keepall zroot@test
# zfs holds -r zroot@test
NAME TAG TIMESTAMP
zroot@test keepall Wed May 13 13:18 2026
zroot/ROOT@test keepall Wed May 13 13:18 2026
zroot/ROOT/default@test keepall Wed May 13 13:18 2026
zroot/home@test keepall Wed May 13 13:18 2026
zroot/home/ykla@test keepall Wed May 13 13:18 2026
zroot/tmp@test keepall Wed May 13 13:18 2026
zroot/usr@test keepall Wed May 13 13:18 2026
zroot/usr/ports@test keepall Wed May 13 13:18 2026
zroot/usr/src@test keepall Wed May 13 13:18 2026
zroot/var@test keepall Wed May 13 13:18 2026
zroot/var/audit@test keepall Wed May 13 13:18 2026
zroot/var/crash@test keepall Wed May 13 13:18 2026
zroot/var/log@test keepall Wed May 13 13:18 2026
zroot/var/mail@test keepall Wed May 13 13:18 2026
zroot/var/tmp@test keepall Wed May 13 13:18 2026
zroot/var/tmp@test keeptest Wed May 13 13:18 2026释放保持标签后,快照即可正常销毁:
# zfs release keeptest zroot/var/tmp@test
# zfs release keepall zroot/var/tmp@test
# zfs destroy zroot/var/tmp@testzfs send 的 -h 选项可在发送流中包含保持标签。这对灾难恢复环境中保持备份完整性特别有用。
# zfs send -h zroot/var/tmp@test | zfs receive backup/var/tmp接收端使用 zfs receive 接收该流时,快照的保持将在接收方自动重建。
16.4.6.1 比较快照
ZFS 提供了内建命令用于比较两个快照之间内容的差异。对于长期保存大量快照的场景,此功能极为实用,用户可借此查看文件系统随时间推移的变化。例如,zfs diff 可帮助用户找到最近的快照,检查其中是否仍包含误删除的文件。比较前面章节中创建的两个快照,得到以下输出:
# zfs diff zroot/var/tmp@test
+ /var/tmp/COPYRIGHT
M /var/tmp/该命令列出了指定快照(此处为 zroot/var/tmp@test)与当前文件系统之间的变化。
第一列显示更改类型:
| 命令 | 功能 |
|---|---|
| + | 添加路径或文件 |
| - | 删除路径或文件 |
| M | 修改路径或文件 |
| R | 重命名路径或文件 |
将输出与上述符号对照可知,ZFS 在创建 zroot/var/tmp@test 快照之后添加了 COPYRIGHT 文件,这也导致挂载在 /var/tmp 的父目录发生了修改。
两个快照的比较在借助 ZFS 复制功能将数据集传输到不同主机备份时非常有用。
通过完整的数据集名称和两个快照名称来比较两个快照:
# cp /var/tmp/COPYRIGHT /var/tmp/COPYRIGHT.copy
# zfs snapshot zroot/var/tmp@diff
# zfs diff zroot/var/tmp@test zroot/var/tmp@diff
+ /var/tmp/COPYRIGHT
M /var/tmp/
+ /var/tmp/COPYRIGHT.copy
# zfs diff zroot/var/tmp@test zroot/var/tmp@test2
+ /var/tmp/COPYRIGHT
M /var/tmp/备份管理员可以比较从发送主机接收到的两个快照,并确定数据集中的实际变化。
16.4.6.2 快照回滚
只要存在至少一个快照,随时都可以回滚到该快照。回滚最常见的场景是:当前数据集的状态已失效,或者更适合使用旧版本。例如,本地开发测试出错、系统更新失败导致功能受损,或者需要恢复已删除的文件或目录——这些情况都很常见。要回滚到快照,可使用 zfs rollback <snapshotname> 命令。如果更改量较大,操作可能耗时较长。在此期间,数据集始终保持一致状态,如同符合 ACID 原则的数据库执行回滚一般。整个过程在数据集在线且无需停机的情况下完成。回滚之后,数据集的状态将恢复到快照创建时的状态。回滚到快照会丢弃该数据集中所有不属于该快照的数据。如果在回滚到更早快照之前先为当前状态创建快照,则之后需要某些数据时可以方便地回滚。这样,用户可以在快照之间来回切换,而不会丢失仍有价值的数据。
16.4.6.3 快照还原测试
可以通过增删文件来验证快照的有效性。在测试环境中,如果事先创建了快照,即使执行 rm -rf /* 命令也可以顺利恢复;如果系统使用 UEFI,则需要根据其他章节的说明自行恢复 EFI 引导。
注意
此操作仅应在测试环境中执行。
假设在之前示例中,因误执行 rm 命令删除了超出预期的数据,需要回滚到快照:
首先查看当前可用的快照:
# zfs list -rt all zroot/var/tmp
NAME USED AVAIL REFER MOUNTPOINT
zroot/var/tmp 232K 45.9G 112K /var/tmp
zroot/var/tmp@test 64K - 96K -
zroot/var/tmp@test2 56K - 100K -
zroot/var/tmp@diff 0B - 112K -查看当前目录下的文件:
# ls /var/tmp
COPYRIGHT COPYRIGHT.copy vi.recover模拟误删除操作,移除 COPYRIGHT 相关文件:
# rm /var/tmp/COPYRIGHT*
# ls /var/tmp
vi.recover此时,用户发现误删了多余的文件,希望将其恢复。ZFS 提供了一种简便的恢复方式:只要定期为重要数据创建快照即可。回滚到上一个快照后,便可找回丢失的文件,并从该点重新开始:
执行回滚操作,将数据集恢复到 diff 快照的状态:
# zfs rollback zroot/var/tmp@diff
# ls -al /var/tmp
total 19
drwxrwxrwt 3 root wheel 5 Apr 16 13:01 .
drwxr-xr-x 25 root wheel 25 Apr 16 12:33 ..
-r--r--r-- 1 root wheel 6070 Apr 16 12:56 COPYRIGHT
-r--r--r-- 1 root wheel 6070 Apr 16 13:01 COPYRIGHT.copy
drwxrwxrwt 2 root wheel 2 Apr 13 12:38 vi.recover回滚操作将数据集恢复到最后一个快照的状态。
确认回滚后快照列表未变:
# zfs list -rt snapshot zroot/var/tmp
NAME USED AVAIL REFER MOUNTPOINT
zroot/var/tmp@test 64K - 96K -
zroot/var/tmp@test2 56K - 100K -
zroot/var/tmp@diff 0B - 112K -也可以回滚到更早的快照,即使之后还有其他快照存在。尝试这样做时,ZFS 会显示以下警告:
# zfs rollback zroot/var/tmp@test
cannot rollback to 'zroot/var/tmp@test': more recent snapshots or bookmarks exist
use '-r' to force deletion of the following snapshots and bookmarks:
zroot/var/tmp@test2
zroot/var/tmp@diff此警告表明,在目标快照与当前数据集状态之间存在其他快照。要完成回滚,必须删除这些快照。
与虚拟机快照不同,默认情况下,zfs rollback 命令只能回滚到最新快照(参考手册,Oracle 官方 ZFS 回滚命令文档)。由于快照是只读的,除非用户使用 -r 选项确认这是所需操作并销毁比目标快照更新的所有快照,否则 ZFS 无法跟踪数据集跨不同状态的所有更改。使用 -r 选项后,ZFS 将销毁比目标快照更新的所有快照,从而允许回滚到非最新的快照。
如果这确实是预期操作,且用户理解删除所有中间快照的后果,可执行以下命令:
# zfs rollback -r zroot/var/tmp@test
# zfs list -rt snapshot zroot/var/tmp
NAME USED AVAIL REFER MOUNTPOINT
zroot/var/tmp@test 0B - 96K -
# ls -al /var/tmp
total 10
drwxrwxrwt 3 root wheel 3 Apr 16 19:49 .
drwxr-xr-x 25 root wheel 25 Apr 16 12:33 ..
drwxrwxrwt 2 root wheel 2 Apr 13 12:38 vi.recover从 zfs list -t snapshot 的输出可以确认,在执行 zfs rollback -r 后,中间快照已删除。
ZFS 不支持一次性递归回滚所有子数据集,需要对每个子文件系统单独执行回滚操作。
# zfs rollback -r zroot@test # 回滚 zroot 到 test 快照并销毁更新的快照
# zfs rollback -r zroot/ROOT@test # 回滚 zroot/ROOT 到 test 快照并销毁更新的快照
# zfs rollback -r zroot/ROOT/default@test # 回滚 zroot/ROOT/default 到 test 快照并销毁更新的快照
# zfs rollback -r zroot/tmp@test # 回滚 zroot/tmp 到 test 快照并销毁更新的快照
# zfs rollback -r zroot/usr@test # 回滚 zroot/usr 到 test 快照并销毁更新的快照
# zfs rollback -r zroot/home@test # 回滚 zroot/home 到 test 快照并销毁更新的快照
# zfs rollback -r zroot/usr/ports@test # 回滚 zroot/usr/ports 到 test 快照并销毁更新的快照
# zfs rollback -r zroot/var@test # 回滚 zroot/var 到 test 快照并销毁更新的快照
# zfs rollback -r zroot/var/log@test # 回滚 zroot/var/log 到 test 快照并销毁更新的快照思考题
探索更优的快照回滚方案。可参考中文社区的 ZFS 脚本项目,或向 OpenZFS 项目提交功能请求与 Pull Request。
16.4.6.4 从快照中恢复单个文件
快照存储在父数据集下的隐藏目录 .zfs/snapshots/快照名称 中。
默认情况下,即使执行 ls -a 命令,这些目录也不会显示。尽管不可见,但仍可像普通目录一样访问。
属性 snapdir 控制着这些隐藏目录是否出现在目录列表中。将该属性设为 visible 后,它们便会显示在 ls 及其他涉及目录内容的命令输出中。
查看 snapdir 属性的当前设置,再将其切换为 visible:
# zfs get snapdir zroot/var/tmp
NAME PROPERTY VALUE SOURCE
zroot/var/tmp snapdir hidden default
# ls -a /var/tmp
. .. vi.recover
# zfs set snapdir=visible zroot/var/tmp
# ls -al /var/tmp
total 10
drwxrwxrwt 4 root wheel 3 Apr 16 19:49 .
drwxr-xr-x 25 root wheel 25 Apr 16 12:33 ..
dr-xr-xr-x+ 3 root wheel 3 Apr 16 19:46 .zfs # 注意此行
drwxrwxrwt 2 root wheel 2 Apr 13 12:38 vi.recover通过将快照中的文件复制回父数据集,可将单个文件恢复到先前状态。.zfs/snapshot 下的目录结构包含与之前创建的快照同名的目录,便于识别。以下示例展示了如何从隐藏的 .zfs 目录中恢复文件——将文件从包含目标文件的快照中复制回来:
删除文件后,通过 .zfs/snapshot 目录找到并恢复:
# rm -rf /var/tmp/vi.recover
# ls -a /var/tmp
. .. .zfs
# ls /var/tmp/.zfs/snapshot
test
# ls /var/tmp/.zfs/snapshot/test
vi.recover
# cp -r /var/tmp/.zfs/snapshot/test/vi.recover /var/tmp
# ls /var/tmp/
.zfs vi.recover即使将属性 snapdir 还原为默认属性 hidden,执行 ls .zfs/snapshot 仍会列出该目录的内容。管理员可自行决定是否显示这些目录,该设置作用于每个数据集。
将 snapdir 恢复为 hidden 后,仍可通过路径直接访问快照:
# zfs set snapdir=hidden zroot/var/tmp
# ls /var/tmp/.zfs/snapshot/test
vi.recover从隐藏的 .zfs/snapshot 目录复制文件或目录非常简便。反过来尝试将文件复制到快照目录时,会出现以下错误:
尝试向快照目录写入文件,验证快照的只读特性:
# cp /COPYRIGHT /var/tmp/.zfs/snapshot/test/
cp: /var/tmp/.zfs/snapshot/test/COPYRIGHT: Read-only file system此错误提醒用户:快照是只读的,创建后不可更改。无法向快照目录复制文件或从中删除文件,因为这会改变快照所代表的数据集状态。
快照随父文件系统自快照创建以来所发生的变化而消耗空间。快照的 written 属性可跟踪该快照占用的空间。
要销毁快照并回收空间,可使用 zfs destroy <数据集>@<快照> 命令。加上 -r 选项可递归删除父数据集下所有同名快照。使用 -n -v 选项时,命令会列出将要删除的快照及其预计回收的空间,而不执行实际销毁。
16.4.7 管理克隆
克隆是快照的可写副本,可写、可挂载,且拥有自己的属性。使用 zfs clone 创建克隆之后,原始快照便无法销毁。要反转克隆与快照之间的父子关系,可使用 zfs promote。提升克隆后,快照将成为克隆的子项,原来父子关系下的空间占用计算方式也会相应改变。克隆可以挂载到 ZFS 文件系统层次结构中的任意位置。
16.4.7.1 创建克隆
下面是展示克隆功能的示例数据集:
# zfs list -rt all zroot/home/ykla
NAME USED AVAIL REFER MOUNTPOINT
zroot/home/ykla 27.7M 45.9G 27.7M /home/ykla
zroot/home/ykla@test 0B - 27.7M -克隆的典型用途是保留快照的同时,对特定数据集做实验,以便在出错时可以恢复。由于快照无法更改,可为其创建一个可读写的克隆。实验取得理想结果后,可将克隆提升为数据集,并删除初始的文件系统。删除原始数据集并非必要,因为克隆与数据集可以共存而不会引发问题。
从快照创建克隆,并验证克隆与原数据集内容一致:
# zfs clone zroot/home/ykla@test zroot/home/ykla2
# ls -al /home/ykla*
/home/ykla:
total 28222
drwxr-xr-x 2 ykla ykla 11 Apr 16 11:55 .
drwxr-xr-x 4 root wheel 4 Apr 16 13:30 ..
-rw-r--r-- 1 ykla ykla 950 Apr 16 19:50 .cshrc
-rw-r--r-- 1 ykla ykla 311 Apr 16 19:50 .login
-rw-r--r-- 1 ykla ykla 79 Apr 16 19:50 .login_conf
-rw------- 1 ykla ykla 289 Apr 16 19:50 .mail_aliases
-rw-r--r-- 1 ykla ykla 255 Apr 16 19:50 .mailrc
-rw-r--r-- 1 ykla ykla 966 Apr 16 19:50 .profile
-rw-r--r-- 1 ykla ykla 1042 Apr 16 19:50 .shrc
-rw-r--r-- 1 ykla ykla 37635246 May 10 13:23 Philosophische-untersuchungen.pdf
-rw-r--r-- 1 root ykla 118 Apr 16 11:58 checksum.txt
/home/ykla2:
total 28222
drwxr-xr-x 2 ykla ykla 11 Apr 16 11:55 .
drwxr-xr-x 4 root wheel 4 Apr 16 13:30 ..
-rw-r--r-- 1 ykla ykla 950 Apr 16 19:50 .cshrc
-rw-r--r-- 1 ykla ykla 311 Apr 16 19:50 .login
-rw-r--r-- 1 ykla ykla 79 Apr 16 19:50 .login_conf
-rw------- 1 ykla ykla 289 Apr 16 19:50 .mail_aliases
-rw-r--r-- 1 ykla ykla 255 Apr 16 19:50 .mailrc
-rw-r--r-- 1 ykla ykla 966 Apr 16 19:50 .profile
-rw-r--r-- 1 ykla ykla 1042 Apr 16 19:50 .shrc
-rw-r--r-- 1 ykla ykla 37635246 May 10 13:23 Philosophische-untersuchungen.pdf
-rw-r--r-- 1 root ykla 118 Apr 16 11:58 checksum.txt查看克隆与原数据集的磁盘占用,二者共享同一份数据块:
# df -h /home*
Filesystem Size Used Avail Capacity Mounted on
zroot/home 46G 96K 46G 0% /home
# df -h /home/ykla*
Filesystem Size Used Avail Capacity Mounted on
zroot/home/ykla 46G 28M 46G 0% /home/ykla
zroot/home/ykla2 46G 28M 46G 0% /home/ykla216.4.7.2 提升克隆
克隆创建时,它是快照创建时刻数据集的完整副本。此后,克隆可独立于原始数据集更改。两者之间由快照关联,ZFS 在 origin 属性中记录这一关联。使用 zfs promote 提升克隆后,克隆将变为独立的数据集,origin 属性的值已清除,克隆与快照之间的关联也随之断开。以下示例展示了这一过程:
提升前,查看克隆的 origin 属性,确认源自快照:
# zfs get origin zroot/home/ykla2
NAME PROPERTY VALUE SOURCE
zroot/home/ykla2 origin zroot/home/ykla@test -执行提升操作:
# zfs promote zroot/home/ykla2提升后 origin 属性已清除,克隆成为独立数据集:
# zfs get origin zroot/home/ykla2
NAME PROPERTY VALUE SOURCE
zroot/home/ykla2 origin - -完成一些更改后(例如将 COPYRIGHT 复制到提升后的克隆中),旧目录便已过时。此时可以用提升后的克隆替换它。要实现替换,先用 zfs destroy 删除旧数据集,再用 zfs rename 将克隆重命名为旧数据集的名称(或一个完全不同的名称)。
在克隆中添加新文件:
# cp /COPYRIGHT /home/ykla2删除原始数据集:
# zfs destroy -f zroot/home/ykla将克隆重命名为原数据集名称,完成替换:
# zfs rename zroot/home/ykla2 zroot/home/ykla
# ls /home/ykla
.cshrc .profile
.login .shrc
.login_conf COPYRIGHT
.mail_aliases Philosophische-untersuchungen.pdf
.mailrc checksum.txt
# df -h /home/ykla
Filesystem Size Used Avail Capacity Mounted on
zroot/home/ykla 46G 28M 46G 0% /home/ykla至此,从快照克隆而来的数据集已成为一个普通数据集。它包含原始快照的所有数据以及新增的文件(如 COPYRIGHT)。克隆为 ZFS 用户提供了多种场景下的实用功能。例如,可以为 jail 提供包含不同应用集的快照,用户克隆这些快照后可按需添加自己的应用程序。对更改满意后,可将克隆提升为完整数据集,并交付给最终用户,最终用户可将其当作普通数据集使用。这大大节省了提供 jail 所需的时间和管理开销。
16.4.8 复制
将数据仅存储在单个池的单一位置中,会使数据面临盗窃、自然灾害或人为破坏等风险。因此,定期备份整个池至关重要。ZFS 提供了内置的序列化功能,可将数据的流表示发送到标准输出。借助此功能,既可以将数据存储到连接至本地系统的另一个池中,也可以通过网络将数据发送到另一台系统。快照是这种复制的基础。用于复制数据的命令是 zfs send 和 zfs receive。
创建所需测试存储池:
# mdconfig -a -t swap -s 1G
md0
# mdconfig -a -t swap -s 1G
md1
# zpool create mypool md0
# zpool create backup md1
# zpool list backup mypool
NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT
backup 960M 372K 960M - - 7% 0% 1.00x ONLINE -
mypool 960M 372K 960M - - 7% 0% 1.00x ONLINE -复制若干 非重要 文件以便于测试:
# cp /home/ykla/bsdbook.pdf /mypool/以下示例展示了如何使用这两个池完成 ZFS 复制:
存储池 mypool 是主池,用于读写常规数据。第二个池 backup 作为备用,以防主池不可用。
请注意,ZFS 不会自动执行故障转移,需要管理员在必要时手动操作。利用快照可以获取一致的文件系统版本用以复制还原。
为 mypool 创建快照后,即可通过复制快照将其传输到 backup 池。此操作不包含自上次快照以来的更改。
# zfs snapshot mypool@backup1确认快照已创建:
# zfs list -t snapshot mypool
NAME USED AVAIL REFER MOUNTPOINT
mypool@backup1 0B - 165M -现在已经存在一个快照,可使用 zfs send 创建代表快照内容的流。将此流存储为文件,或在另一个池上接收。
必须将流重定向到文件或管道,否则会出现如下错误:
# zfs send mypool@backup1
Error: Stream can not be written to a terminal.
You must redirect standard output.16.4.8.1 创建备份流
要备份数据集,可在使用 zfs send 时将流重定向到已挂载备份池上的文件。
请确保池有足够的可用空间容纳所发送快照的大小(即快照中包含的数据总量,而非与上一个快照之间的差异容量)。
# zfs send mypool@backup1 > /backup/backup1查看发送后两个池的空间占用变化:
# zpool list mypool backup
NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT
backup 960M 161M 799M - - 8% 16% 1.00x ONLINE -
mypool 960M 166M 794M - - 8% 17% 1.00x ONLINE -zfs send 已将快照 backup1 中的所有数据传输到存储池 backup 。要自动创建并发送这些快照,可配置 cron 任务。
16.4.8.2 接收备份流
与作为归档文件存储不同,ZFS 可将数据接收为活动的文件系统,从而直接访问备份数据。要访问这些流中的实际数据,可使用 zfs receive 将流还原为文件和目录。
以下示例将 zfs send 与 zfs receive 结合,通过管道将数据从池 mypool 复制到另一个池 backup。
先为 mypool 创建快照:
# zfs snapshot mypool@replica1传输完成后,可直接在接收池上使用数据。只能将数据集复制到空数据集。
执行全量发送与接收:
# zfs send -v mypool@replica1 | zfs receive backup/mypool
full send of mypool@replica1 estimated size is 166M
total estimated size is 166M
TIME SENT SNAPSHOT mypool@replica1查看接收后两个池的空间占用:
# zpool list mypool backup
NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT
backup 960M 331M 629M - - 12% 34% 1.00x ONLINE -
mypool 960M 166M 794M - - 8% 17% 1.00x ONLINE -查看备份目录结构:
# ls -al /backup/
total 169330
drwxr-xr-x 3 root wheel 4 Apr 16 11:56 .
drwxr-xr-x 22 root wheel 24 Apr 16 11:53 ..
-rw-r--r-- 1 root wheel 173869216 Apr 16 11:54 backup1
drwxr-xr-x 2 root wheel 3 Apr 16 11:53 mypool验证备份文件与源文件一致:
# ls -al /backup/mypool/bsdbook.pdf
-rw-r--r-- 1 root wheel 173330339 Apr 16 11:54 /backup/mypool/bsdbook.pdf16.4.8.3 增量备份
zfs send 还可以确定两个快照之间的差异,仅发送二者之间的增量。这既节省了磁盘空间,又缩短了传输时间。
再将若干 非重要 文件复制到 mypool 池中便于测试:
# cp /home/ykla/bsdbook.epub /mypool例如:
# zfs snapshot mypool@replica2查看三个快照的 REFER 值变化:
# zfs list -t snapshot mypool
NAME USED AVAIL REFER MOUNTPOINT
mypool@backup1 0B - 165M -
mypool@replica1 0B - 165M -
mypool@replica2 0B - 356M -查看增量复制前两个池的容量:
# zpool list backup mypool
NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT
backup 960M 331M 629M - - 12% 34% 1.00x ONLINE -
mypool 960M 356M 604M - - 12% 37% 1.00x ONLINE -以上创建了第二个快照 replica2。此快照包含自上一个快照 replica1 以来文件系统发生的所有更改。
使用 zfs send -i 并指定这两个快照对,会生成仅包含已更改数据的增量复制流。若初始快照已存在于接收端,此操作便成功。
# zfs send -v -i mypool@replica1 mypool@replica2 | zfs receive backup/mypool
send from mypool@replica1 to mypool@replica2 estimated size is 191M
total estimated size is 191M
TIME SENT SNAPSHOT mypool@replica2增量发送后,查看两个池的空间占用——备份池仅增加了差异数据量:
# zpool list backup mypool
NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT
backup 960M 522M 438M - - 13% 54% 1.00x ONLINE -
mypool 960M 356M 604M - - 12% 37% 1.00x ONLINE -列出两个池及其数据集,查看空间分配:
# zfs list -r backup mypool
NAME USED AVAIL REFER MOUNTPOINT
backup 522M 310M 165M /backup
backup/mypool 356M 310M 356M /backup/mypool
mypool 356M 476M 356M /mypool查看备份池中的快照列表:
# zfs list -t snapshot backup mypool
NAME USED AVAIL REFER MOUNTPOINT
mypool@backup1 0B - 165M -
mypool@replica1 0B - 165M -
mypool@replica2 0B - 356M -增量流仅复制了已更改的数据,而非整个 replica1。仅发送差异可大幅缩短传输时间,并避免每次都复制整个池而节省磁盘空间。在通过慢速网络或按传输字节计费的网络复制时,这一优势尤为明显。
至此,新的文件系统 backup/mypool 已可用,其中包含 mypool 池的数据和文件。
确认增量备份文件已存在于备份池中:
# ls -al /backup/mypool/bsdbook.epub
-rw-r--r-- 1 root wheel 199611278 Apr 16 11:57 /backup/mypool/bsdbook.epub使用 -p 选项可复制数据集的属性,包括压缩设置、配额和挂载点。使用 -R 选项可复制数据集的所有子数据集及其属性。发送和接收过程可以自动化,以便在第二个池上定期创建备份。
16.4.8.4 通过 SSH 发送加密备份
通过网络发送流数据是维持远程备份的好方法,但存在一个缺点:经网络链路发送的数据未加密,任何人都可拦截流数据并将其还原为原始数据,而无需发送方的授权。这在通过互联网向远程主机发送数据时不可接受。为确保数据安全传输,可使用 SSH 加密发送的数据。由于 ZFS 要求将流从标准输出重定向,因此可通过 SSH 轻松管道传输。为确保文件系统的内容在传输过程中及在远程系统上均保持加密,可考虑使用 PEFS。
首先更改一些配置并采取安全预防措施。下文描述了执行 zfs send 操作所需的步骤:
配置更改如下:
在发送和接收主机之间使用 SSH 密钥实现无密码 SSH 访问
ZFS 需要
root用户的权限来发送和接收流。这要求以root身份登录到接收系统。出于安全原因,默认情况下禁止
root登录。使用 ZFS 授权系统来允许每个系统上的非
root用户执行相应的发送和接收操作。在发送系统上:sh# zfs allow -u 用户名 send,snapshot mypool要挂载池,非特权用户必须拥有该目录,而且常规用户需要有挂载文件系统的权限。
在接收系统上:
# sysctl vfs.usermount=1
vfs.usermount: 0 -> 1
# echo vfs.usermount=1 >> /etc/sysctl.conf
# zfs create recvpool/backup
# zfs allow -u 用户名 create,mount,receive recvpool/backup
# chown 用户名 /recvpool/backup现在,非特权用户也可以接收和挂载数据集,并将 home 数据集复制到远程系统。建议使用 IP 地址或完全限定域名。接收方将数据写入 recvpool 池上的 backup 数据集。
% zfs snapshot -r mypool/home@monday
% zfs send -R mypool/home@monday | ssh 用户名@backuphost zfs recv -dvu recvpool/backup首先在 mypool 池上为文件系统数据集 home 创建递归快照 monday。
然后 zfs send -R 会将数据集、所有子数据集、快照、克隆以及设置一并纳入流中。
输出经 SSH 管道传输到远程主机 backuphost 上等待的 zfs receive。
在 zfs recv 中:
-d选项会用快照名称覆盖接收方池的名称;-u选项使文件系统在接收方不挂载;-v选项可显示更多传输详情,包括耗时和数据量。
16.4.9 书签
书签是快照的轻量级引用,记录快照创建时刻的时间点位置。与快照不同,书签不持有数据块,因此创建后不占用任何额外磁盘空间,也无需像快照那样随数据变化而消耗空间。书签的典型用途是作为 zfs send 增量流的源,在没有完整快照的情况下实现增量复制。
书签功能需要存储池启用功能标志 bookmarks。在现代版本的 ZFS 中,该标志默认启用。
16.4.9.1 创建书签
使用 zfs bookmark 命令为快照 zroot/var/tmp@test 创建书签:
# zfs bookmark zroot/var/tmp@test zroot/var/tmp#mybookmark也可以基于已有书签再创建书签:
# zfs bookmark zroot/var/tmp#mybookmark zroot/var/tmp#mybookmark2与快照的 @ 分隔符不同,书签使用 # 分隔数据集名称与书签名。
16.4.9.2 列出书签
书签不会在普通的 zfs list 操作中显示。要列出书签,需使用 -t bookmark 选项:
# zfs list -t bookmark
NAME USED AVAIL REFER MOUNTPOINT
zroot/var/tmp#mybookmark - - 96K -
zroot/var/tmp#mybookmark2 - - 96K -由于书签不占用空间也不可挂载,USED、AVAIL、REFER 和 MOUNTPOINT 列均显示为 -。
16.4.9.3 使用书签增量发送
书签的主要价值在于增量复制。如果源系统的快照已销毁,但仍需向备份系统发送增量流,书签可替代快照作为增量源:
# zfs send -i zroot/var/tmp#mybookmark zroot/var/tmp@test | zfs receive backup/var/tmp管理员因此可以在销毁中间快照后仍能继续增量备份链,极大节省了存储空间。
16.4.9.4 销毁书签
使用 zfs destroy 命令销毁书签,与销毁快照的语法类似,但使用 # 分隔符:
# zfs destroy zroot/var/tmp#mybookmark2书签销毁后,若其是某个增量发送链唯一的源,则后续的增量发送将无法使用该书签作为基准。
16.4.9.5 书签与回滚
尝试回滚到非最新快照时,ZFS 会检测是否存在更新的快照和书签,并提示用户使用 -r 选项强制删除:
# zfs rollback zroot/var/tmp@test
cannot rollback to 'zroot/var/tmp@test': more recent snapshots or bookmarks exist
use '-r' to force deletion of the following snapshots and bookmarks:
zroot/var/tmp@test2
zroot/var/tmp@diff书签虽然不持有数据,但代表一个时间点引用,因此会阻止回滚到比其更早的快照,除非用户明确确认需要删除这些书签。
16.4.10 数据集、用户和组配额
数据集配额用于限制特定数据集可消耗的空间。引用配额与之类似,但它仅计算数据集本身使用的空间,不包含快照和子数据集。同样,用户配额和组配额可防止用户或组耗尽池或数据集的全部空间。
16.4.10.1 数据集配额
新增用户的家目录数据集通常会自动创建为 zroot/home/用户名,且 mountpoint 一般是 /home/用户名。
示例假设系统中存在用户 ykla。如下:
# zfs list -r zroot/home
NAME USED AVAIL REFER MOUNTPOINT
zroot/home 356M 48.7G 96K /home
zroot/home/ykla 356M 48.7G 356M /home/ykla要对 zroot/home/ykla 强制实施 1 GB 的数据集配额:
# zfs set quota=1G zroot/home/ykla要对 zroot/home/ykla 强制实施 2 GB 的引用配额:
# zfs set refquota=2G zroot/home/ykla要删除 zroot/home/ykla 的 10 GB 配额:
# zfs set quota=none zroot/home/ykla16.4.10.2 用户配额
一般格式为 userquota@用户=大小 数据集或池,且用户的名称可以是以下任一格式:
- POSIX 兼容的名称,如 ykla。
- POSIX 数字 ID,如 789。
- SID 名称,如 ykla@example.com
- SID 数字 ID,如 S-1-123-456-789。
例如,要为用户 ykla 强制实施 50 GB 的用户配额:
# zfs set userquota@ykla=50G zroot用户配额属性不会通过 zfs get all 显示。除非授予 userquota 特权,非 root 用户无法看到其他用户的配额。具有此特权的用户可以查看并设置所有人的配额。
要查看用户配额:
# zfs get userquota@ykla zroot
NAME PROPERTY VALUE SOURCE
zroot userquota@ykla 50G local要删除所有配额:
# zfs set userquota@ykla=none zroot确认用户配额已清除:
# zfs get userquota@ykla zroot
NAME PROPERTY VALUE SOURCE
zroot userquota@ykla none local16.4.10.3 组配额
设置组配额的一般格式为:groupquota@组=大小 数据集或池。
要将组 ykla 的配额设置为 50 GB,请使用:
# zfs set groupquota@ykla=50G zroot与用户配额属性类似,非 root 用户可以查看其所属组的配额。具有 groupquota 特权的用户或 root 可以查看并设置所有组的配额。
确认组配额已生效:
# zfs get groupquota@ykla zroot
NAME PROPERTY VALUE SOURCE
zroot groupquota@ykla 50G local要删除组 ykla 的配额,或者确保没有设置配额,请使用:
# zfs set groupquota@ykla=none zroot确认组配额已清除:
# zfs get groupquota@ykla zroot
NAME PROPERTY VALUE SOURCE
zroot groupquota@ykla none local16.4.10.4 查看配额使用情况
要显示每个用户在文件系统或快照中使用的空间以及所有配额,请使用 zfs userspace。有关组信息,请使用 zfs groupspace。
特权用户和 root 可以列出 zroot/home/ykla 的配额,方法是:
# zfs get quota zroot/home/ykla
NAME PROPERTY VALUE SOURCE
zroot/home/ykla quota none local16.4.11 保留空间
保留空间确保数据集始终拥有可用空间(保留的空间不会分配给任何其他数据集),对保障关键数据集或日志文件的空间非常实用。
reservation 属性的格式为 reservation=大小,以下命令将为 zroot/home/ykla 设置 10 GB 的保留空间:
# zfs set reservation=10G zroot/home/ykla以下命令显示 zroot/home/ykla 上已有的保留空间:
# zfs get reservation zroot/home/ykla
NAME PROPERTY VALUE SOURCE
zroot/home/ykla reservation 10G local要清除所有保留空间:
# zfs set reservation=none zroot/home/ykla确认保留空间已清除:
# zfs get reservation zroot/home/ykla
NAME PROPERTY VALUE SOURCE
zroot/home/ykla reservation none local16.4.11.1 引用预留
refreservation 属性用于设置引用预留(refreservation),适用同样的原则,格式为 refreservation=size。
以下命令将为 zroot/home/ykla 设置 1 GB 的引用预留:
# zfs set refreservation=1G zroot/home/ykla以下命令显示 zroot/home/ykla 上已有的引用预留:
# zfs get refreservation zroot/home/ykla
NAME PROPERTY VALUE SOURCE
zroot/home/ykla refreservation 1G local要清除所有引用预留:
# zfs set refreservation=none zroot/home/ykla确认引用预留已清除:
# zfs get refreservation zroot/home/ykla
NAME PROPERTY VALUE SOURCE
zroot/home/ykla refreservation none local16.4.12 压缩
ZFS 的数据压缩在文件系统层面实现,对上层应用透明,启用后通常既能减少磁盘占用,又能提升读写吞吐量。
管理员可以通过数据集属性查看压缩效果。
# zfs get used,compressratio,compression,logicalused zroot
NAME PROPERTY VALUE SOURCE
zroot used 3.94G -
zroot compressratio 1.94x -
zroot compression on local
zroot logicalused 1.73G -将 zroot 文件系统的数据压缩算法设置为 zstd-5 级别:
# zfs set compression=zstd-5 zroot注意
压缩属性变更立即生效,无需重启系统。但该属性仅对新写入的数据生效,不会自动压缩已有的数据。
再次列出各个 ZFS 文件系统的数据压缩属性及其当前设置:
# zfs get compression
NAME PROPERTY VALUE SOURCE
zroot compression zstd-5 local
zroot/ROOT compression zstd-5 inherited from zroot
zroot/ROOT/default compression zstd-5 inherited from zroot
zroot/home compression zstd-5 inherited from zroot
zroot/home/ykla compression zstd-5 inherited from zroot
zroot/tmp compression zstd-5 inherited from zroot
zroot/usr compression zstd-5 inherited from zroot
zroot/usr/ports compression zstd-5 inherited from zroot
zroot/usr/src compression zstd-5 inherited from zroot
zroot/var compression zstd-5 inherited from zroot
zroot/var/audit compression zstd-5 inherited from zroot
zroot/var/crash compression zstd-5 inherited from zroot
zroot/var/log compression zstd-5 inherited from zroot
zroot/var/mail compression zstd-5 inherited from zroot
zroot/var/tmp compression zstd-5 inherited from zroot查看各个 ZFS 文件系统的实际数据压缩比:
技巧
compressratio表示已压缩数据与未压缩数据的比值。例如 2.70x 表示数据压缩到原始大小的约 37%。
# zfs get compressratio
NAME PROPERTY VALUE SOURCE
zroot compressratio 2.70x -
zroot/ROOT compressratio 2.68x -
zroot/ROOT/default compressratio 2.68x -
zroot/home compressratio 1.00x -
zroot/home/ykla compressratio 1.01x -
zroot/tmp compressratio 1.00x -
zroot/usr compressratio 2.73x -
zroot/usr/ports compressratio 1.00x -
zroot/usr/src compressratio 2.73x -
zroot/var compressratio 1.47x -
zroot/var/audit compressratio 1.00x -
zroot/var/crash compressratio 1.01x -
zroot/var/log compressratio 2.90x -
zroot/var/mail compressratio 1.00x -
zroot/var/tmp compressratio 1.00x -ZFS 提供了不同的压缩算法,各有优劣。不同压缩算法在压缩比、压缩速度与解压速度方面表现各异,需根据工作负载特征选择。
- LZ4:需要池启用
lz4_compress功能标志(GUID:org.illumos:lz4_compress),启用后即为当前默认压缩算法。LZ4 处理可压缩数据比 LZJB 快约 50%,处理不可压缩数据快三倍以上,解压速度也比 LZJB 快约 80%。在现代 CPU 上,LZ4 单核压缩速度通常超过 500 MB/s,解压速度超过 1.5 GB/s。 - LZJB:由 ZFS 创始人之一 Jeff Bonwick 设计,在未启用 LZ4 功能标志的旧池上是默认压缩算法。LZJB 压缩效果良好且 CPU 开销低于 GZIP。
- ZSTD:一种高性能压缩算法(GUID:
org.freebsd:zstd_compress),兼具高压缩比与高速度。相比 GZIP 在更高速度下提供略好的压缩比,相比 LZ4 提供更好的压缩比而速度仅略慢。可通过zstd-N(N=1~19)指定压缩级别,zstd等同于zstd-3。可通过zstd-fast-N指定快速模式,其中 N 为1–10, 20, 30, …, 100, 500, 1000中的整数,映射为负 zstd 级别;级别越低压缩越快,1000 提供最快压缩和最低压缩比。zstd-fast等同于zstd-fast-1。 - GZIP:流行压缩算法,主要优势在于可配置压缩级别。设置
compression属性时,管理员可从gzip-1(最快)到gzip-9(最佳压缩比)之间选择,以在 CPU 时间与磁盘空间之间取得平衡。gzip等同于gzip-6(这也是 gzip(1) 的默认级别)。 - ZLE:零长度编码,仅压缩连续零块,适用于包含大量零块的数据集。
压缩与用户配额结合时可能产生意外的副作用。用户配额限制的是压缩后用户实际消耗的空间。若一个用户的配额为 10 GB,写入了 10 GB 可压缩数据,则他仍能存储更多数据。如果他后续更新某个文件(例如数据库),使用更多或更少可压缩数据时,其可用空间量将发生变化。这可能造成一种奇怪的情况:用户并未增加实际数据量(logicalused 属性),但因压缩率变化而触及配额上限。
压缩与备份的交互也可能出现类似的意外效果。配额通常用于限制数据存储以确保预留足够的备份空间。由于配额不考虑压缩因素,ZFS 可能写入比未压缩备份更多的数据。
16.4.13 去重
启用去重可节省存储空间,但需大量内存支撑去重表。在启用前应先评估工作负载是否真正需要去重——压缩在不增加额外开销的情况下即可提供大部分空间节省。
并非所有数据都适合去重。如果池中的数据没有冗余,去重未必能带来节省。ZFS 可通过模拟去重来预估潜在的空间节省(必须先导出池):
# zdb -S mypool
Simulated DDT histogram:
bucket allocated referenced
______ ______________________________ ______________________________
refcnt blocks LSIZE PSIZE DSIZE blocks LSIZE PSIZE DSIZE
------ ------ ----- ----- ----- ------ ----- ----- -----
1 285 35.5M 35.0M 35.0M 285 35.5M 35.0M 35.0M
2 278 34.8M 34.3M 34.3M 834 104M 103M 103M
Total 563 70.3M 69.3M 69.3M 1.09K 140M 138M 138M
dedup = 1.99, compress = 1.01, copies = 1.00, dedup * compress / copies = 2.02zdb -S 完成分析后,会显示启用去重可能带来的空间节省比例。
本例中 dedup 值 2.02 是一个较高的值,节省主要来自压缩。若在此池上启用去重,不仅不会节省任何空间,启用去重所需的内存开销也得不偿失。
系统管理员可使用以下公式来规划存储分配,判断工作负载的重复块数量是否值得投入相应内存。
如果 dedup 值接近 2,则推荐去重;如果 dedup 接近 1,则不推荐去重。
如果数据具有良好的可压缩性,空间节省可能非常可观。
最佳实践是先启用压缩,因为压缩也能带来显著的性能提升。仅在去重能提供明显节省且拥有足够内存支撑 DDT 的情况下才启用去重。
要启用去重,请在目标池上设置 dedup 属性:
# zfs set dedup=on zroot去重仅影响写入池的新增数据,仅启用此选项不会对已写入的数据生效。
新启用去重的池将显示如下:
# zpool list zroot
NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT
zroot 51.5G 1.21G 50.3G - - 0% 2% 1.00x ONLINE -DEDUP 列显示池的实际去重率。1.00x 表示数据尚未去重。
下面的例子将随机生成块文件,然后将之分别复制到两个不同文件名观察去重效果:
# dd if=/dev/urandom of=/zroot/testfile bs=1M count=100
# cp /zroot/testfile /zroot/testfile.copy1
# cp /zroot/testfile /zroot/testfile.copy2要观察冗余数据的去重效果,可使用:
zpool list zroot
NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT
zroot 51.5G 1.31G 50.2G - - 0% 2% 2.99x ONLINE -DEDUP 列显示 3.00x 的去重系数。检测并去重数据副本仅使用了三分之一的空间。空间节省潜力巨大,但需要足够的内存来跟踪去重块。
还可以使用 zdb 命令:
# zdb -DD zroot
DDT-sha256: version=1 [FDT]; flags=0x03 [FLAT LOG]; rootobj=154
DDT-sha256-zap-unique: dspace=221184; mspace=163840; entries=643
DDT-log-sha256-0: flags=0x01 [FLUSHING]; obj=155; len=393216; txg=593; entries=802
DDT-log-sha256-1: flags=0x00; obj=156; len=0; txg=601; entries=0
DDT histogram (aggregated over all DDTs):
bucket allocated referenced
______ ______________________________ ______________________________
refcnt blocks LSIZE PSIZE DSIZE blocks LSIZE PSIZE DSIZE
------ ------ ----- ----- ----- ------ ----- ----- -----
1 4 8.50K 8.50K 16K 4 8.50K 8.50K 16K
2 800 100M 100M 100M 2.34K 300M 300M 300M
Total 804 100M 100M 100M 2.35K 300M 300M 300M
dedup = 3.00, compress = 1.00, copies = 1.00, dedup * compress / copies = 3.0016.4.14 参考文献
- 金步国. GPT 分区详解[EB/OL]. [2026-04-02]. https://www.jinbuguo.com/storage/gpt.html. GPT 分区基础技术文档。
- 匆匆过客. 如何轻松改变分区类型 ID?试试这 2 种方法![EB/OL]. (2022-11-20)[2026-04-02]. https://www.disktool.cn/content-center/change-partition-type-id-2111.html. 介绍分区类型 ID 与分区 UUID 的区别。
- FreeBSD Project. zfs -- configures ZFS datasets[EB/OL]. [2026-04-14]. https://man.freebsd.org/cgi/man.cgi?query=zfs&sektion=8. ZFS 数据集管理工具手册页,涵盖快照、克隆与回滚操作。
- FreeBSD Project. zpool -- configures ZFS storage pools[EB/OL]. [2026-04-14]. https://man.freebsd.org/cgi/man.cgi?query=zpool&sektion=8. ZFS 存储池管理工具手册页。
- FreeBSD Project. FreeBSD Handbook, Chapter 21: The Z File System (ZFS)[EB/OL]. [2026-04-14]. https://docs.freebsd.org/en/books/handbook/zfs/. FreeBSD 手册中关于 ZFS 的完整配置与管理指南。
- OpenZFS Project. zfs-rollback(8)[EB/OL]. [2026-04-18]. https://openzfs.github.io/openzfs-docs/man/master/8/zfs-rollback.8.html. 详细说明了 -r 参数用于销毁更新的快照,而非递归回滚子数据集。
- FreeBSD Project. zfsprops -- native and user-defined properties of ZFS datasets[EB/OL]. [2026-04-14]. https://man.freebsd.org/cgi/man.cgi?query=zfsprops&sektion=7. ZFS 数据集属性手册页,定义 compression、compressratio 等属性。