> For the complete documentation index, see [llms.txt](https://book.bsdcn.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://book.bsdcn.org/di-24-zhang-zfs-wen-jian-xi-tong/di-24.5-jie-zfs-guan-li.md).

# 24.5 ZFS 管理

## 创建和销毁数据集

与传统磁盘和卷管理器不同，ZFS 不会预先分配空间。传统文件系统在分区并分配空间后，若不添加新磁盘，便无法创建新的文件系统。而 ZFS 允许随时创建新的文件系统。每个 **数据集** 都具备压缩、去重、缓存和配额等特性，此外还有只读、大小写敏感、网络文件共享和挂载点等实用属性。数据集可以嵌套，子数据集会继承父数据集的属性。每个数据集都可以委托管理、复制、创建快照、纳入 Jail，也可以直接销毁。为每种文件类型或文件集合创建单独的数据集是一种推荐做法。然而，数据集过多亦存在不足：`zfs list` 等命令会变慢，挂载数百乃至数千个数据集也会拖慢 FreeBSD 的启动速度。

查看目前的数据集：

```sh
# 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
```

> **技巧**
>
> 本节示例中出现的用户名 `ykla` 及路径 **/home/ykla** 均为示例，请根据自身环境替换为实际用户名和主目录。

创建新的数据集并启用 LZ4 压缩：

```sh
# 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` 预览待销毁的数据集和快照。

销毁创建的数据集：

```sh
# 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` 属性：

```sh
NAME   PROPERTY  VALUE    SOURCE
zroot  freeing   0        -
```

该属性会显示哪些数据集正在后台释放其块。如果有子数据集（例如快照或其他数据集），则无法直接销毁父数据集。要销毁数据集及其所有子数据集，可使用 `-r` 递归销毁。使用 `-n -v` 可以列出此操作将销毁的数据集和快照，但不会实际销毁任何数据。销毁快照时，回收的空间也会一并显示。

## 创建和销毁卷

卷是一种特殊的数据集类型。它不作为文件系统挂载，而是以块设备的形式暴露于 **/dev/zvol/poolname/dataset** 路径下。因此，卷可用于其他文件系统、充当虚拟机磁盘，或通过 iSCSI 及 HAST 等协议提供给网络上的其他主机。

卷可以格式化并使用任何文件系统，也可以不格式化而直接存储原始数据。对用户而言，卷与普通磁盘无差异。在这些 *zvol* 上放置普通文件系统，可以获得普通磁盘或文件系统所不具备的功能。例如，对一个 250 MB 的卷启用压缩属性，即可创建一个压缩的 FAT 文件系统。

创建 3 GB 的 ZFS 卷并启用压缩：

```sh
# zfs create -V 3G -o compression=on zroot/fat32
```

> **技巧**
>
> 如果设置的 FAT32 容量过低，由于文件簇不足，将无法正常格式化。

确认卷已创建并查看其空间占用：

```sh
# zfs list zroot/fat32
NAME          USED  AVAIL  REFER  MOUNTPOINT
zroot/fat32  3.05G  49.0G    56K  -
```

卷以块设备形式出现在 **/dev/zvol/** 路径下：

```sh
# 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 文件系统：

```sh
# 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
```

挂载卷并确认挂载成功：

```sh
# mount -t msdosfs /dev/zvol/zroot/fat32 /mnt
# mount | grep fat32
/dev/zvol/zroot/fat32 on /mnt (msdosfs, local)
```

复制一些文件以测试：

```sh
# cp /home/ykla/Philosophische-untersuchungen.pdf /mnt/
# df -h /mnt | grep fat32
/dev/zvol/zroot/fat32    3.0G     36M    3.0G     1%    /mnt
```

销毁卷的过程与销毁常规文件系统数据集类似：

```sh
# zfs destroy zroot/fat32
```

该操作几乎即时完成，但空闲空间可能需要几分钟才能在后台回收。

## 重命名数据集

要更改数据集的名称，可使用 `zfs rename` 命令。要更改数据集的父级，同样使用该命令。将数据集重命名至不同的父级下，会改变其从父级继承的属性值。重命名数据集时，会卸载该数据集并将其挂载到新位置（该位置从新父级继承）。要阻止此行为，可使用 `-u` 选项。

将数据集重命名并移动到不同的父级：

首先创建用于演示的数据集并确认其位置：

```sh
# 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
```

给数据集创建快照，演示重命名后快照仍会保留：

```sh
# 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 下：

```sh
# 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

```

快照无法更改父级。再创建一个快照验证：

```sh
# 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  -
```

重命名快照也使用相同的命令。

```sh
# 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` 同样适用于递归重命名数据集及其所有子数据集。

## 设置数据集属性

每个 ZFS 数据集都有一组控制其行为的属性。大多数属性默认从父数据集继承，但可以单独覆盖。使用 `zfs set` 命令设置数据集属性，语法为 `<property=value dataset>`。大多数属性的有效值范围有限，`zfs get` 会列出每种属性及其有效值。通过 `zfs get all <数据集>` 可查看数据集当前的所有属性（包括继承值和本地设置值），方便问题诊断与状态确认。使用 `zfs inherit` 可以将大多数属性恢复为继承值。此外，还可以定义用户自定义属性，这些属性成为数据集配置的一部分，可提供关于数据集或其内容的附加信息。为区分自定义属性与 ZFS 内置属性，可使用冒号（`:`）创建自定义命名空间。

设置自定义属性并查看：

```sh
# 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` 选项。如果自定义属性在任何父级数据集中未定义，此选项将删除它（但池的历史记录仍然会记录该更改）。

删除自定义属性并验证：

```sh
# 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
#
```

### 获取和设置共享属性

两个常用且实用的数据集属性是 NFS 和 SMB 共享选项。设置这些属性可以定义 ZFS 是否通过网络共享数据集以及如何共享。FreeBSD 同时支持 NFS（`sharenfs`）和 SMB（`sharesmb`）共享属性，其中 SMB 共享需安装 Samba。以下以 NFS 为例，要获取共享的当前状态，可输入：

```sh
# zfs get sharenfs zroot/home
NAME        PROPERTY  VALUE     SOURCE
zroot/home  sharenfs  off       default
```

要启用数据集的共享，可以输入：

```sh
# zfs set sharenfs=on zroot/home
# zfs get sharenfs zroot/home
NAME        PROPERTY  VALUE     SOURCE
zroot/home  sharenfs  on        local
```

可以为通过 NFS 共享的数据集设置其他选项，如 `-alldirs`、`-maproot` 和 `-network`。要设置共享选项，可以输入：

```sh
# 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  local
```

> **警告**
>
> `-maproot=root` 将 NFS 客户端的 root 用户映射为服务器端的 root，远程 root 用户将对共享数据集拥有完全读写权限。NFS 默认启用 `root_squash`（将客户端 root 映射为 `nobody`），使用 `-maproot=root` 相当于关闭此安全机制。建议仅在受信任的内部网络中使用，或改为 `-maproot=nobody` 以限制权限，也可映射到指定的非特权用户（如 `-maproot=1000`）。

## 管理快照

ZFS 快照采用写时复制（COW）机制，创建过程瞬时完成且不占用额外磁盘空间。快照创建后，原文件系统中的数据块发生修改时，ZFS 将新数据写入新位置，旧数据块原地保留，从而捕获文件系统在快照创建时刻的时间点状态。快照提供了数据集的只读、时间点副本。快照作用于整个数据集而非单个文件，在创建时不额外占用空间，随其引用的块发生变化而消耗空间。

```sh
ZFS 写时复制（COW）与快照机制：

  快照创建前：
     数据块 A ──► 数据块 B ──► 数据块 C

  快照创建瞬间（不复制数据，仅记录引用）：
     快照 @s ──► 引用 A、B、C
     数据块 A ──► 数据块 B ──► 数据块 C
     （快照不占用额外空间）

  数据块 B 被修改时（写时复制）：
     快照 @s ──► 引用 A、B（旧）、C
     数据块 A ──► 数据块 B'（新位置）──► 数据块 C
     数据块 B（旧，被快照引用，不释放）

  空间占用：
     快照仅占用"已变更数据块"的空间
     未变更的数据块由当前文件系统和快照共享
```

快照的典型用途是，在执行软件安装或系统升级等高风险操作之前，快速备份当前文件系统状态。如果操作失败，可回滚到快照恢复系统状态；如果升级成功，则删除快照释放空间。快照回滚速度很快，几乎不会造成停机。快照不能替代完整的池备份，但在特定时刻保存数据集副本方面极为高效。

FreeBSD 中文社区（CFC）提供了 ZFS 脚本，可用于查看、创建、删除和恢复 ZFS 快照（ZFS snapshot）。[ZFS 脚本项目地址](https://github.com/FreeBSD-Ask/zfs-snap)。该脚本已部署至 <https://docs.bsdcn.org/zfs.sh>，可在 FreeBSD 系统上直接使用 `fetch` 命令下载。

### 创建快照

在默认情况下，使用 Auto ZFS 布局创建的分区结构如下：

```sh
# 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` 选项可以递归创建快照，在所有子数据集上使用相同的名称。

创建整个池的递归快照：

```sh
# zfs snapshot -r zroot@test   # 为 zroot 池及其所有子文件系统递归创建快照 test
```

快照不会在正常的 `zfs list` 操作中显示。要列出快照，必须在 `zfs list` 命令后加上 `-t snapshot` 选项。`-t all` 会显示文件系统和快照。

```sh
# 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` 列中显示可用空间。可以通过以下命令比较快照与原数据集：

```sh
# 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*），而不是重新保存整个文件系统的内容。这意味着快照在发生变化时占用的空间极小。

通过将文件复制到数据集后再创建第二个快照，可以更清楚地观察空间占用情况：

```sh
# cp /COPYRIGHT /var/tmp
# zfs snapshot zroot/var/tmp@test2
```

查看创建第二个快照后各快照的空间占用变化：

```sh
# 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` 列中也发生了变化，这反映的是它与之后创建的快照之间的差异。

### 销毁快照

销毁快照时，可以使用 `-r` 参数递归删除：

```sh
# zfs destroy -r zroot@test   # 递归删除 zroot 池及其子文件系统的 test 快照
# zfs list -t snap             # 列出所有 ZFS 快照
no datasets available
```

## 快照保持

有时需要防止关键快照被误删——例如在备份流程尚未完成之前，或因法规要求必须保留的特定时间点副本。ZFS 的保持（hold）机制可对快照施加命名的轻量级引用，使其在保持解除前无法被 `zfs destroy` 删除。

为快照添加保持标签：

```sh
# zfs hold keeptest zroot/var/tmp@test
```

> **技巧**
>
> `keeptest` 只是便于识别的标签，可以修改为其他名称。

查看快照上的所有保持：

```sh
# zfs holds zroot/var/tmp@test
NAME                TAG       TIMESTAMP
zroot/var/tmp@test  keeptest  Wed May 13 13:18 2026
```

尝试销毁被保持的快照会返回错误：

```sh
# 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` 选项可递归地对所有子数据集的同名快照施加保持：

```sh
# 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
```

释放保持标签后，快照即可正常销毁：

```sh
# zfs release keeptest zroot/var/tmp@test
# zfs release keepall zroot/var/tmp@test
# zfs destroy zroot/var/tmp@test
```

`zfs send` 的 `-h` 选项可在发送流中包含保持标签。这对灾难恢复环境中保持备份完整性特别有用。

```sh
# zfs send -h zroot/var/tmp@test | zfs receive backup/var/tmp
```

接收端使用 `zfs receive` 接收该流时，快照的保持将在接收方自动重建。

### 比较快照

ZFS 提供了内建命令用于比较两个快照之间内容的差异。对于长期保存大量快照的场景，此功能极为实用，用户可借此查看文件系统随时间推移的变化。例如，`zfs diff` 可帮助用户找到最近的快照，检查其中是否仍包含误删除的文件。比较前面章节中创建的两个快照，得到以下输出：

```sh
# 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 复制功能将数据集传输到不同主机备份时非常有用。

通过完整的数据集名称和两个快照名称来比较两个快照：

```sh
# 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/
```

备份管理员可以比较从发送主机接收到的两个快照，并确定数据集中的实际变化。

### 快照回滚

只要存在至少一个快照，随时都可以回滚到该快照。回滚最常见的场景是：当前数据集的状态已失效，或者更适合使用旧版本。例如，本地开发测试出错、系统更新失败导致功能受损，或者需要恢复已删除的文件或目录——这些情况都很常见。要回滚到快照，可使用 `zfs rollback <snapshotname>` 命令。如果更改量较大，操作可能耗时较长。在此期间，数据集始终保持一致状态，如同符合 ACID 原则的数据库执行回滚一般。整个过程在数据集在线且无需停机的情况下完成。回滚之后，数据集的状态将恢复到快照创建时的状态。回滚到快照会丢弃该数据集中所有不属于该快照的数据。如果在回滚到更早快照之前先为当前状态创建快照，则之后需要某些数据时可以方便地回滚。这样，用户可以在快照之间来回切换，而不会丢失仍有价值的数据。

### 快照还原测试

可以通过增删文件来验证快照的有效性。在测试环境中，如果事先创建了快照，即使执行 `rm -rf /*` 命令也可以顺利恢复；如果系统使用 UEFI，则需要根据其他章节的说明自行恢复 EFI 引导。

> **注意**
>
> 此操作仅应在测试环境中执行。

假设在之前示例中，因误执行 `rm` 命令删除了超出预期的数据，需要回滚到快照：

首先查看当前可用的快照：

```sh
# 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  -
```

查看当前目录下的文件：

```sh
# ls /var/tmp
COPYRIGHT	COPYRIGHT.copy	vi.recover
```

模拟误删除操作，移除 COPYRIGHT 相关文件：

```sh
# rm /var/tmp/COPYRIGHT*
# ls /var/tmp
vi.recover
```

此时，用户发现误删了多余的文件，希望将其恢复。ZFS 提供了一种简便的恢复方式：只要定期为重要数据创建快照即可。回滚到上一个快照后，便可找回丢失的文件，并从该点重新开始：

执行回滚操作，将数据集恢复到 diff 快照的状态：

```sh
# 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
```

回滚操作将数据集恢复到最后一个快照的状态。

确认回滚后快照列表未变：

```sh
# 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 会显示以下警告：

```sh
# 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` 命令只能回滚到最新快照（[参考手册](https://docs.oracle.com/cd/E19253-01/819-7065/gbcxk/index.html)，Oracle 官方 ZFS 回滚命令文档）。由于快照是只读的，除非用户使用 `-r` 选项确认这是所需操作并销毁比目标快照更新的所有快照，否则 ZFS 无法跟踪数据集跨不同状态的所有更改。使用 `-r` 选项后，ZFS 将销毁比目标快照更新的所有快照，从而允许回滚到非最新的快照。

如果这确实是预期操作，且用户理解删除所有中间快照的后果，可执行以下命令：

```sh
# 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 不支持一次性递归回滚所有子数据集，需要对每个子文件系统单独执行回滚操作。

```sh
# 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 脚本项目](https://github.com/FreeBSD-Ask/zfs-snap)，或向 OpenZFS 项目提交功能请求与 Pull Request。

### 从快照中恢复单个文件

快照存储在父数据集下的隐藏目录 **.zfs/snapshots/快照名称** 中。

默认情况下，即使执行 `ls -a` 命令，这些目录也不会显示。尽管不可见，但仍可像普通目录一样访问。

属性 `snapdir` 控制着这些隐藏目录是否出现在目录列表中。将该属性设为 `visible` 后，它们便会显示在 `ls` 及其他涉及目录内容的命令输出中。

查看 snapdir 属性的当前设置，再将其切换为 visible：

```sh
# 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 目录找到并恢复：

```sh
# 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 后，仍可通过路径直接访问快照：

```sh
# zfs set snapdir=hidden zroot/var/tmp
# ls /var/tmp/.zfs/snapshot/test
vi.recover
```

从隐藏的 **.zfs/snapshot** 目录复制文件或目录非常简便。反过来尝试将文件复制到快照目录时，会出现以下错误：

尝试向快照目录写入文件，验证快照的只读特性：

```sh
# cp /COPYRIGHT /var/tmp/.zfs/snapshot/test/
cp: /var/tmp/.zfs/snapshot/test/COPYRIGHT: Read-only file system
```

此错误提醒用户：快照是只读的，创建后不可更改。由于向快照目录复制或删除文件会改变快照所代表的数据集状态，无法执行这些操作。

快照随父文件系统自快照创建以来所发生的变化而消耗空间。快照的 `written` 属性可跟踪该快照占用的空间。

要销毁快照并回收空间，可使用 `zfs destroy <数据集>@<快照>` 命令。加上 `-r` 选项可递归删除父数据集下所有同名快照。使用 `-n -v` 选项时，命令会列出将要删除的快照及其预计回收的空间，而不执行实际销毁。

## 管理克隆

克隆是快照的可写副本，可写、可挂载，且拥有自己的属性。使用 `zfs clone` 创建克隆之后，原始快照便无法销毁。要反转克隆与快照之间的父子关系，可使用 `zfs promote`。提升克隆后，快照将成为克隆的子项，原来父子关系下的空间占用计算方式也会相应改变。克隆可以挂载到 ZFS 文件系统层次结构中的任意位置。

```sh
ZFS 克隆创建与提升流程：

  阶段一：创建克隆
  ─────────────────
  原数据集（zroot/home/ykla）
     │
     │  zfs snapshot（创建快照）
     ▼
  快照 zroot/home/ykla@test
     │
     │  zfs clone（创建可写克隆）
     ▼
  克隆 zroot/home/ykla2
  （origin 属性指向 @test，依赖快照存在）

  阶段二：提升克隆（zfs promote）
  ─────────────────
  提升前：
     原数据集 ──► 快照 @test ◄── 克隆（origin）

  提升后：
     克隆（已提升为独立数据集）──► 快照 @test ◄── 原数据集
     （origin 属性清除，父子关系反转）

  阶段三：替换原数据集
  ─────────────────
     zfs destroy 原数据集
     │
     │  zfs rename（克隆重命名为原名称）
     ▼
  克隆成为新的独立数据集
```

### 创建克隆

下面是展示克隆功能的示例数据集：

```sh
# 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  -
```

克隆的典型用途是保留快照的同时，对特定数据集做实验，以便在出错时可以恢复。由于快照无法更改，可为其创建一个可读写的克隆。实验取得理想结果后，可将克隆提升为数据集，并删除初始的文件系统。由于克隆与数据集可以共存而不会引发问题，删除原始数据集并非必要。

从快照创建克隆，并验证克隆与原数据集内容一致：

```sh
# 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
```

查看克隆与原数据集的磁盘占用，二者共享同一份数据块：

```sh
# 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/ykla2
```

### 提升克隆

克隆创建时，它是快照创建时刻数据集的完整副本。此后，克隆可独立于原始数据集更改。两者之间由快照关联，ZFS 在 `origin` 属性中记录这一关联。使用 `zfs promote` 提升克隆后，克隆将变为独立的数据集，`origin` 属性的值已清除，克隆与快照之间的关联也随之断开。以下示例展示了这一过程：

提升前，查看克隆的 origin 属性，确认源自快照：

```sh
# zfs get origin zroot/home/ykla2
NAME              PROPERTY  VALUE                 SOURCE
zroot/home/ykla2  origin    zroot/home/ykla@test  -
```

执行提升操作：

```sh
# zfs promote zroot/home/ykla2
```

提升后 origin 属性已清除，克隆成为独立数据集：

```sh
# zfs get origin zroot/home/ykla2
NAME              PROPERTY  VALUE   SOURCE
zroot/home/ykla2  origin    -       -
```

完成一些更改后（例如将 **COPYRIGHT** 复制到提升后的克隆中），旧目录便已过时。此时可以用提升后的克隆替换它。要实现替换，先用 `zfs destroy` 删除旧数据集，再用 `zfs rename` 将克隆重命名为旧数据集的名称（或一个完全不同的名称）。

在克隆中添加新文件：

```sh
# cp /COPYRIGHT /home/ykla2
```

删除原始数据集：

```sh
# zfs destroy -f zroot/home/ykla
```

将克隆重命名为原数据集名称，完成替换：

```sh
# 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 所需的时间和管理开销。

## 复制

将数据仅存储在单个池的单一位置中，会使数据面临盗窃、自然灾害或人为破坏等风险。因此，定期备份整个池至关重要。ZFS 提供了内置的序列化功能，可将数据的流表示发送到标准输出。借助此功能，既可以将数据存储到连接至本地系统的另一个池中，也可以通过网络将数据发送到另一台系统。快照是这种复制的基础。用于复制数据的命令是 `zfs send` 和 `zfs receive`。

```sh
ZFS 复制（send/receive）数据流：

  全量复制（初次备份）：
  ──────────────────────
  源池 mypool
     │
     │  zfs snapshot mypool@replica1
     ▼
  快照 @replica1
     │
     │  zfs send（序列化为数据流）
     ▼
  数据流（stdout）
     │
     │  管道或 SSH 传输
     ▼
  zfs receive backup/mypool
     │
     │  还原为文件系统
     ▼
  备份池 backup/mypool（含 @replica1 快照）

  增量复制（后续备份）：
  ──────────────────────
  源池 mypool（已有 @replica1，新增 @replica2）
     │
     │  zfs send -i @replica1 @replica2
     │  （仅发送两个快照间的差异）
     ▼
  增量数据流
     │
     │  zfs receive backup/mypool
     ▼
  备份池更新至 @replica2 状态
  （仅写入变更的数据块）
```

创建所需测试存储池：

```sh
# 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  -
```

复制若干 **非重要** 文件以便于测试：

```sh
# cp /home/ykla/bsdbook.pdf /mypool/
```

以下示例展示了如何使用这两个池完成 ZFS 复制：

存储池 **mypool** 是主池，用于读写常规数据。第二个池 **backup** 作为备用，以防主池不可用。

请注意，ZFS 不会自动执行故障转移，需要管理员在必要时手动操作。利用快照可以获取一致的文件系统版本用以复制还原。

为 **mypool** 创建快照后，即可通过复制快照将其传输到 **backup** 池。此操作不包含自上次快照以来的更改。

```sh
# zfs snapshot mypool@backup1
```

确认快照已创建：

```sh
# zfs list -t snapshot mypool
NAME             USED  AVAIL  REFER  MOUNTPOINT
mypool@backup1     0B      -   165M  -
```

现在已经存在一个快照，可使用 `zfs send` 创建代表快照内容的流。将此流存储为文件，或在另一个池上接收。

必须将流重定向到文件或管道，否则会出现如下错误：

```sh
# zfs send mypool@backup1
Error: Stream can not be written to a terminal.
You must redirect standard output.
```

### 创建备份流

要备份数据集，可在使用 `zfs send` 时将流重定向到已挂载备份池上的文件。

请确保池有足够的可用空间容纳所发送快照的大小（即快照中包含的数据总量，而非与上一个快照之间的差异容量）。

```sh
# zfs send mypool@backup1 > /backup/backup1
```

查看发送后两个池的空间占用变化：

```sh
# 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 任务。

### 接收备份流

与作为归档文件存储不同，ZFS 可将数据接收为活动的文件系统，从而直接访问备份数据。要访问这些流中的实际数据，可使用 `zfs receive` 将流还原为文件和目录。

以下示例将 `zfs send` 与 `zfs receive` 结合，通过管道将数据从池 **mypool** 复制到另一个池 **backup**。

先为 mypool 创建快照：

```sh
# zfs snapshot mypool@replica1
```

传输完成后，可直接在接收池上使用数据。**只能将数据集复制到空数据集。**

执行全量发送与接收：

```sh
# 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
```

查看接收后两个池的空间占用：

```sh
# 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  -
```

查看备份目录结构：

```sh
# 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
```

验证备份文件与源文件一致：

```sh
# ls -al /backup/mypool/bsdbook.pdf
-rw-r--r--  1 root wheel 173330339 Apr 16 11:54 /backup/mypool/bsdbook.pdf
```

### 增量备份

`zfs send` 还可以确定两个快照之间的差异，仅发送二者之间的增量。这既节省了磁盘空间，又缩短了传输时间。

再将若干 **非重要** 文件复制到 **mypool** 池中便于测试：

```sh
# cp /home/ykla/bsdbook.epub /mypool
```

例如：

```sh
# zfs snapshot mypool@replica2
```

查看三个快照的 REFER 值变化：

```sh
# zfs list -t snapshot mypool
NAME              USED  AVAIL  REFER  MOUNTPOINT
mypool@backup1      0B      -   165M  -
mypool@replica1     0B      -   165M  -
mypool@replica2     0B      -   356M  -
```

查看增量复制前两个池的容量：

```sh
# 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` 并指定这两个快照对，会生成仅包含已更改数据的增量复制流。若初始快照已存在于接收端，此操作便成功。

```sh
# 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
```

增量发送后，查看两个池的空间占用——备份池仅增加了差异数据量：

```sh
# 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  -
```

列出两个池及其数据集，查看空间分配：

```sh
# 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
```

查看备份池中的快照列表：

```sh
# 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** 池的数据和文件。

确认增量备份文件已存在于备份池中：

```sh
# ls -al /backup/mypool/bsdbook.epub
-rw-r--r--  1 root wheel 199611278 Apr 16 11:57 /backup/mypool/bsdbook.epub
```

使用 `-p` 选项可复制数据集的属性，包括压缩设置、配额和挂载点。使用 `-R` 选项可复制数据集的所有子数据集及其属性。发送和接收过程可以自动化，以便在第二个池上定期创建备份。

### 通过 SSH 发送加密备份

通过网络发送流数据是维持远程备份的有效手段，但存在一个缺点：经网络链路发送的数据未加密，任何人都可拦截流数据并将其还原为原始数据，而无需发送方的授权。这在通过互联网向远程主机发送数据时不可接受。为确保数据安全传输，可使用 SSH 加密发送的数据。由于 ZFS 要求将流从标准输出重定向，可通过 SSH 轻松管道传输。为确保文件系统内容在远程系统上静态存储时保持加密，推荐使用 ZFS 原生加密。在接收方创建加密数据集：

```sh
# zfs create -o encryption=aes-256-gcm -o keyformat=passphrase recvpool/backup
```

接收的流数据将自动以加密形式写入磁盘。ZFS 原生加密自 OpenZFS 0.8.0 起已成熟可用，与 ZFS 快照、发送/接收无缝集成，且无需额外安装内核模块。

首先更改一些配置并采取安全预防措施。下文描述了执行 `zfs send` 操作所需的步骤：

配置更改如下：

* 在发送和接收主机之间使用 SSH 密钥实现无密码 SSH 访问
* ZFS 需要 `root` 用户的权限来发送和接收流。这要求以 `root` 身份登录到接收系统。
* 出于安全原因，默认情况下禁止 `root` 登录。
* 使用 ZFS 授权系统来允许每个系统上的非 `root` 用户执行相应的发送和接收操作。在发送系统上：

  ```sh
  # zfs allow -u 用户名 send,snapshot mypool
  ```
* 要挂载池，非特权用户必须拥有该目录，而且常规用户需要有挂载文件系统的权限。

在接收系统上：

```sh
# 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* 数据集。

```sh
% 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` 选项可显示更多传输详情，包括耗时和数据量。

## 书签

书签是快照的轻量级引用，记录快照创建时刻的时间点位置。与快照不同，书签不持有数据块，因此创建后不占用任何额外磁盘空间，也无需像快照那样随数据变化而消耗空间。书签的典型用途是作为 `zfs send` 增量流的源，在没有完整快照的情况下实现增量复制。

书签功能需要存储池启用功能标志 `bookmarks`。在现代版本的 ZFS 中，该标志默认启用。

### 创建书签

使用 `zfs bookmark` 命令为快照 `zroot/var/tmp@test` 创建书签：

```sh
# zfs bookmark zroot/var/tmp@test zroot/var/tmp#mybookmark
```

也可以基于已有书签再创建书签：

```sh
# zfs bookmark zroot/var/tmp#mybookmark zroot/var/tmp#mybookmark2
```

与快照的 `@` 分隔符不同，书签使用 `#` 分隔数据集名称与书签名。

### 列出书签

书签不会在普通的 `zfs list` 操作中显示。要列出书签，需使用 `-t bookmark` 选项：

```sh
# zfs list -t bookmark
NAME                        USED  AVAIL  REFER  MOUNTPOINT
zroot/var/tmp#mybookmark       -      -    96K  -
zroot/var/tmp#mybookmark2      -      -    96K  -
```

由于书签不占用空间也不可挂载，`USED`、`AVAIL` 和 `MOUNTPOINT` 列均显示为 `-`。`REFER` 列显示书签创建时刻数据集引用的数据量。

### 使用书签增量发送

书签的主要价值在于增量复制。如果源系统的快照已销毁，但仍需向备份系统发送增量流，书签可替代快照作为增量源：

```sh
# zfs send -i zroot/var/tmp#mybookmark zroot/var/tmp@test | zfs receive backup/var/tmp
```

管理员因此可以在销毁中间快照后仍能继续增量备份链，极大节省了存储空间。

### 销毁书签

使用 `zfs destroy` 命令销毁书签，与销毁快照的语法类似，但使用 `#` 分隔符：

```sh
# zfs destroy zroot/var/tmp#mybookmark2
```

书签销毁后，若其是某个增量发送链唯一的源，则后续的增量发送将无法使用该书签作为基准。

### 书签与回滚

尝试回滚到非最新快照时，ZFS 会检测是否存在更新的快照和书签，并提示用户使用 `-r` 选项强制删除：

```sh
# 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
```

书签虽然不持有数据，但代表一个时间点引用，因此会阻止回滚到比其更早的快照，除非用户明确确认需要删除这些书签。

## 数据集、用户和组配额

数据集配额用于限制特定数据集可消耗的空间。引用配额与之类似，但它仅计算数据集本身使用的空间，不包含快照和子数据集。同样，用户配额和组配额可防止用户或组耗尽池或数据集的全部空间。

### 数据集配额

新增用户的家目录数据集通常会自动创建为 `zroot/home/用户名`，且 `mountpoint` 一般是 **/home/用户名**。

示例假设系统中存在用户 `ykla`。如下：

```sh
# 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 的数据集配额：

```sh
# zfs set quota=1G zroot/home/ykla
```

要对 **zroot/home/ykla** 强制实施 2 GB 的引用配额：

```sh
# zfs set refquota=2G zroot/home/ykla
```

要删除 **zroot/home/ykla** 的 1 GB 配额：

```sh
# zfs set quota=none zroot/home/ykla
```

### 用户配额

一般格式为 `userquota@用户=大小 数据集或池`，且用户的名称可以是以下任一格式：

| 格式          | 示例                                            |
| ----------- | --------------------------------------------- |
| POSIX 兼容的名称 | *ykla*                                        |
| POSIX 数字 ID | *789*                                         |
| SID 名称      | [*ykla@example.com*](mailto:ykla@example.com) |
| SID 数字 ID   | *S-1-123-456-789*                             |

例如，要为用户 **ykla** 强制实施 50 GB 的用户配额：

```sh
# zfs set userquota@ykla=50G zroot
```

用户配额属性不会通过 `zfs get all` 显示。除非授予 `userquota` 特权，非 `root` 用户无法看到其他用户的配额。具有此特权的用户可以查看并设置所有人的配额。

要查看用户配额：

```sh
# zfs get userquota@ykla  zroot
NAME   PROPERTY        VALUE           SOURCE
zroot  userquota@ykla  50G             local
```

要删除所有配额：

```sh
# zfs set userquota@ykla=none zroot
```

确认用户配额已清除：

```sh
# zfs get userquota@ykla  zroot
NAME   PROPERTY        VALUE           SOURCE
zroot  userquota@ykla  none            local
```

### 组配额

设置组配额的一般格式为：`groupquota@组=大小 数据集或池`。

要将组 *ykla* 的配额设置为 50 GB，请使用：

```sh
# zfs set groupquota@ykla=50G zroot
```

与用户配额属性类似，非 `root` 用户可以查看其所属组的配额。具有 `groupquota` 特权的用户或 `root` 可以查看并设置所有组的配额。

确认组配额已生效：

```sh
# zfs get groupquota@ykla zroot
NAME   PROPERTY         VALUE            SOURCE
zroot  groupquota@ykla  50G              local
```

要删除组 *ykla* 的配额，或者确保没有设置配额，请使用：

```sh
# zfs set groupquota@ykla=none zroot
```

确认组配额已清除：

```sh
# zfs get groupquota@ykla zroot
NAME   PROPERTY         VALUE            SOURCE
zroot  groupquota@ykla  none             local
```

### 查看配额使用情况

要显示每个用户在文件系统或快照中使用的空间以及所有配额，请使用 `zfs userspace`。有关组信息，请使用 `zfs groupspace`。

特权用户和 `root` 可以列出 **zroot/home/ykla** 的配额，方法是：

```sh
# zfs get quota zroot/home/ykla
NAME             PROPERTY  VALUE  SOURCE
zroot/home/ykla  quota     none   local
```

## 保留空间

保留空间确保数据集始终拥有可用空间（保留的空间不会分配给任何其他数据集），对保障关键数据集或日志文件的空间非常实用。

`reservation` 属性的格式为 `reservation=大小`，以下命令将为 **zroot/home/ykla** 设置 10 GB 的保留空间：

```sh
# zfs set reservation=10G zroot/home/ykla
```

以下命令显示 **zroot/home/ykla** 上已有的保留空间：

```sh
#  zfs get reservation zroot/home/ykla
NAME             PROPERTY     VALUE   SOURCE
zroot/home/ykla  reservation  10G     local
```

要清除所有保留空间：

```sh
# zfs set reservation=none zroot/home/ykla
```

确认保留空间已清除：

```sh
#  zfs get reservation zroot/home/ykla
NAME             PROPERTY     VALUE   SOURCE
zroot/home/ykla  reservation  none    local
```

### 引用预留

`refreservation` 属性用于设置引用预留（refreservation），适用同样的原则，格式为 `refreservation=大小`。

以下命令将为 **zroot/home/ykla** 设置 1 GB 的引用预留：

```sh
# zfs set refreservation=1G zroot/home/ykla
```

以下命令显示 **zroot/home/ykla** 上已有的引用预留：

```sh
# zfs get refreservation zroot/home/ykla
NAME             PROPERTY        VALUE      SOURCE
zroot/home/ykla  refreservation  1G         local
```

要清除所有引用预留：

```sh
# zfs set refreservation=none zroot/home/ykla
```

确认引用预留已清除：

```sh
# zfs get refreservation zroot/home/ykla
NAME             PROPERTY        VALUE      SOURCE
zroot/home/ykla  refreservation  none       local
```

## 压缩

ZFS 的数据压缩在文件系统层面实现，对上层应用透明，启用后通常既能减少磁盘占用，又能提升读写吞吐量。

管理员可以通过数据集属性查看压缩效果。

```sh
# 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 级别：

```sh
# zfs set compression=zstd-5 zroot
```

> **注意**
>
> 压缩属性变更立即生效，无需重启系统。但该属性仅对新写入的数据生效，不会自动压缩已有的数据。

再次列出各个 ZFS 文件系统的数据压缩属性及其当前设置：

```sh
# 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%。

```sh
# 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 可能写入比未压缩备份更多的数据。

## 去重

启用去重可节省存储空间，但需大量内存支撑去重表。在启用前应先评估工作负载是否真正需要去重——压缩在不增加额外开销的情况下即可提供大部分空间节省。

并非所有数据都适合去重。如果池中的数据没有冗余，去重未必能带来节省。ZFS 可通过模拟去重来预估潜在的空间节省（建议先导出池以确保结果准确）：

```sh
# 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.02

```

`zdb -S` 完成分析后，会显示启用去重可能带来的空间节省比例。

本例中 `dedup` 值 `2.02` 是一个较高的值，节省主要来自压缩。若在此池上启用去重，不仅不会节省任何空间，启用去重所需的内存开销也得不偿失。

系统管理员可使用以下公式来规划存储分配，判断工作负载的重复块数量是否值得投入相应内存。

$$
\text{最终存储效率比率} =
\frac{\dfrac{\text{逻辑空间}}{\text{已分配空间}} \times \dfrac{\text{未压缩大小}}{\text{压缩后大小}}}{\text{副本系数}}
===============================================================================================

\frac{\text{去重率} \times \text{压缩率}}{\text{副本系数}}
$$

如果 `dedup` 值接近 2，则推荐去重；如果 `dedup` 接近 1，则不推荐去重。

如果数据具有良好的可压缩性，空间节省可能非常可观。

由于压缩也能带来显著的性能提升，最佳实践是先启用压缩。仅在去重能提供明显节省且拥有足够内存支撑 DDT 的情况下才启用去重。

要启用去重，请在目标池上设置 `dedup` 属性：

```sh
# zfs set dedup=on zroot
```

去重仅影响写入池的新增数据，仅启用此选项不会对已写入的数据生效。

新启用去重的池将显示如下：

```sh
# 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` 表示数据尚未去重。

下面的例子将随机生成块文件，随后分别复制到两个不同文件名观察去重效果：

```sh
# dd if=/dev/urandom of=/zroot/testfile bs=1M count=100
# cp /zroot/testfile /zroot/testfile.copy1
# cp /zroot/testfile /zroot/testfile.copy2
```

要观察冗余数据的去重效果，可使用：

```sh
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` 列显示 `2.99x` 的去重系数。检测并去重数据副本仅使用了三分之一的空间。空间节省潜力巨大，但需要足够的内存来跟踪去重块。

还可以使用 `zdb` 命令：

```sh
# 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.00
```

## 参考文献

* 金步国. 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 等属性。


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://book.bsdcn.org/di-24-zhang-zfs-wen-jian-xi-tong/di-24.5-jie-zfs-guan-li.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
