第 22 章 Z 文件系统(ZFS)

如果发现翻译错误,请直接 发起PR修改

ZFS 是一种先进的文件系统,旨在解决以前存储子系统软件中存在的主要问题。

最初由 Sun™ 开发,持续的开源 ZFS 开发已经转移到了 OpenZFS 项目

ZFS 有三个主要的设计目标:

  • 数据完整性:所有数据都包含数据的校验和。ZFS 会计算校验和并将其与数据一起写入。当以后读取该数据时,ZFS 会重新计算校验和。如果校验和不匹配,即检测到一个或多个数据错误,ZFS 将尝试在可用的副本、镜像或奇偶块时自动纠正错误。

  • 汇集存储:将物理存储设备添加到一个池中,并从该共享池中分配存储空间。空间可供所有文件系统和卷使用,并通过在池中添加新的存储设备来增加空间。

  • 性能:缓存机制提供了更高的性能。ARC 是一种先进的基于内存的读取缓存。ZFS 提供了第二级基于磁盘的读取缓存 L2ARC ,以及一种基于磁盘的同步写入缓存,名为 ZIL

完整的功能和术语列表请参见 ZFS 功能和术语

22.1. ZFS 有何不同之处

ZFS 不仅仅是一个文件系统,它在根本上与传统的文件系统有所不同。将卷管理器和文件系统的传统分离角色结合起来,为 ZFS 提供了独特的优势。文件系统现在能够意识到底层磁盘的结构。传统的文件系统一次只能存在于单个磁盘上。如果有两个磁盘,那么就需要创建两个单独的文件系统。传统的硬件 RAID 配置通过将操作系统呈现为由物理磁盘提供的空间组成的单个逻辑磁盘来避免这个问题,操作系统在其上放置一个文件系统。即使使用像 GEOM 提供的软件 RAID 解决方案,位于 RAID 之上的 UFS 文件系统也认为它正在处理一个单一设备。 ZFS 的卷管理器和文件系统的组合解决了这个问题,并允许创建共享可用存储池的文件系统。 ZFS 意识到物理磁盘布局的一个重要优势是,当向池中添加额外的磁盘时,现有的文件系统会自动增长。然后,这个新空间就可以供文件系统使用。 ZFS 还可以为每个文件系统应用不同的属性。这使得创建单独的文件系统和数据集比创建单个的整体文件系统更有用。

22.2. 快速入门指南

FreeBSD 可以在系统初始化期间挂载 ZFS 池和数据集。要启用它,请将以下行添加到 /etc/rc.conf: 文件中:

zfs_enable="YES"

然后启动服务:

# service zfs start

本节中的示例假设有三个 SCSI 磁盘,设备名称分别为 da0da1da2 。使用 SATA 硬件的用户应该使用 ada 设备名称。

22.2.1. 单磁盘池

要使用单个磁盘设备创建一个简单且非冗余的池,请按以下步骤操作:

# zpool create example /dev/da0

要查看新的存储池,请查看 df 命令的输出:

# df
Filesystem  1K-blocks    Used    Avail Capacity  Mounted on
/dev/ad0s1a   2026030  235230  1628718    13%    /
devfs               1       1        0   100%    /dev
/dev/ad0s1d  54098308 1032846 48737598     2%    /usr
example      17547136       0 17547136     0%    /example

这个输出显示了创建和挂载 example 池,并且现在可以作为文件系统访问。为用户创建文件以供浏览:

# cd /example
# ls
# touch testfile
# ls -al
total 4
drwxr-xr-x   2 root  wheel    3 Aug 29 23:15 .
drwxr-xr-x  21 root  wheel  512 Aug 29 23:12 ..
-rw-r--r--   1 root  wheel    0 Aug 29 23:15 testfile

此池尚未使用任何高级的 ZFS 功能和属性。要在此池上创建启用了压缩的数据集,请执行以下操作:

# zfs create example/compressed
# zfs set compression=gzip example/compressed

example/compressed 数据集现在是一个 ZFS 压缩文件系统。尝试将一些大文件复制到 /example/compressed

禁用压缩功能:

# zfs set compression=off example/compressed

要卸载文件系统,请使用 zfs umount 命令,然后使用 df 命令进行验证:

# zfs umount example/compressed
# df
Filesystem  1K-blocks    Used    Avail Capacity  Mounted on
/dev/ad0s1a   2026030  235232  1628716    13%    /
devfs               1       1        0   100%    /dev
/dev/ad0s1d  54098308 1032864 48737580     2%    /usr
example      17547008       0 17547008     0%    /example

要重新挂载文件系统以使其再次可访问,请使用 zfs mount 命令,并使用 df 命令进行验证:

# zfs mount example/compressed
# df
Filesystem         1K-blocks    Used    Avail Capacity  Mounted on
/dev/ad0s1a          2026030  235234  1628714    13%    /
devfs                      1       1        0   100%    /dev
/dev/ad0s1d         54098308 1032864 48737580     2%    /usr
example             17547008       0 17547008     0%    /example
example/compressed  17547008       0 17547008     0%    /example/compressed

运行 mount 命令会显示池和文件系统:

# mount
/dev/ad0s1a on / (ufs, local)
devfs on /dev (devfs, local)
/dev/ad0s1d on /usr (ufs, local, soft-updates)
example on /example (zfs, local)
example/compressed on /example/compressed (zfs, local)

创建后,可以像任何文件系统一样使用 ZFS 数据集。根据需要,可以在每个数据集上设置其他可用的功能。下面的示例创建了一个名为 data 的新文件系统。它假设该文件系统包含重要文件,并将其配置为存储每个数据块的两个副本。

# zfs create example/data
# zfs set copies=2 example/data

使用 df 命令查看数据和空间使用情况:

# df
Filesystem         1K-blocks    Used    Avail Capacity  Mounted on
/dev/ad0s1a          2026030  235234  1628714    13%    /
devfs                      1       1        0   100%    /dev
/dev/ad0s1d         54098308 1032864 48737580     2%    /usr
example             17547008       0 17547008     0%    /example
example/compressed  17547008       0 17547008     0%    /example/compressed
example/data        17547008       0 17547008     0%    /example/data

请注意,池中的所有文件系统都具有相同的可用空间。在这些示例中使用 df 命令显示,文件系统使用它们所需的空间,并且都从同一个池中获取。 ZFS 摒弃了卷和分区等概念,允许多个文件系统共享同一个池。

销毁不再需要的文件系统和池:

# zfs destroy example/compressed
# zfs destroy example/data
# zpool destroy example

22.2.2. RAID-Z

磁盘会出现故障。避免因磁盘故障导致数据丢失的一种方法是使用 RAID。ZFS 在其存储池设计中支持此功能。 RAID-Z 存储池需要三个或更多的磁盘,但提供比镜像存储池更多的可用空间。

这个示例创建了一个 RAID-Z 池,指定要添加到池中的磁盘:

# zpool create storage raidz da0 da1 da2

Sun™ 建议在 RAID-Z 配置中使用的设备数量应在三到九之间。对于需要由 10 个或更多磁盘组成的单个池的环境,考虑将其分成较小的 RAID-Z 组。如果有两个磁盘可用,可以使用 ZFS 镜像提供冗余性(如果需要)。有关更多详细信息,请参阅 zpool(8)

前面的示例创建了名为 storagezpool 。这个示例在该池中创建了一个名为 home 的新文件系统:

# zfs create storage/home

启用压缩并存储目录和文件的额外副本:

# zfs set copies=2 storage/home
# zfs set compression=gzip storage/home

要将此目录设置为用户的新家目录,请将用户数据复制到此目录并创建相应的符号链接:

# cp -rp /home/* /storage/home
# rm -rf /home /usr/home
# ln -s /storage/home /home
# ln -s /storage/home /usr/home

用户数据现在存储在新创建的 /storage/home 上。通过添加一个新用户并以该用户身份登录来进行测试。

创建一个文件系统快照,以便以后可以回滚:

# zfs snapshot storage/home@08-30-08

ZFS 创建数据集的快照,而不是单个目录或文件。

@ 字符是文件系统名称或卷名称之间的分隔符。在删除重要目录之前,先备份文件系统,然后回滚到一个早期的快照,其中目录仍然存在:

# zfs rollback storage/home@08-30-08

要列出所有可用的快照,请在文件系统的 .zfs/snapshot 目录中运行 ls 命令。例如,要查看已拍摄的快照:

# ls /storage/home/.zfs/snapshot

编写一个脚本来定期对用户数据进行快照。随着时间的推移,快照可能会占用大量的磁盘空间。使用以下命令删除先前的快照:

# zfs destroy storage/home@08-30-08

经过测试,使用以下命令将 /storage/home 设置为真实的 /home

# zfs set mountpoint=/home storage/home

运行 dfmount 命令来确认系统现在将文件系统视为真实的 /home

# mount
/dev/ad0s1a on / (ufs, local)
devfs on /dev (devfs, local)
/dev/ad0s1d on /usr (ufs, local, soft-updates)
storage on /storage (zfs, local)
storage/home on /home (zfs, local)
# df
Filesystem   1K-blocks    Used    Avail Capacity  Mounted on
/dev/ad0s1a    2026030  235240  1628708    13%    /
devfs                1       1        0   100%    /dev
/dev/ad0s1d   54098308 1032826 48737618     2%    /usr
storage       26320512       0 26320512     0%    /storage
storage/home  26320512       0 26320512     0%    /home

这样就完成了 RAID-Z 的配置。通过将以下行添加到 /etc/periodic.conf ,可以将关于创建的文件系统的每日状态更新添加到夜间的 periodic(8) 运行中:

daily_status_zfs_enable="YES"

22.2.3. 恢复 RAID-Z

每个软件 RAID 都有一种监控其 state 的方法。使用以下命令查看 RAID-Z 设备的状态:

# zpool status -x

如果所有池都处于 在线 状态,并且一切正常,消息将显示:

all pools are healthy

如果出现问题,比如磁盘处于 离线 状态,池的状态将如下所示:

  pool: storage
 state: DEGRADED
status: One or more devices has been taken offline by the administrator.
	Sufficient replicas exist for the pool to continue functioning in a
	degraded state.
action: Online the device using 'zpool online' or replace the device with
	'zpool replace'.
 scrub: none requested
config:

	NAME        STATE     READ WRITE CKSUM
	storage     DEGRADED     0     0     0
	  raidz1    DEGRADED     0     0     0
	    da0     ONLINE       0     0     0
	    da1     OFFLINE      0     0     0
	    da2     ONLINE       0     0     0

errors: No known data errors

"OFFLINE"显示管理员使用以下方式将 da1 下线:

# zpool offline storage da1

立即关闭计算机并更换 da1。重新启动计算机并将 da1 返回到池中:

# zpool replace storage da1

接下来,再次检查状态,这次不使用 -x 选项以显示所有的池:

# zpool status storage
 pool: storage
 state: ONLINE
 scrub: resilver completed with 0 errors on Sat Aug 30 19:44:11 2008
config:

	NAME        STATE     READ WRITE CKSUM
	storage     ONLINE       0     0     0
	  raidz1    ONLINE       0     0     0
	    da0     ONLINE       0     0     0
	    da1     ONLINE       0     0     0
	    da2     ONLINE       0     0     0

errors: No known data errors

在这个例子中,一切都正常。

22.2.4. 数据验证

ZFS 使用校验和来验证存储数据的完整性。创建文件系统时会自动启用校验和功能。

禁用校验和是可以的,但 不推荐 !校验和占用很少的存储空间,并提供数据完整性。大多数 ZFS 功能在禁用校验和的情况下将无法正常工作。禁用这些校验和不会明显提高性能。

验证数据校验和(称为 scrubbing )可以确保 storage 池的完整性,具体操作如下:

# zpool scrub storage

一个 scrub 的持续时间取决于存储的数据量。数据量越大,验证所需的时间就越长。由于 scrub 是 I/O 密集型操作, ZFS 只允许同时运行一个 scrub。在 scrub 完成后,可以使用 zpool status 命令查看状态。

# zpool status storage
 pool: storage
 state: ONLINE
 scrub: scrub completed with 0 errors on Sat Jan 26 19:57:37 2013
config:

	NAME        STATE     READ WRITE CKSUM
	storage     ONLINE       0     0     0
	  raidz1    ONLINE       0     0     0
	    da0     ONLINE       0     0     0
	    da1     ONLINE       0     0     0
	    da2     ONLINE       0     0     0

errors: No known data errors

显示最后一次清 scrub 的完成日期有助于决定何时开始下一次 scrub。例行 scrub 有助于保护数据免受静默损坏,并确保池的完整性。

请参考 zfs(8)zpool(8) 了解其他 ZFS 选项。

22.3. zpool 管理

ZFS 管理使用两个主要工具。 zpool 工具控制池的操作,允许添加、删除、替换和管理磁盘。zfs 工具允许创建、销毁和管理数据集,包括 文件系统

22.3.1. 创建和销毁存储池

创建一个 ZFS 存储池需要做出永久性的决策,因为在创建后无法更改池的结构。最重要的决策是将物理磁盘分组成哪种类型的 vdev 。有关可能选项的详细信息,请参阅 vdev 类型 列表。创建池后,大多数 vdev 类型不允许向 vdev 添加磁盘。例外情况是镜像,它允许向 vdev 添加新磁盘,并且条带可以通过将新磁盘附加到 vdev 来升级为镜像。虽然添加新的 vdev 可以扩展池,但池的布局在创建后无法更改。取而代之的是备份数据,销毁池,然后重新创建池。

创建一个简单的镜像池:

# zpool create mypool mirror /dev/ada1 /dev/ada2
# zpool status
  pool: mypool
 state: ONLINE
  scan: none requested
config:

        NAME        STATE     READ WRITE CKSUM
        mypool      ONLINE       0     0     0
          mirror-0  ONLINE       0     0     0
            ada1    ONLINE       0     0     0
            ada2    ONLINE       0     0     0

errors: No known data errors

要使用单个命令创建多个 vdev ,请使用以 vdev 类型关键字 mirror 分隔的磁盘组。

# zpool create mypool mirror /dev/ada1 /dev/ada2 mirror /dev/ada3 /dev/ada4
# zpool status
  pool: mypool
 state: ONLINE
  scan: none requested
config:

        NAME        STATE     READ WRITE CKSUM
        mypool      ONLINE       0     0     0
          mirror-0  ONLINE       0     0     0
            ada1    ONLINE       0     0     0
            ada2    ONLINE       0     0     0
          mirror-1  ONLINE       0     0     0
            ada3    ONLINE       0     0     0
            ada4    ONLINE       0     0     0

errors: No known data errors

池还可以使用分区而不是整个磁盘。将 ZFS 放在单独的分区中可以使同一磁盘具有其他用途的分区。特别是,它允许添加带有引导代码和用于引导的文件系统的分区。这样就可以从同时也是池成员的磁盘启动。在 FreeBSD 上,使用分区而不是整个磁盘时, ZFS 不会带来性能损失。使用分区还允许管理员对磁盘进行“欠配置”,使用不到全部容量。如果将来替换的磁盘与原始磁盘的名义大小相同,但实际容量略小,较小的分区仍将适应替换磁盘。

使用分区创建一个 RAID-Z2 池:

# zpool create mypool raidz2 /dev/ada0p3 /dev/ada1p3 /dev/ada2p3 /dev/ada3p3 /dev/ada4p3 /dev/ada5p3
# zpool status
  pool: mypool
 state: ONLINE
  scan: none requested
config:

        NAME        STATE     READ WRITE CKSUM
        mypool      ONLINE       0     0     0
          raidz2-0  ONLINE       0     0     0
            ada0p3  ONLINE       0     0     0
            ada1p3  ONLINE       0     0     0
            ada2p3  ONLINE       0     0     0
            ada3p3  ONLINE       0     0     0
            ada4p3  ONLINE       0     0     0
            ada5p3  ONLINE       0     0     0

errors: No known data errors

销毁一个不再需要的池以重用磁盘。销毁池需要先卸载该池中的文件系统。如果有任何数据集正在使用中,卸载操作将失败,不会销毁池。可以使用 -f 强制销毁池。这可能会导致应用程序中对这些数据集有打开文件的未定义行为。

22.3.2. 添加和移除设备

有两种方法可以将磁盘添加到池中:使用 zpool attach 将磁盘附加到现有的 vdev 上,或者使用 zpool add 将 vdev 添加到池中。一些 vdev 类型 允许在创建后向 vdev 添加磁盘。

使用单个磁盘创建的池缺乏冗余性。它可以检测到损坏,但无法修复,因为没有其他数据的副本。 副本 属性可以从小故障(如坏扇区)中恢复,但不提供与镜像或 RAID-Z 相同级别的保护。从由单个磁盘 vdev 组成的池开始,使用 zpool attach 将新磁盘添加到 vdev 中,创建镜像。还可以使用 zpool attach 将新磁盘添加到镜像组,增加冗余性和读取性能。在为池分区的磁盘上,将第一个磁盘的布局复制到第二个磁盘上。使用 gpart backupgpart restore 可以使这个过程更容易。

通过连接 ada0p3,将单个磁盘(条带)vdev ada1p3 升级为镜像:

# zpool status
  pool: mypool
 state: ONLINE
  scan: none requested
config:

        NAME        STATE     READ WRITE CKSUM
        mypool      ONLINE       0     0     0
          ada0p3    ONLINE       0     0     0

errors: No known data errors
# zpool attach mypool ada0p3 ada1p3
Make sure to wait until resilvering finishes before rebooting.

If you boot from pool 'mypool', you may need to update boot code on newly attached disk _ada1p3_.

Assuming you use GPT partitioning and _da0_ is your new boot disk you may use the following command:

        gpart bootcode -b /boot/pmbr -p /boot/gptzfsboot -i 1 da0
# gpart bootcode -b /boot/pmbr -p /boot/gptzfsboot -i 1 ada1
bootcode written to ada1
# zpool status
  pool: mypool
 state: ONLINE
status: One or more devices is currently being resilvered.  The pool will
        continue to function, possibly in a degraded state.
action: Wait for the resilver to complete.
  scan: resilver in progress since Fri May 30 08:19:19 2014
        527M scanned out of 781M at 47.9M/s, 0h0m to go
        527M resilvered, 67.53% done
config:

        NAME        STATE     READ WRITE CKSUM
        mypool      ONLINE       0     0     0
          mirror-0  ONLINE       0     0     0
            ada0p3  ONLINE       0     0     0
            ada1p3  ONLINE       0     0     0  (resilvering)

errors: No known data errors
# zpool status
  pool: mypool
 state: ONLINE
  scan: resilvered 781M in 0h0m with 0 errors on Fri May 30 08:15:58 2014
config:

        NAME        STATE     READ WRITE CKSUM
        mypool      ONLINE       0     0     0
          mirror-0  ONLINE       0     0     0
            ada0p3  ONLINE       0     0     0
            ada1p3  ONLINE       0     0     0

errors: No known data errors

当无法将磁盘添加到现有的 vdev 时,例如对于 RAID-Z ,一种替代方法是向池中添加另一个 vdev 。添加 vdev 可以通过在 vdev 之间分布写操作来提供更高的性能。每个 vdev 都提供自己的冗余性。可以混合使用 mirrorRAID-Z 等不同类型的 vdev ,但不建议这样做。向包含镜像或 RAID-Z vdev 的池中添加一个非冗余的 vdev 会对整个池中的数据造成风险。分布写操作意味着非冗余磁盘的故障将导致丢失对池中每个块的一部分数据。

ZFS 将数据跨越每个 vdev 进行条带化。例如,使用两个镜像 vdev ,这实际上是一个 RAID 10,将写操作跨越两组镜像。ZFS 分配空间以使每个 vdev 在同一时间达到 100 %的使用率。如果 vdev 具有不同数量的可用空间,性能将降低,因为更多的数据写入将发送到使用率较低的 vdev。

在将新设备连接到引导池时,请记得更新引导代码。

将第二个镜像组(ada2p3ada3p3)附加到现有的镜像中:

# zpool status
  pool: mypool
 state: ONLINE
  scan: resilvered 781M in 0h0m with 0 errors on Fri May 30 08:19:35 2014
config:

        NAME        STATE     READ WRITE CKSUM
        mypool      ONLINE       0     0     0
          mirror-0  ONLINE       0     0     0
            ada0p3  ONLINE       0     0     0
            ada1p3  ONLINE       0     0     0

errors: No known data errors
# zpool add mypool mirror ada2p3 ada3p3
# gpart bootcode -b /boot/pmbr -p /boot/gptzfsboot -i 1 ada2
bootcode written to ada2
# gpart bootcode -b /boot/pmbr -p /boot/gptzfsboot -i 1 ada3
bootcode written to ada3
# zpool status
  pool: mypool
 state: ONLINE
  scan: scrub repaired 0 in 0h0m with 0 errors on Fri May 30 08:29:51 2014
config:

        NAME        STATE     READ WRITE CKSUM
        mypool      ONLINE       0     0     0
          mirror-0  ONLINE       0     0     0
            ada0p3  ONLINE       0     0     0
            ada1p3  ONLINE       0     0     0
          mirror-1  ONLINE       0     0     0
            ada2p3  ONLINE       0     0     0
            ada3p3  ONLINE       0     0     0

errors: No known data errors

从池中删除 vdev 是不可能的,如果剩余的冗余足够,从镜像中删除磁盘是独占的。如果镜像组中只剩下一个磁盘,该组将不再是镜像,而变成条带,如果该剩余磁盘故障,将会危及整个池。

从三路镜像组中移除一个磁盘:

# zpool status
  pool: mypool
 state: ONLINE
  scan: scrub repaired 0 in 0h0m with 0 errors on Fri May 30 08:29:51 2014
config:

        NAME        STATE     READ WRITE CKSUM
        mypool      ONLINE       0     0     0
          mirror-0  ONLINE       0     0     0
            ada0p3  ONLINE       0     0     0
            ada1p3  ONLINE       0     0     0
            ada2p3  ONLINE       0     0     0

errors: No known data errors
# zpool detach mypool ada2p3
# zpool status
  pool: mypool
 state: ONLINE
  scan: scrub repaired 0 in 0h0m with 0 errors on Fri May 30 08:29:51 2014
config:

        NAME        STATE     READ WRITE CKSUM
        mypool      ONLINE       0     0     0
          mirror-0  ONLINE       0     0     0
            ada0p3  ONLINE       0     0     0
            ada1p3  ONLINE       0     0     0

errors: No known data errors

22.3.3. 检查池的状态

池状态非常重要。如果驱动器离线或 ZFS 检测到读取、写入或校验错误,相应的错误计数会增加。status 输出显示了池中每个设备的配置和状态,以及整个池的状态。还显示了要采取的操作和有关上次 scrub 的详细信息。

# zpool status
  pool: mypool
 state: ONLINE
  scan: scrub repaired 0 in 2h25m with 0 errors on Sat Sep 14 04:25:50 2013
config:

        NAME        STATE     READ WRITE CKSUM
        mypool      ONLINE       0     0     0
          raidz2-0  ONLINE       0     0     0
            ada0p3  ONLINE       0     0     0
            ada1p3  ONLINE       0     0     0
            ada2p3  ONLINE       0     0     0
            ada3p3  ONLINE       0     0     0
            ada4p3  ONLINE       0     0     0
            ada5p3  ONLINE       0     0     0

errors: No known data errors

22.3.4. 清除错误

当检测到错误时,ZFS 会增加读取、写入或校验和错误计数。使用 zpool clear mypool 命令清除错误消息并重置计数。清除错误状态对于自动化脚本非常重要,这些脚本在池遇到错误时会通知管理员。如果不清除旧错误,这些脚本可能无法报告后续的错误。

22.3.5. 替换一个正常工作的设备

可能需要用不同的磁盘替换一个磁盘。当替换一个工作中的磁盘时,该过程会在替换期间保持旧磁盘在线。池永远不会进入 降级 状态,从而降低数据丢失的风险。运行 zpool replace 命令将数据从旧磁盘复制到新磁盘。操作完成后, ZFS 会将旧磁盘与 vdev 断开连接。如果新磁盘比旧磁盘大,可能可以使用新空间来扩展 zpool 。请参见 扩展池

替换池中的一个正常工作设备:

# zpool status
  pool: mypool
 state: ONLINE
  scan: none requested
config:

        NAME        STATE     READ WRITE CKSUM
        mypool      ONLINE       0     0     0
          mirror-0  ONLINE       0     0     0
            ada0p3  ONLINE       0     0     0
            ada1p3  ONLINE       0     0     0

errors: No known data errors
# zpool replace mypool ada1p3 ada2p3
Make sure to wait until resilvering finishes before rebooting.

When booting from the pool 'zroot', update the boot code on the newly attached disk 'ada2p3'.

Assuming GPT partitioning is used and [.filename]#da0# is the new boot disk, use the following command:

        gpart bootcode -b /boot/pmbr -p /boot/gptzfsboot -i 1 da0
# gpart bootcode -b /boot/pmbr -p /boot/gptzfsboot -i 1 ada2
# zpool status
  pool: mypool
 state: ONLINE
status: One or more devices is currently being resilvered.  The pool will
        continue to function, possibly in a degraded state.
action: Wait for the resilver to complete.
  scan: resilver in progress since Mon Jun  2 14:21:35 2014
        604M scanned out of 781M at 46.5M/s, 0h0m to go
        604M resilvered, 77.39% done
config:

        NAME             STATE     READ WRITE CKSUM
        mypool           ONLINE       0     0     0
          mirror-0       ONLINE       0     0     0
            ada0p3       ONLINE       0     0     0
            replacing-1  ONLINE       0     0     0
              ada1p3     ONLINE       0     0     0
              ada2p3     ONLINE       0     0     0  (resilvering)

errors: No known data errors
# zpool status
  pool: mypool
 state: ONLINE
  scan: resilvered 781M in 0h0m with 0 errors on Mon Jun  2 14:21:52 2014
config:

        NAME        STATE     READ WRITE CKSUM
        mypool      ONLINE       0     0     0
          mirror-0  ONLINE       0     0     0
            ada0p3  ONLINE       0     0     0
            ada2p3  ONLINE       0     0     0

errors: No known data errors

22.3.6. 处理故障设备

当池中的磁盘发生故障时,该磁盘所属的 vdev 将进入 降级 状态。数据仍然可用,但性能降低,因为 ZFS 会通过可用的冗余计算缺失的数据。为了将 vdev 恢复到完全功能状态,需要替换故障的物理设备。然后,ZFS 会开始 重建 操作。ZFS 会通过可用的冗余重新计算故障设备上的数据,并将其写入替代设备。完成后,vdev 将返回 在线 状态。

如果 vdev 没有任何冗余,或者设备已经损坏且没有足够的冗余来弥补,那么存储池将进入 故障 状态。除非有足够的设备重新连接存储池,否则存储池将无法运行,需要从备份中恢复数据。

当替换一个故障磁盘时,故障磁盘的名称会变为新磁盘的 GUID。如果替换设备具有相同的设备名称,则不需要为 zpool replace 指定新的设备名称参数。

使用 zpool replace 命令替换故障的磁盘:

# zpool status
  pool: mypool
 state: DEGRADED
status: One or more devices could not be opened.  Sufficient replicas exist for
        the pool to continue functioning in a degraded state.
action: Attach the missing device and online it using 'zpool online'.
   see: http://illumos.org/msg/ZFS-8000-2Q
  scan: none requested
config:

        NAME                    STATE     READ WRITE CKSUM
        mypool                  DEGRADED     0     0     0
          mirror-0              DEGRADED     0     0     0
            ada0p3              ONLINE       0     0     0
            316502962686821739  UNAVAIL      0     0     0  was /dev/ada1p3

errors: No known data errors
# zpool replace mypool 316502962686821739 ada2p3
# zpool status
  pool: mypool
 state: DEGRADED
status: One or more devices is currently being resilvered.  The pool will
        continue to function, possibly in a degraded state.
action: Wait for the resilver to complete.
  scan: resilver in progress since Mon Jun  2 14:52:21 2014
        641M scanned out of 781M at 49.3M/s, 0h0m to go
        640M resilvered, 82.04% done
config:

        NAME                        STATE     READ WRITE CKSUM
        mypool                      DEGRADED     0     0     0
          mirror-0                  DEGRADED     0     0     0
            ada0p3                  ONLINE       0     0     0
            replacing-1             UNAVAIL      0     0     0
              15732067398082357289  UNAVAIL      0     0     0  was /dev/ada1p3/old
              ada2p3                ONLINE       0     0     0  (resilvering)

errors: No known data errors
# zpool status
  pool: mypool
 state: ONLINE
  scan: resilvered 781M in 0h0m with 0 errors on Mon Jun  2 14:52:38 2014
config:

        NAME        STATE     READ WRITE CKSUM
        mypool      ONLINE       0     0     0
          mirror-0  ONLINE       0     0     0
            ada0p3  ONLINE       0     0     0
            ada2p3  ONLINE       0     0     0

errors: No known data errors

22.3.7. Scrubbing 池

定期对池进行 scrub 操作,最好每个月至少一次。scrub 操作对磁盘的使用较高,运行时会降低性能。在安排 scrub 操作时避免高负载时段,或者使用 vfs.zfs.scrub_delay 来调整 scrub 操作的相对优先级,以防止其影响其他工作负载的速度。

# zpool scrub mypool
# zpool status
  pool: mypool
 state: ONLINE
  scan: scrub in progress since Wed Feb 19 20:52:54 2014
        116G scanned out of 8.60T at 649M/s, 3h48m to go
        0 repaired, 1.32% done
config:

        NAME        STATE     READ WRITE CKSUM
        mypool      ONLINE       0     0     0
          raidz2-0  ONLINE       0     0     0
            ada0p3  ONLINE       0     0     0
            ada1p3  ONLINE       0     0     0
            ada2p3  ONLINE       0     0     0
            ada3p3  ONLINE       0     0     0
            ada4p3  ONLINE       0     0     0
            ada5p3  ONLINE       0     0     0

errors: No known data errors

如果需要取消一个 scrub 操作,请运行 zpool scrub -s mypool

22.3.8. 自我修复

存储在数据块中的校验和使文件系统能够自我修复。这个功能会自动修复数据,如果其校验和与存储池中另一个设备上记录的校验和不匹配。例如,一个具有两个磁盘的镜像配置,其中一个驱动器开始出现故障,无法正确存储数据。当数据长时间未被访问时,如长期存档存储,情况会更糟。传统的文件系统需要运行检查和修复数据的命令,如 fsck(8)。这些命令需要时间,在严重情况下,管理员必须决定执行哪个修复操作。当 ZFS 检测到一个数据块的校验和不匹配时,它会尝试从镜像磁盘中读取数据。如果该磁盘能提供正确的数据, ZFS 将将其提供给应用程序,并纠正具有错误校验和的磁盘上的数据。在正常存储池操作期间,这一切都在没有任何系统管理员干预的情况下发生。

下一个示例通过创建一个镜像磁盘池来展示这种自我修复行为,其中包括 /dev/ada0/dev/ada1

# zpool create healer mirror /dev/ada0 /dev/ada1
# zpool status healer
  pool: healer
 state: ONLINE
  scan: none requested
config:

    NAME        STATE     READ WRITE CKSUM
    healer      ONLINE       0     0     0
      mirror-0  ONLINE       0     0     0
       ada0     ONLINE       0     0     0
       ada1     ONLINE       0     0     0

errors: No known data errors
# zpool list
NAME     SIZE  ALLOC   FREE   CKPOINT  EXPANDSZ   FRAG   CAP  DEDUP  HEALTH  ALTROOT
healer   960M  92.5K   960M         -         -     0%    0%  1.00x  ONLINE  -

将一些重要数据复制到池中,以使用自我修复功能保护免受数据错误,并为池创建校验和以备后续比较。

# cp /some/important/data /healer
# zfs list
NAME     SIZE  ALLOC   FREE    CAP  DEDUP  HEALTH  ALTROOT
healer   960M  67.7M   892M     7%  1.00x  ONLINE  -
# sha1 /healer > checksum.txt
# cat checksum.txt
SHA1 (/healer) = 2753eff56d77d9a536ece6694bf0a82740344d1f

通过向镜像中的一个磁盘的开头写入随机数据来模拟数据损坏。为了防止 ZFS 在检测到数据损坏时修复数据,可以在损坏之前导出池,并在之后重新导入。

这是一个危险的操作,可能会破坏重要数据,仅用于演示目的。在存储池的正常运行期间,请 不要尝试 执行此操作。此意外损坏示例也不应在任何使用 ZFS 以外的文件系统的磁盘上运行,该磁盘上的另一个分区中也不应该有 ZFS 。请不要使用除了存储池中的设备名称之外的任何其他磁盘设备名称。确保存储池有适当的备份,并在执行命令之前对其进行测试!

# zpool export healer
# dd if=/dev/random of=/dev/ada1 bs=1m count=200
200+0 records in
200+0 records out
209715200 bytes transferred in 62.992162 secs (3329227 bytes/sec)
# zpool import healer

池状态显示一个设备发生了错误。请注意,从池中读取数据的应用程序没有接收到任何错误数据。ZFS 从 ada0 设备提供了正确校验和的数据。要找到校验和错误的设备,请查找 CKSUM 列中包含非零值的设备。

# zpool status healer
    pool: healer
   state: ONLINE
  status: One or more devices has experienced an unrecoverable error.  An
          attempt was made to correct the error.  Applications are unaffected.
  action: Determine if the device needs to be replaced, and clear the errors
          using 'zpool clear' or replace the device with 'zpool replace'.
     see: http://illumos.org/msg/ZFS-8000-4J
    scan: none requested
  config:

      NAME        STATE     READ WRITE CKSUM
      healer      ONLINE       0     0     0
        mirror-0  ONLINE       0     0     0
         ada0     ONLINE       0     0     0
         ada1     ONLINE       0     0     1

errors: No known data errors

ZFS 检测到错误,并通过使用未受影响的 ada0 镜像磁盘中的冗余来处理该错误。通过与原始数据进行校验比较,可以确定池是否恢复一致。

# sha1 /healer >> checksum.txt
# cat checksum.txt
SHA1 (/healer) = 2753eff56d77d9a536ece6694bf0a82740344d1f
SHA1 (/healer) = 2753eff56d77d9a536ece6694bf0a82740344d1f

在故意篡改之前和之后生成校验和,同时池数据仍然匹配。这显示了当校验和不同时, ZFS 能够自动检测和纠正任何错误。请注意,这需要池中具有足够的冗余。由单个设备组成的池没有自我修复能力。这也是为什么在 ZFS 中校验和如此重要的原因;不要出于任何原因禁用它们。 ZFS 不需要 fsck(8) 或类似的文件系统一致性检查程序来检测和纠正这个问题,并且在出现问题时保持池可用。现在需要进行一次 scrub 操作来覆盖在 ada1 上的损坏数据。

# zpool scrub healer
# zpool status healer
  pool: healer
 state: ONLINE
status: One or more devices has experienced an unrecoverable error.  An
            attempt was made to correct the error.  Applications are unaffected.
action: Determine if the device needs to be replaced, and clear the errors
            using 'zpool clear' or replace the device with 'zpool replace'.
   see: http://illumos.org/msg/ZFS-8000-4J
  scan: scrub in progress since Mon Dec 10 12:23:30 2012
        10.4M scanned out of 67.0M at 267K/s, 0h3m to go
        9.63M repaired, 15.56% done
config:

    NAME        STATE     READ WRITE CKSUM
    healer      ONLINE       0     0     0
      mirror-0  ONLINE       0     0     0
       ada0     ONLINE       0     0     0
       ada1     ONLINE       0     0   627  (repairing)

errors: No known data errors

Scrub 操作从 ada0 读取数据,并将任何具有错误校验和的数据重写到 ada1 上,这可以通过 zpool status 中的 (repairing) 输出来显示。操作完成后,池的状态将发生变化:

# zpool status healer
  pool: healer
 state: ONLINE
status: One or more devices has experienced an unrecoverable error.  An
        attempt was made to correct the error.  Applications are unaffected.
action: Determine if the device needs to be replaced, and clear the errors
             using 'zpool clear' or replace the device with 'zpool replace'.
   see: http://illumos.org/msg/ZFS-8000-4J
  scan: scrub repaired 66.5M in 0h2m with 0 errors on Mon Dec 10 12:26:25 2012
config:

    NAME        STATE     READ WRITE CKSUM
    healer      ONLINE       0     0     0
      mirror-0  ONLINE       0     0     0
       ada0     ONLINE       0     0     0
       ada1     ONLINE       0     0 2.72K

errors: No known data errors

在从 ada0 同步所有数据到 ada1 后,完成了 清洗 操作,请通过运行 zpool clear 命令清除池状态中的错误消息。

# zpool clear healer
# zpool status healer
  pool: healer
 state: ONLINE
  scan: scrub repaired 66.5M in 0h2m with 0 errors on Mon Dec 10 12:26:25 2012
config:

    NAME        STATE     READ WRITE CKSUM
    healer      ONLINE       0     0     0
      mirror-0  ONLINE       0     0     0
       ada0     ONLINE       0     0     0
       ada1     ONLINE       0     0     0

errors: No known data errors

现在,池已经恢复到完全正常的状态,所有错误计数都为零。

22.3.9. 扩展池

每个 vdev 中最小的设备限制了冗余池的可用大小。用一个更大的设备替换最小的设备。在完成 替换重建 操作后,池可以扩展到使用新设备的容量。例如,考虑一个由 1 TB 驱动器和 2 TB 驱动器组成的镜像。可用空间为 1 TB 。当用另一个 2 TB 驱动器替换 1 TB 驱动器时,重新同步过程将现有数据复制到新驱动器上。由于两个设备现在都具有 2 TB 的容量,镜像的可用空间增长到 2 TB。

通过在每个设备上使用 zpool online -e 来开始扩展。在扩展所有设备之后,额外的空间将可用于池。

22.3.10. 导入和导出存储池

在将存储池移动到另一个系统之前,请先 导出(export) 它们。 ZFS 会卸载所有数据集,并将每个设备标记为已导出,但仍然锁定以防止其他磁盘使用。这使得存储池可以在其他支持 ZFS 的机器、其他操作系统甚至不同的硬件架构上导入(有一些注意事项,请参阅 zpool(8))。当数据集有打开的文件时,请使用 zpool export -f 强制导出存储池。请谨慎使用此功能。数据集将被强制卸载,可能导致那些数据集上有打开文件的应用程序出现意外行为。

导出一个未使用的池:

# zpool export mypool

导入一个存储池会自动挂载数据集。如果不希望出现这种行为,请使用 zpool import -N 来阻止它。 zpool import -o 为此特定导入设置临时属性。zpool import altroot= 允许使用基本挂载点而不是文件系统的根来导入存储池。如果该存储池最后在另一个系统上使用并且没有正确导出,请使用 zpool import -f 来强制导入。zpool import -a 导入所有未被其他系统使用的存储池。

列出所有可导入的池:

# zpool import
   pool: mypool
     id: 9930174748043525076
  state: ONLINE
 action: The pool can be imported using its name or numeric identifier.
 config:

        mypool      ONLINE
          ada2p3    ONLINE

导入具有替代根目录的池:

# zpool import -o altroot=/mnt mypool
# zfs list
zfs list
NAME                 USED  AVAIL  REFER  MOUNTPOINT
mypool               110K  47.0G    31K  /mnt/mypool

22.3.11. 升级存储池

在升级 FreeBSD 之后,或者从使用较旧版本的系统导入池时,需要手动将池升级到最新的 ZFS 版本以支持新功能。在升级之前,请考虑池是否可能需要在较旧的系统上导入。升级是一个单向过程。升级较旧的池是可能的,但是无法降级具有较新功能的池。

将 v28 池升级以支持 Feature Flags

# zpool status
  pool: mypool
 state: ONLINE
status: The pool is formatted using a legacy on-disk format.  The pool can
        still be used, but some features are unavailable.
action: Upgrade the pool using 'zpool upgrade'.  Once this is done, the
        pool will no longer be accessible on software that does not support feat
        flags.
  scan: none requested
config:

        NAME        STATE     READ WRITE CKSUM
        mypool      ONLINE       0     0     0
          mirror-0  ONLINE       0     0     0
	    ada0    ONLINE       0     0     0
	    ada1    ONLINE       0     0     0

errors: No known data errors
# zpool upgrade
This system supports ZFS pool feature flags.

The following pools are formatted with legacy version numbers and are upgraded to use feature flags.
After being upgraded, these pools will no longer be accessible by software that does not support feature flags.

VER  POOL
---  ------------
28   mypool

Use 'zpool upgrade -v' for a list of available legacy versions.
Every feature flags pool has all supported features enabled.
# zpool upgrade mypool
This system supports ZFS pool feature flags.

Successfully upgraded 'mypool' from version 28 to feature flags.
Enabled the following features on 'mypool':
  async_destroy
  empty_bpobj
  lz4_compress
  multi_vdev_crash_dump

只有在完成 zpool upgrade 之后,ZFS 的新功能才会可用。使用 zpool upgrade -v 命令查看升级提供的新功能,以及已经支持的功能。

升级一个池子以支持新的功能标志:

# zpool status
  pool: mypool
 state: ONLINE
status: Some supported features are not enabled on the pool. The pool can
        still be used, but some features are unavailable.
action: Enable all features using 'zpool upgrade'. Once this is done,
        the pool may no longer be accessible by software that does not support
        the features. See zpool-features(7) for details.
  scan: none requested
config:

        NAME        STATE     READ WRITE CKSUM
        mypool      ONLINE       0     0     0
          mirror-0  ONLINE       0     0     0
	    ada0    ONLINE       0     0     0
	    ada1    ONLINE       0     0     0

errors: No known data errors
# zpool upgrade
This system supports ZFS pool feature flags.

All pools are formatted using feature flags.

Some supported features are not enabled on the following pools. Once a
feature is enabled the pool may become incompatible with software
that does not support the feature. See zpool-features(7) for details.

POOL  FEATURE
---------------
zstore
      multi_vdev_crash_dump
      spacemap_histogram
      enabled_txg
      hole_birth
      extensible_dataset
      bookmarks
      filesystem_limits
# zpool upgrade mypool
This system supports ZFS pool feature flags.

Enabled the following features on 'mypool':
  spacemap_histogram
  enabled_txg
  hole_birth
  extensible_dataset
  bookmarks
  filesystem_limits

更新从池中引导的系统的引导代码,以支持新的池版本。在包含引导代码的分区上使用 gpart bootcode 命令。根据系统的引导方式,有两种类型的引导代码可用:GPT(最常见的选项)和 EFI(适用于更现代的系统)。

对于使用 GPT 的传统引导,请使用以下命令:

# gpart bootcode -b /boot/pmbr -p /boot/gptzfsboot -i 1 ada1

对于使用 EFI 引导的系统,请执行以下命令:

# gpart bootcode -p /boot/boot1.efifat -i 1 ada1

将引导代码应用于池中的所有可引导磁盘。有关更多信息,请参阅 gpart(8)

22.3.12. 显示池的历史记录

ZFS 记录更改池的命令,包括创建数据集、更改属性或替换磁盘。查看关于池创建的历史记录很有用,还可以检查哪个用户执行了特定的操作以及何时执行的。历史记录不会保存在日志文件中,而是作为池本身的一部分。用于查看这个历史记录的命令被恰当地命名为 zpool history

# zpool history
History for 'tank':
2013-02-26.23:02:35 zpool create tank mirror /dev/ada0 /dev/ada1
2013-02-27.18:50:58 zfs set atime=off tank
2013-02-27.18:51:09 zfs set checksum=fletcher4 tank
2013-02-27.18:51:18 zfs create tank/backup

输出显示了 zpoolzfs 命令以某种方式修改了池,并附带了时间戳。不包括像 zfs list 这样的命令。当未指定池名称时, ZFS 会显示所有池的历史记录。

当使用选项 -i-l 时,zpool history 命令可以显示更多的信息。 -i 选项会显示用户发起的事件以及内部记录的 ZFS 事件。

# zpool history -i
History for 'tank':
2013-02-26.23:02:35 [internal pool create txg:5] pool spa 28; zfs spa 28; zpl 5;uts  9.1-RELEASE 901000 amd64
2013-02-27.18:50:53 [internal property set txg:50] atime=0 dataset = 21
2013-02-27.18:50:58 zfs set atime=off tank
2013-02-27.18:51:04 [internal property set txg:53] checksum=7 dataset = 21
2013-02-27.18:51:09 zfs set checksum=fletcher4 tank
2013-02-27.18:51:13 [internal create txg:55] dataset = 39
2013-02-27.18:51:18 zfs create tank/backup

通过添加 -l 来显示更多详细信息。以长格式显示历史记录,包括发出命令的用户的名称和发生更改的主机名。

# zpool history -l
History for 'tank':
2013-02-26.23:02:35 zpool create tank mirror /dev/ada0 /dev/ada1 [user 0 (root) on :global]
2013-02-27.18:50:58 zfs set atime=off tank [user 0 (root) on myzfsbox:global]
2013-02-27.18:51:09 zfs set checksum=fletcher4 tank [user 0 (root) on myzfsbox:global]
2013-02-27.18:51:18 zfs create tank/backup [user 0 (root) on myzfsbox:global]

输出显示 root 用户使用磁盘 /dev/ada0/dev/ada1 创建了镜像池。在池创建后的命令中还显示了主机名 myzfsbox 。主机名的显示在将池从一个系统导出并在另一个系统导入时变得重要。可以通过为每个命令记录的主机名来区分在另一个系统上发出的命令。

将两个选项 zpool history 结合起来,以便为任何给定的池提供尽可能详细的信息。池历史记录在追踪执行的操作或需要更详细的输出进行调试时提供有价值的信息。

22.3.13. 性能监控

内置的监控系统可以实时显示池的 I/O 统计信息。它显示池中的可用空间和已使用空间的数量,每秒执行的读写操作次数以及使用的 I/O 带宽。默认情况下, ZFS 监视并显示系统中的所有池。提供池名称以限制监控到该池。一个基本示例:

# zpool iostat
               capacity     operations    bandwidth
pool        alloc   free   read  write   read  write
----------  -----  -----  -----  -----  -----  -----
data         288G  1.53T      2     11  11.3K  57.1K

要持续查看 I/O 活动,请在最后一个参数中指定一个数字,表示更新之间等待的秒数间隔。每个间隔后都会打印下一个统计行。按下 Ctrl+C 停止此连续监视。在间隔之后的命令行上给出第二个数字,以指定要显示的统计总数。

使用 -v 参数可以显示更详细的 I/O 统计信息。池中的每个设备都会显示一行统计信息。这对于查看在每个设备上执行的读写操作非常有用,并且可以帮助确定是否有任何单个设备导致池变慢。以下示例显示了一个具有两个设备的镜像池。

# zpool iostat -v
                            capacity     operations    bandwidth
pool                     alloc   free   read  write   read  write
-----------------------  -----  -----  -----  -----  -----  -----
data                      288G  1.53T      2     12  9.23K  61.5K
  mirror                  288G  1.53T      2     12  9.23K  61.5K
    ada1                     -      -      0      4  5.61K  61.7K
    ada2                     -      -      1      4  5.04K  61.7K
-----------------------  -----  -----  -----  -----  -----  -----

22.3.14. 分割存储池

ZFS 可以将由一个或多个镜像 vdev 组成的池分割成两个池。除非另有指定,ZFS 会分离每个镜像的最后一个成员,并创建一个包含相同数据的新池。请务必先使用 -n 进行试运行。这将显示所请求操作的详细信息,但不会实际执行操作。这有助于确认操作是否符合用户的意图。

22.4. zfs 管理

zfs 实用程序可以在池中创建、销毁和管理所有现有的 ZFS 数据集。要管理池本身,请使用 zpool

22.4.1. 创建和销毁数据集

与传统的磁盘和卷管理器不同,ZFS 中的空间是 不预分配 的。在传统文件系统中,分区和分配空间后,无法在不添加新磁盘的情况下添加新的文件系统。而在 ZFS 中,可以随时创建新的文件系统。每个 数据集 都有属性,包括压缩、去重、缓存和配额等功能,以及其他有用的属性,如只读、大小写敏感、网络文件共享和挂载点。可以将数据集嵌套在彼此之间,并且子数据集将继承其祖先的属性。 委派复制快照jail 允许将每个数据集作为一个单元进行管理和销毁。为每种不同类型或文件集创建单独的数据集具有优势。拥有大量数据集的缺点是,某些命令(如 zfs list)的速度会变慢,并且挂载数百甚至数千个数据集会减慢 FreeBSD 的启动过程。

创建一个新的数据集,并在其上启用 LZ4 压缩

# zfs list
NAME                  USED  AVAIL  REFER  MOUNTPOINT
mypool                781M  93.2G   144K  none
mypool/ROOT           777M  93.2G   144K  none
mypool/ROOT/default   777M  93.2G   777M  /
mypool/tmp            176K  93.2G   176K  /tmp
mypool/usr            616K  93.2G   144K  /usr
mypool/usr/home       184K  93.2G   184K  /usr/home
mypool/usr/ports      144K  93.2G   144K  /usr/ports
mypool/usr/src        144K  93.2G   144K  /usr/src
mypool/var           1.20M  93.2G   608K  /var
mypool/var/crash      148K  93.2G   148K  /var/crash
mypool/var/log        178K  93.2G   178K  /var/log
mypool/var/mail       144K  93.2G   144K  /var/mail
mypool/var/tmp        152K  93.2G   152K  /var/tmp
# zfs create -o compress=lz4 mypool/usr/mydataset
# zfs list
NAME                   USED  AVAIL  REFER  MOUNTPOINT
mypool                 781M  93.2G   144K  none
mypool/ROOT            777M  93.2G   144K  none
mypool/ROOT/default    777M  93.2G   777M  /
mypool/tmp             176K  93.2G   176K  /tmp
mypool/usr             704K  93.2G   144K  /usr
mypool/usr/home        184K  93.2G   184K  /usr/home
mypool/usr/mydataset  87.5K  93.2G  87.5K  /usr/mydataset
mypool/usr/ports       144K  93.2G   144K  /usr/ports
mypool/usr/src         144K  93.2G   144K  /usr/src
mypool/var            1.20M  93.2G   610K  /var
mypool/var/crash       148K  93.2G   148K  /var/crash
mypool/var/log         178K  93.2G   178K  /var/log
mypool/var/mail        144K  93.2G   144K  /var/mail
mypool/var/tmp         152K  93.2G   152K  /var/tmp

销毁数据集比删除数据集上的文件要快得多,因为它不涉及扫描文件和更新相应的元数据。

销毁已创建的数据集:

# zfs list
NAME                   USED  AVAIL  REFER  MOUNTPOINT
mypool                 880M  93.1G   144K  none
mypool/ROOT            777M  93.1G   144K  none
mypool/ROOT/default    777M  93.1G   777M  /
mypool/tmp             176K  93.1G   176K  /tmp
mypool/usr             101M  93.1G   144K  /usr
mypool/usr/home        184K  93.1G   184K  /usr/home
mypool/usr/mydataset   100M  93.1G   100M  /usr/mydataset
mypool/usr/ports       144K  93.1G   144K  /usr/ports
mypool/usr/src         144K  93.1G   144K  /usr/src
mypool/var            1.20M  93.1G   610K  /var
mypool/var/crash       148K  93.1G   148K  /var/crash
mypool/var/log         178K  93.1G   178K  /var/log
mypool/var/mail        144K  93.1G   144K  /var/mail
mypool/var/tmp         152K  93.1G   152K  /var/tmp
# zfs destroy mypool/usr/mydataset
# zfs list
NAME                  USED  AVAIL  REFER  MOUNTPOINT
mypool                781M  93.2G   144K  none
mypool/ROOT           777M  93.2G   144K  none
mypool/ROOT/default   777M  93.2G   777M  /
mypool/tmp            176K  93.2G   176K  /tmp
mypool/usr            616K  93.2G   144K  /usr
mypool/usr/home       184K  93.2G   184K  /usr/home
mypool/usr/ports      144K  93.2G   144K  /usr/ports
mypool/usr/src        144K  93.2G   144K  /usr/src
mypool/var           1.21M  93.2G   612K  /var
mypool/var/crash      148K  93.2G   148K  /var/crash
mypool/var/log        178K  93.2G   178K  /var/log
mypool/var/mail       144K  93.2G   144K  /var/mail
mypool/var/tmp        152K  93.2G   152K  /var/tmp

在现代版本的 ZFS 中, zfs destroy 是异步的,释放的空间可能需要几分钟才会在池中显示出来。使用 zpool get freeing poolname 命令来查看 freeing 属性,该属性显示了哪些数据集正在后台释放其块。如果存在子数据集,例如 快照 或其他数据集,那么无法销毁父数据集。要销毁一个数据集及其子数据集,可以使用 -r 选项递归地销毁数据集及其子数据集。使用 -n -v 选项列出此操作销毁的数据集和快照,而不实际销毁任何内容。销毁快照释放的空间也会显示出来。

22.4.2. 创建和销毁卷

卷是一种特殊的数据集类型。它不像文件系统那样挂载,而是在 /dev/zvol/poolname/dataset 下以块设备的形式公开。这使得可以将卷用于其他文件系统,用于虚拟机的磁盘备份,或者通过 iSCSI 或 HAST 等协议使其对其他网络主机可用。

使用任何文件系统或者不使用文件系统来格式化一个卷,以存储原始数据。对于用户来说,一个卷看起来就像一个普通的磁盘。在这些 zvol 上放置普通的文件系统提供了普通磁盘或文件系统所没有的功能。例如,使用压缩属性在一个 250MB 的卷上可以创建一个压缩的 FAT 文件系统。

# zfs create -V 250m -o compression=on tank/fat32
# zfs list tank
NAME USED AVAIL REFER MOUNTPOINT
tank 258M  670M   31K /tank
# newfs_msdos -F32 /dev/zvol/tank/fat32
# mount -t msdosfs /dev/zvol/tank/fat32 /mnt
# df -h /mnt | grep fat32
Filesystem           Size Used Avail Capacity Mounted on
/dev/zvol/tank/fat32 249M  24k  249M     0%   /mnt
# mount | grep fat32
/dev/zvol/tank/fat32 on /mnt (msdosfs, local)

销毁一个卷与销毁一个常规文件系统数据集基本相同。该操作几乎是瞬时完成的,但在后台重新获取空闲空间可能需要几分钟的时间。

22.4.3. 重命名数据集

要更改数据集的名称,请使用 zfs rename 命令。要更改数据集的父级,请同样使用此命令。将数据集重命名为具有不同父级的数据集将更改从父级数据集继承的属性的值。将数据集重命名为新位置(从新父级数据集继承)将卸载然后重新挂载它。要防止此行为,请使用 -u 选项。

重命名数据集并将其移动到不同的父数据集下:

# zfs list
NAME                   USED  AVAIL  REFER  MOUNTPOINT
mypool                 780M  93.2G   144K  none
mypool/ROOT            777M  93.2G   144K  none
mypool/ROOT/default    777M  93.2G   777M  /
mypool/tmp             176K  93.2G   176K  /tmp
mypool/usr             704K  93.2G   144K  /usr
mypool/usr/home        184K  93.2G   184K  /usr/home
mypool/usr/mydataset  87.5K  93.2G  87.5K  /usr/mydataset
mypool/usr/ports       144K  93.2G   144K  /usr/ports
mypool/usr/src         144K  93.2G   144K  /usr/src
mypool/var            1.21M  93.2G   614K  /var
mypool/var/crash       148K  93.2G   148K  /var/crash
mypool/var/log         178K  93.2G   178K  /var/log
mypool/var/mail        144K  93.2G   144K  /var/mail
mypool/var/tmp         152K  93.2G   152K  /var/tmp
# zfs rename mypool/usr/mydataset mypool/var/newname
# zfs list
NAME                  USED  AVAIL  REFER  MOUNTPOINT
mypool                780M  93.2G   144K  none
mypool/ROOT           777M  93.2G   144K  none
mypool/ROOT/default   777M  93.2G   777M  /
mypool/tmp            176K  93.2G   176K  /tmp
mypool/usr            616K  93.2G   144K  /usr
mypool/usr/home       184K  93.2G   184K  /usr/home
mypool/usr/ports      144K  93.2G   144K  /usr/ports
mypool/usr/src        144K  93.2G   144K  /usr/src
mypool/var           1.29M  93.2G   614K  /var
mypool/var/crash      148K  93.2G   148K  /var/crash
mypool/var/log        178K  93.2G   178K  /var/log
mypool/var/mail       144K  93.2G   144K  /var/mail
mypool/var/newname   87.5K  93.2G  87.5K  /var/newname
mypool/var/tmp        152K  93.2G   152K  /var/tmp

重命名快照使用相同的命令。由于快照的特性,重命名不能改变它们的父数据集。要重命名递归快照,请指定 -r ;这也会重命名所有子数据集中具有相同名称的快照。

# zfs list -t snapshot
NAME                                USED  AVAIL  REFER  MOUNTPOINT
mypool/var/newname@first_snapshot      0      -  87.5K  -
# zfs rename mypool/var/newname@first_snapshot new_snapshot_name
# zfs list -t snapshot
NAME                                   USED  AVAIL  REFER  MOUNTPOINT
mypool/var/newname@new_snapshot_name      0      -  87.5K  -

22.4.4. 设置数据集属性

每个 ZFS 数据集都有控制其行为的属性。大多数属性会自动从父数据集继承,但可以在本地进行覆盖。使用 zfs set property=value dataset 在数据集上设置属性。大多数属性有一组有限的有效值,zfs get 将显示每个可能的属性和有效值。使用 zfs inherit 将大多数属性恢复为其继承的值。还可以定义用户自定义属性。它们成为数据集配置的一部分,并提供有关数据集或其内容的进一步信息。为了区分这些自定义属性和作为 ZFS 的一部分提供的属性,可以使用冒号(:)为属性创建自定义命名空间。

# zfs set custom:costcenter=1234 tank
# zfs get custom:costcenter tank
NAME PROPERTY           VALUE SOURCE
tank custom:costcenter  1234  local

要删除自定义属性,请使用 zfs inherit 命令并加上 -r 选项。如果自定义属性在任何父数据集中都没有定义,这个选项将删除它(但池的历史记录仍然会记录这个更改)。

# zfs inherit -r custom:costcenter tank
# zfs get custom:costcenter tank
NAME    PROPERTY           VALUE              SOURCE
tank    custom:costcenter  -                  -
# zfs get all tank | grep custom:costcenter
#

22.4.4.1. 获取和设置共享属性

两个常用且有用的数据集属性是 NFS 和 SMB 共享选项。设置这些选项可以定义 ZFS 在网络上共享数据集的方式和方式。目前,FreeBSD 仅支持设置 NFS 共享。要获取共享的当前状态,请输入:

# zfs get sharenfs mypool/usr/home
NAME             PROPERTY  VALUE    SOURCE
mypool/usr/home  sharenfs  on       local
# zfs get sharesmb mypool/usr/home
NAME             PROPERTY  VALUE    SOURCE
mypool/usr/home  sharesmb  off      local

要启用数据集的共享,请输入:

#  zfs set sharenfs=on mypool/usr/home

设置通过 NFS 共享数据集的其他选项,例如 -alldirs-maproot-network。要在通过 NFS 共享的数据集上设置选项,请输入:

#  zfs set sharenfs="-alldirs,-maproot=root,-network=192.168.1.0/24" mypool/usr/home

22.4.5. 管理快照

快照 是 ZFS 最强大的功能之一。快照提供了数据集的只读、时间点的副本。通过写时复制(COW),ZFS 通过在磁盘上保留旧版本的数据来快速创建快照。如果没有快照存在,当数据被重写或删除时,ZFS 会回收空间以供将来使用。快照通过记录当前数据集与先前版本之间的差异来保留磁盘空间。允许在整个数据集上进行快照,而不是在单个文件或目录上进行快照。数据集的快照复制其中包含的所有内容。这包括文件系统属性、文件、目录、权限等。快照在创建时不占用额外的空间,但随着它们引用的块的变化而消耗空间。使用 -r 进行递归快照会在数据集及其子数据集上创建具有相同名称的快照,提供文件系统的一致时刻快照。当应用程序在相关数据集上有文件或相互依赖时,这可能很重要。如果没有快照,备份将具有来自不同时间点的文件副本。

ZFS 中的快照提供了许多其他具有快照功能的文件系统所缺乏的功能。快照的典型用法是在执行风险操作(如软件安装或系统升级)时,快速备份文件系统的当前状态。如果操作失败,回滚到快照可以将系统恢复到创建快照时的相同状态。如果升级成功,可以删除快照以释放空间。如果没有快照,升级失败通常需要恢复备份,这是繁琐、耗时的,并且可能需要停机时间,期间系统无法使用。回滚到快照是快速的,即使系统在正常运行中,几乎没有停机时间。考虑到从备份中复制数据所需的时间,对于多 TB 存储系统来说,节省的时间是巨大的。快照不能替代对池的完整备份,但提供了一种快速简便的方式来存储特定时间点的数据集副本。

22.4.5.1. 创建快照

要创建快照,请使用 zfs snapshot dataset@snapshotname 命令。添加 -r 选项可以递归地创建快照,并在所有子数据集上使用相同的名称。

创建整个池的递归快照:

# zfs list -t all
NAME                                   USED  AVAIL  REFER  MOUNTPOINT
mypool                                 780M  93.2G   144K  none
mypool/ROOT                            777M  93.2G   144K  none
mypool/ROOT/default                    777M  93.2G   777M  /
mypool/tmp                             176K  93.2G   176K  /tmp
mypool/usr                             616K  93.2G   144K  /usr
mypool/usr/home                        184K  93.2G   184K  /usr/home
mypool/usr/ports                       144K  93.2G   144K  /usr/ports
mypool/usr/src                         144K  93.2G   144K  /usr/src
mypool/var                            1.29M  93.2G   616K  /var
mypool/var/crash                       148K  93.2G   148K  /var/crash
mypool/var/log                         178K  93.2G   178K  /var/log
mypool/var/mail                        144K  93.2G   144K  /var/mail
mypool/var/newname                    87.5K  93.2G  87.5K  /var/newname
mypool/var/newname@new_snapshot_name      0      -  87.5K  -
mypool/var/tmp                         152K  93.2G   152K  /var/tmp
# zfs snapshot -r mypool@my_recursive_snapshot
# zfs list -t snapshot
NAME                                        USED  AVAIL  REFER  MOUNTPOINT
mypool@my_recursive_snapshot                   0      -   144K  -
mypool/ROOT@my_recursive_snapshot              0      -   144K  -
mypool/ROOT/default@my_recursive_snapshot      0      -   777M  -
mypool/tmp@my_recursive_snapshot               0      -   176K  -
mypool/usr@my_recursive_snapshot               0      -   144K  -
mypool/usr/home@my_recursive_snapshot          0      -   184K  -
mypool/usr/ports@my_recursive_snapshot         0      -   144K  -
mypool/usr/src@my_recursive_snapshot           0      -   144K  -
mypool/var@my_recursive_snapshot               0      -   616K  -
mypool/var/crash@my_recursive_snapshot         0      -   148K  -
mypool/var/log@my_recursive_snapshot           0      -   178K  -
mypool/var/mail@my_recursive_snapshot          0      -   144K  -
mypool/var/newname@new_snapshot_name           0      -  87.5K  -
mypool/var/newname@my_recursive_snapshot       0      -  87.5K  -
mypool/var/tmp@my_recursive_snapshot           0      -   152K  -

普通的 zfs list 操作不会显示快照。要列出快照,请在 zfs list 后面添加 -t snapshot-t all 可以同时显示文件系统和快照。

快照不会直接挂载,因此在 MOUNTPOINT 列中不显示路径。ZFS 在 AVAIL 列中不提及可用磁盘空间,因为快照在创建后是只读的。将快照与原始数据集进行比较:

# zfs list -rt all mypool/usr/home
NAME                                    USED  AVAIL  REFER  MOUNTPOINT
mypool/usr/home                         184K  93.2G   184K  /usr/home
mypool/usr/home@my_recursive_snapshot      0      -   184K  -

同时显示数据集和快照可以展示快照以 写时复制 方式工作的原理。它们保存所做的更改(delta),而不是再次保存完整的文件系统内容。这意味着在进行更改时,快照所占用的空间很小。通过将文件复制到数据集中,然后创建第二个快照,可以更加观察空间使用情况:

# cp /etc/passwd /var/tmp
# zfs snapshot mypool/var/tmp@after_cp
# zfs list -rt all mypool/var/tmp
NAME                                   USED  AVAIL  REFER  MOUNTPOINT
mypool/var/tmp                         206K  93.2G   118K  /var/tmp
mypool/var/tmp@my_recursive_snapshot    88K      -   152K  -
mypool/var/tmp@after_cp                   0      -   118K  -

第二个快照包含了复制操作后数据集的变化。这样可以节省大量的空间。请注意,快照 mypool/var/tmp@my_recursive_snapshot 的大小在 USED 列中也发生了变化,以显示它与之后拍摄的快照之间的变化。

22.4.5.2. 比较快照

ZFS 提供了一个内置命令,用于比较两个快照之间内容的差异。当用户想要查看文件系统随时间变化的方式时,这非常有帮助,尤其是在有很多快照的情况下。例如, zfs diff 命令可以帮助用户找到最新的快照,其中仍然包含了意外删除的文件。对于前一节创建的两个快照,执行此命令将输出如下结果:

# zfs list -rt all mypool/var/tmp
NAME                                   USED  AVAIL  REFER  MOUNTPOINT
mypool/var/tmp                         206K  93.2G   118K  /var/tmp
mypool/var/tmp@my_recursive_snapshot    88K      -   152K  -
mypool/var/tmp@after_cp                   0      -   118K  -
# zfs diff mypool/var/tmp@my_recursive_snapshot
M       /var/tmp/
+       /var/tmp/passwd

该命令列出了指定快照(在本例中为 mypool/var/tmp@my_recursive_snapshot )与活动文件系统之间的变化。第一列显示变化类型:

+

添加路径或文件。

-

删除路径或文件。

M

修改路径或文件。

R

重命名路径或文件。

将输出与表格进行比较,可以清楚地看到 ZFS 在创建快照 mypool/var/tmp@my_recursive_snapshot 之后添加了 passwd 。这也导致了挂载在 /var/tmp 上的父目录的修改。

当使用 ZFS 复制功能将数据集传输到不同的主机进行备份时,比较两个快照是非常有帮助的。

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

# cp /var/tmp/passwd /var/tmp/passwd.copy
# zfs snapshot mypool/var/tmp@diff_snapshot
# zfs diff mypool/var/tmp@my_recursive_snapshot mypool/var/tmp@diff_snapshot
M       /var/tmp/
+       /var/tmp/passwd
+       /var/tmp/passwd.copy
# zfs diff mypool/var/tmp@my_recursive_snapshot mypool/var/tmp@after_cp
M       /var/tmp/
+       /var/tmp/passwd

备份管理员可以比较从发送主机接收到的两个快照,并确定数据集中的实际更改。有关更多信息,请参阅 复制 部分。

22.4.5.3. 快照回滚

当至少有一个快照可用时,随时可以回滚到该快照。大多数情况下,当数据集的当前状态不再存在或者更喜欢旧版本时,会出现这种情况。诸如本地开发测试失败、系统更新失败导致系统功能受阻,或者需要恢复已删除的文件或目录等情况都很常见。要回滚快照,请使用 zfs rollback snapshotname 命令。如果存在大量更改,操作将需要很长时间。在此期间,数据集始终保持一致的状态,就像符合 ACID 原则的数据库执行回滚操作一样。这一切都发生在数据集处于活动状态且可访问的情况下,无需停机。一旦快照回滚完成,数据集的状态与快照创建时的状态相同。回滚到快照会丢弃数据集中不属于该快照的所有其他数据。在回滚到以前的快照之前,将当前数据集的状态进行快照是一个好主意,以便稍后需要某些数据。这样,用户可以在快照之间来回切换,而不会丢失仍然有价值的数据。

在第一个示例中,由于一个粗心的 rm 操作删除了比预期更多的数据,因此需要回滚一个快照。

# zfs list -rt all mypool/var/tmp
NAME                                   USED  AVAIL  REFER  MOUNTPOINT
mypool/var/tmp                         262K  93.2G   120K  /var/tmp
mypool/var/tmp@my_recursive_snapshot    88K      -   152K  -
mypool/var/tmp@after_cp               53.5K      -   118K  -
mypool/var/tmp@diff_snapshot              0      -   120K  -
# ls /var/tmp
passwd          passwd.copy     vi.recover
# rm /var/tmp/passwd*
# ls /var/tmp
vi.recover

在这一点上,用户注意到额外文件被删除了,并希望将它们恢复。 ZFS 提供了一种简单的方法来使用回滚将它们恢复,当定期对重要数据进行快照时。要将文件恢复并从最后一个快照重新开始,执行以下命令:

# zfs rollback mypool/var/tmp@diff_snapshot
# ls /var/tmp
passwd          passwd.copy     vi.recover

回滚操作将数据集恢复到最后一个快照的状态。也可以回滚到之前拍摄的快照之后拍摄的其他快照的状态。在尝试这样做时,ZFS 会发出以下警告:

# zfs list -rt snapshot mypool/var/tmp
AME                                   USED  AVAIL  REFER  MOUNTPOINT
mypool/var/tmp@my_recursive_snapshot    88K      -   152K  -
mypool/var/tmp@after_cp               53.5K      -   118K  -
mypool/var/tmp@diff_snapshot              0      -   120K  -
# zfs rollback mypool/var/tmp@my_recursive_snapshot
cannot rollback to 'mypool/var/tmp@my_recursive_snapshot': more recent snapshots exist
use '-r' to force deletion of the following snapshots:
mypool/var/tmp@after_cp
mypool/var/tmp@diff_snapshot

这个警告意味着在当前数据集状态和用户想要回滚的快照之间存在快照。要完成回滚操作,请删除这些快照。由于快照是只读的,ZFS 无法跟踪数据集不同状态之间的所有更改。除非用户使用 -r 参数确认这是所需的操作,否则 ZFS 不会删除受影响的快照。如果这是用户的意图,并且理解丢失所有中间快照的后果,请执行以下命令:

# zfs rollback -r mypool/var/tmp@my_recursive_snapshot
# zfs list -rt snapshot mypool/var/tmp
NAME                                   USED  AVAIL  REFER  MOUNTPOINT
mypool/var/tmp@my_recursive_snapshot     8K      -   152K  -
# ls /var/tmp
vi.recover

zfs list -t snapshot 的输出确认了由于 zfs rollback -r 的结果,中间快照已被删除。

22.4.5.4. 从快照中恢复单个文件

快照存储在父数据集的隐藏目录下: .zfs/snapshots/snapshotname 。默认情况下,即使执行标准的 ls -a 命令,这些目录也不会显示出来。尽管目录不可见,但可以像访问普通目录一样访问它。名为 snapdir 的属性控制这些隐藏目录是否在目录列表中显示。将该属性设置为 visible 可以使它们出现在 ls 和其他处理目录内容的命令的输出中。

# zfs get snapdir mypool/var/tmp
NAME            PROPERTY  VALUE    SOURCE
mypool/var/tmp  snapdir   hidden   default
# ls -a /var/tmp
.               ..              passwd          vi.recover
# zfs set snapdir=visible mypool/var/tmp
# ls -a /var/tmp
.               ..              .zfs            passwd          vi.recover

通过将文件从快照复制回父数据集,将其恢复到先前的状态。 .zfs/snapshot 下的目录结构中有一个类似于先前拍摄的快照的目录,以便更容易识别它们。下一个示例显示了如何从隐藏的 .zfs 目录中复制文件,从包含文件最新版本的快照中恢复文件:

# rm /var/tmp/passwd
# ls -a /var/tmp
.               ..              .zfs            vi.recover
# ls /var/tmp/.zfs/snapshot
after_cp                my_recursive_snapshot
# ls /var/tmp/.zfs/snapshot/after_cp
passwd          vi.recover
# cp /var/tmp/.zfs/snapshot/after_cp/passwd /var/tmp

即使将 snapdir 属性设置为隐藏,运行 ls .zfs/snapshot 仍然会列出该目录的内容。管理员决定是否显示这些目录。这是每个数据集的设置。从这个隐藏的 .zfs/snapshot 复制文件或目录非常简单。尝试反过来操作会导致以下错误:

# cp /etc/rc.conf /var/tmp/.zfs/snapshot/after_cp/
cp: /var/tmp/.zfs/snapshot/after_cp/rc.conf: Read-only file system

该错误提醒用户快照是只读的,创建后不能更改。将文件复制到快照目录或从中删除文件都是不允许的,因为这会改变所表示数据集的状态。

快照占用的空间取决于父文件系统自快照以来的更改量。快照的 written 属性跟踪快照使用的空间。

要销毁快照并回收空间,请使用 zfs destroy dataset@snapshot 命令。添加 -r 选项可以递归删除父数据集下具有相同名称的所有快照。在命令中添加 -n -v 选项可以显示要删除的快照列表以及执行实际销毁操作前将回收的空间的估计值。

22.4.6. 管理克隆实例

克隆是快照的副本,更像一个常规数据集。与快照不同,克隆是可写的和可挂载的,并且具有自己的属性。使用 zfs clone 创建克隆后,无法销毁原始快照。要反转克隆和快照之间的子/父关系,请使用 zfs promote 。将克隆提升为快照成为克隆的子项,而不是原始父数据集的子项。这将改变 ZFS 对空间的计算方式,但实际上不会改变所消耗的空间量。可以在 ZFS 文件系统层次结构中的任何位置挂载克隆,不仅限于快照的原始位置下方。

要展示克隆功能,请使用以下示例数据集:

# zfs list -rt all camino/home/joe
NAME                    USED  AVAIL  REFER  MOUNTPOINT
camino/home/joe         108K   1.3G    87K  /usr/home/joe
camino/home/joe@plans    21K      -  85.5K  -
camino/home/joe@backup    0K      -    87K  -

克隆的典型用途是在进行特定数据集的实验时,保留快照以备不时之需。由于快照是不可更改的,因此需要创建一个可读/写的快照克隆。在克隆中获得所需的结果后,将克隆提升为数据集并删除旧的文件系统。严格来说,删除父数据集并非必需,因为克隆和数据集可以共存而不会出现问题。

# zfs clone camino/home/joe@backup camino/home/joenew
# ls /usr/home/joe*
/usr/home/joe:
backup.txz     plans.txt

/usr/home/joenew:
backup.txz     plans.txt
# df -h /usr/home
Filesystem          Size    Used   Avail Capacity  Mounted on
usr/home/joe        1.3G     31k    1.3G     0%    /usr/home/joe
usr/home/joenew     1.3G     31k    1.3G     0%    /usr/home/joenew

创建克隆会使其成为数据集在拍摄快照时的精确副本。现在可以独立地更改克隆与其源数据集之间的连接。两者之间的连接是快照。 ZFS 将此连接记录在属性 origin 中。使用 zfs promote 提升克隆将使其成为独立的数据集。这将删除 origin 属性的值,并断开新独立数据集与快照之间的连接。以下示例说明了这一点:

# zfs get origin camino/home/joenew
NAME                  PROPERTY  VALUE                     SOURCE
camino/home/joenew    origin    camino/home/joe@backup    -
# zfs promote camino/home/joenew
# zfs get origin camino/home/joenew
NAME                  PROPERTY  VALUE   SOURCE
camino/home/joenew    origin    -       -

在进行一些更改后,例如将 loader.conf 复制到推广的克隆中,旧目录在这种情况下变得过时。相反,推广的克隆可以替代它。为了做到这一点,首先使用 zfs destroy 命令销毁旧数据集,然后使用 zfs rename 命令将克隆重命名为旧数据集的名称(或完全不同的名称)。

# cp /boot/defaults/loader.conf /usr/home/joenew
# zfs destroy -f camino/home/joe
# zfs rename camino/home/joenew camino/home/joe
# ls /usr/home/joe
backup.txz     loader.conf     plans.txt
# df -h /usr/home
Filesystem          Size    Used   Avail Capacity  Mounted on
usr/home/joe        1.3G    128k    1.3G     0%    /usr/home/joe

克隆的快照现在是一个普通的数据集。它包含了原始快照中的所有数据,以及像 loader.conf 这样添加到其中的文件。在不同的场景中,克隆为 ZFS 用户提供了有用的功能。例如,可以将 jails 作为包含不同安装应用程序集的快照。用户可以克隆这些快照,并根据需要添加自己的应用程序。一旦对更改满意,可以将克隆提升为完整的数据集,并将其提供给最终用户,就像使用真实数据集一样。这样可以节省提供这些监狱时的时间和管理开销。

22.4.7. 复制

将数据存储在一个位置的单个池中会使其暴露于盗窃、自然灾害或人为灾害等风险。定期备份整个池是至关重要的。 ZFS 提供了一个内置的序列化功能,可以将数据的流表示发送到标准输出。使用此功能,可以将这些数据存储在连接到本地系统的另一个池中,也可以将其发送到另一个系统上的网络上。快照是复制的基础(参见 ZFS 快照 部分)。用于复制数据的命令是 zfs sendzfs receive

这些示例展示了使用这两个存储池进行 ZFS 复制的情况:

# zpool list
NAME    SIZE  ALLOC   FREE   CKPOINT  EXPANDSZ   FRAG   CAP  DEDUP  HEALTH  ALTROOT
backup  960M    77K   896M         -         -     0%    0%  1.00x  ONLINE  -
mypool  984M  43.7M   940M         -         -     0%    4%  1.00x  ONLINE  -

名为 mypool 的池是主要的池,数据的写入和读取在此池中定期进行。在主要池不可用时,使用第二个备用池 backup。请注意,ZFS 不会自动执行此故障转移,而是需要系统管理员在需要时手动执行。使用快照提供一致的文件系统版本进行复制。在创建 mypool 的快照后,通过复制快照将其复制到 backup 池中。这不包括自最近快照以来所做的更改。

# zfs snapshot mypool@backup1
# zfs list -t snapshot
NAME                    USED  AVAIL  REFER  MOUNTPOINT
mypool@backup1             0      -  43.6M  -

现在有了一个快照,使用 zfs send 命令创建一个表示快照内容的流。将这个流存储为文件,或者在另一个存储池中接收它。将流写入标准输出,但要将其重定向到文件或管道,否则会出现错误。

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

使用 zfs send 命令备份数据集时,将其重定向到位于已挂载的备份池上的文件。确保备份池有足够的空间来容纳发送的快照的大小,这指的是快照中包含的数据,而不是与上一个快照的更改。

# zfs send mypool@backup1 > /backup/backup1
# zpool list
NAME    SIZE  ALLOC   FREE   CKPOINT  EXPANDSZ   FRAG    CAP  DEDUP  HEALTH  ALTROOT
backup  960M  63.7M   896M         -         -     0%     6%  1.00x  ONLINE  -
mypool  984M  43.7M   940M         -         -     0%     4%  1.00x  ONLINE  -

zfs send 命令将名为 backup1 的快照中的所有数据传输到名为 backup 的存储池中。要自动创建和发送这些快照,请使用 cron(8) 任务。

ZFS 可以将备份存储为实时文件系统,而不是存储为归档文件,从而可以直接访问备份数据。要访问这些流中包含的实际数据,可以使用 zfs receive 将流转换回文件和目录。下面的示例结合了 zfs sendzfs receive ,使用管道将数据从一个存储池复制到另一个存储池。在传输完成后,可以直接在接收存储池上使用数据。只能将数据集复制到空数据集。

# zfs snapshot mypool@replica1
# zfs send -v mypool@replica1 | zfs receive backup/mypool
send from @ to mypool@replica1 estimated size is 50.1M
total estimated size is 50.1M
TIME        SENT   SNAPSHOT

# zpool list
NAME    SIZE  ALLOC   FREE   CKPOINT  EXPANDSZ   FRAG    CAP  DEDUP  HEALTH  ALTROOT
backup  960M  63.7M   896M         -         -     0%     6%  1.00x  ONLINE  -
mypool  984M  43.7M   940M         -         -     0%     4%  1.00x  ONLINE  -

22.4.7.1. 增量备份

zfs send 还可以确定两个快照之间的差异,并发送两者之间的个别差异。这样可以节省磁盘空间和传输时间。例如:

# zfs snapshot mypool@replica2
# zfs list -t snapshot
NAME                    USED  AVAIL  REFER  MOUNTPOINT
mypool@replica1         5.72M      -  43.6M  -
mypool@replica2             0      -  44.1M  -
# zpool list
NAME    SIZE  ALLOC   FREE   CKPOINT  EXPANDSZ   FRAG   CAP  DEDUP  HEALTH  ALTROOT
backup  960M  61.7M   898M         -         -     0%    6%  1.00x  ONLINE  -
mypool  960M  50.2M   910M         -         -     0%    5%  1.00x  ONLINE  -

创建一个名为 replica2 的第二个快照。这个第二个快照包含了从现在到上一个快照 replica1 之间对文件系统所做的更改。使用 zfs send -i 命令,并指定快照对,可以生成一个增量复制流,其中包含了更改的数据。如果接收端已经存在初始快照,则此操作将成功。

# zfs send -v -i mypool@replica1 mypool@replica2 | zfs receive /backup/mypool
send from @replica1 to mypool@replica2 estimated size is 5.02M
total estimated size is 5.02M
TIME        SENT   SNAPSHOT

# zpool list
NAME    SIZE  ALLOC   FREE   CKPOINT  EXPANDSZ   FRAG  CAP  DEDUP  HEALTH  ALTROOT
backup  960M  80.8M   879M         -         -     0%   8%  1.00x  ONLINE  -
mypool  960M  50.2M   910M         -         -     0%   5%  1.00x  ONLINE  -

# zfs list
NAME                         USED  AVAIL  REFER  MOUNTPOINT
backup                      55.4M   240G   152K  /backup
backup/mypool               55.3M   240G  55.2M  /backup/mypool
mypool                      55.6M  11.6G  55.0M  /mypool

# zfs list -t snapshot
NAME                                         USED  AVAIL  REFER  MOUNTPOINT
backup/mypool@replica1                       104K      -  50.2M  -
backup/mypool@replica2                          0      -  55.2M  -
mypool@replica1                             29.9K      -  50.0M  -
mypool@replica2                                 0      -  55.0M  -

增量流复制了变化的数据,而不是整个 replica1。仅发送差异所需的传输时间更短,并且通过不每次复制整个池来节省磁盘空间。这在通过慢速网络或按传输字节计费的网络上进行复制时非常有用。

一个新的文件系统,backup/mypool,可用于存储来自池 mypool 的文件和数据。使用 -p 参数可以复制数据集的属性,包括压缩设置、配额和挂载点。使用 -R 参数可以复制数据集的所有子数据集以及它们的属性。自动化发送和接收操作可以在第二个池中创建定期备份。

22.4.7.2. 通过 SSH 发送加密备份

通过网络发送流是保持远程备份的好方法,但它也有一个缺点。通过网络链路发送的数据没有加密,允许任何人在不知情的情况下拦截和转换流为数据。当将流发送到远程主机时,这是不可取的。使用 SSH 来安全地加密通过网络连接发送的数据。由于 ZFS 需要将流重定向到标准输出,所以通过 SSH 进行管道传输很容易。为了在传输和远程系统上保持文件系统的内容加密,考虑使用 PEFS

首先,更改一些设置并采取安全预防措施。这描述了执行 zfs send 操作所需的必要步骤;有关 SSH 的更多信息,请参见 OpenSSH

将配置更改如下:

  • 使用 SSH 密钥在发送和接收主机之间实现无密码 SSH 访问

  • ZFS 需要 root 用户的权限来发送和接收流。这需要以 root 用户身份登录到接收系统。

  • 出于安全原因,默认情况下禁止 root 用户登录。

  • 使用 ZFS 委派 系统,允许每个系统上的非 root 用户执行相应的发送和接收操作。在发送系统上:

    # zfs allow -u someuser 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 someuser create,mount,receive recvpool/backup
# chown someuser /recvpool/backup

非特权用户现在可以接收和挂载数据集,并将 home 数据集复制到远程系统:

% zfs snapshot -r mypool/home@monday
% zfs send -R mypool/home@monday | ssh someuser@backuphost zfs recv -dvu recvpool/backup

在池 mypool 上创建一个名为 monday 的文件系统数据集 home 的递归快照。然后,zfs send -R 命令将数据集、所有子数据集、快照、克隆和设置包含在流中。通过 SSH 将输出导向等待的远程主机 backuphost 上的 zfs receive 命令。使用 IP 地址或完全限定域名是良好的实践。接收机将数据写入 recvpool 池上的 backup 数据集。在 zfs recv 命令中添加 -d 选项会使用快照的名称覆盖接收端池的名称。 -u 选项会导致接收端上的文件系统不挂载。使用 -v 选项可以显示有关传输的更多详细信息,包括经过的时间和传输的数据量。

22.4.8. 数据集、用户和组配额

使用 数据集配额 来限制特定数据集消耗的空间量。 引用配额 的工作方式类似,但是计算的是数据集本身使用的空间,不包括快照和子数据集。同样地,使用 用户配额 组配额 来防止用户或组使用完池或数据集中的所有空间。

以下示例假设用户已经存在于系统中。在将用户添加到系统之前,请确保首先创建他们的主目录数据集,并将 mountpoint 设置为 /home/bob 。然后,创建用户并将主目录指向数据集的 mountpoint 位置。这将正确设置所有者和组权限,而不会遮盖可能存在的任何预先存在的主目录路径。

为了对 storage/home/bob 强制执行一个 10 GB 的数据集配额:

# zfs set quota=10G storage/home/bob

为了强制限制 storage/home/bob 的引用配额为 10 GB :

# zfs set refquota=10G storage/home/bob

要移除 storage/home/bob 的 10 GB 配额:

# zfs set quota=none storage/home/bob

通用格式为 userquota@user=size ,用户的名称必须符合以下格式之一:

  • POSIX 兼容的名称,例如 joe

  • POSIX 数值 ID,例如 789

  • SID 名称,例如 [email protected]

  • SID 是一个数字 ID,例如 S-1-123-456-789

例如,要为名为 joe 的用户强制执行 50 GB 的用户配额:

# zfs set userquota@joe=50G

要移除任何配额:

# zfs set userquota@joe=none

zfs get all 命令不会显示用户配额属性。非 root 用户除非被授予 userquota 权限,否则无法查看其他用户的配额。拥有此权限的用户可以查看和设置所有人的配额。

设置群组配额的一般格式为:groupquota@group=size

要将组 firstgroup 的配额设置为 50 GB,请使用以下命令:

# zfs set groupquota@firstgroup=50G

要删除组 firstgroup 的配额,或确保未设置配额,请使用以下命令:

# zfs set groupquota@firstgroup=none

与用户配额属性一样,非 root 用户可以查看与他们所属的组相关联的配额。具有 groupquota 特权或 root 权限的用户可以查看和设置所有组的所有配额。

要显示文件系统或快照中每个用户使用的空间量以及任何配额,请使用 zfs userspace。要获取组信息,请使用 zfs groupspace。有关支持的选项或如何仅显示特定选项的更多信息,请参阅 zfs(1)

特权用户和 root 用户可以使用以下命令列出 storage/home/bob 目录的配额:

# zfs get quota storage/home/bob

22.4.9. 预留空间

预留空间 保证了数据集上始终可用的一定量空间。预留的空间将不会对其他数据集可用。这个有用的功能确保了重要数据集或日志文件的可用的空闲空间。

reservation 属性的一般格式是 reservation=size,因此要在 storage/home/bob 上设置 10 GB 的预留空间,可以使用以下命令:

# zfs set reservation=10G storage/home/bob

取消任何预留空间:

# zfs set reservation=none storage/home/bob

同样的原则适用于设置 refreservation 属性以设置 引用预留 ,其一般格式为 refreservation =size

该命令显示位于 storage/home/bob 目录下的任何预留空间或引用预留空间。

# zfs get reservation storage/home/bob
# zfs get refreservation storage/home/bob

22.4.10. 压缩

ZFS 提供透明压缩功能。在块级别压缩数据可以节省空间,同时提高磁盘吞吐量。如果数据压缩率为 25% ,压缩后的数据写入磁盘的速度与未压缩版本相同,从而实现了 125% 的有效写入速度。压缩也可以作为 去重 的一个很好的替代方案,因为它不需要额外的内存。

ZFS 提供了不同的压缩算法,每种算法都有不同的权衡。在 ZFS v5000 中引入的 LZ4 压缩算法可以在不牺牲性能的情况下对整个池进行压缩。 LZ4 的最大优势是 提前中止(early abort) 功能。如果 LZ4 在数据的头部部分无法达到至少 12.5% 的压缩率, ZFS 会以未压缩的方式写入该块,以避免浪费 CPU 周期尝试压缩已经压缩或无法压缩的数据。有关 ZFS 中可用的不同压缩算法的详细信息,请参阅术语部分中的 压缩 条目。

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

# zfs get used,compressratio,compression,logicalused mypool/compressed_dataset
NAME        PROPERTY          VALUE     SOURCE
mypool/compressed_dataset  used              449G      -
mypool/compressed_dataset  compressratio     1.11x     -
mypool/compressed_dataset  compression       lz4       local
mypool/compressed_dataset  logicalused       496G      -

该数据集使用了 449 GB 的空间(使用的属性)。如果不进行压缩,它将占用 496 GB 的空间(逻辑使用的属性)。这导致了 1.11:1 的压缩比率。

当与 用户配额 结合使用时,压缩可能会产生意想不到的副作用。用户配额限制了用户在数据集上实际消耗的空间(在压缩之后)。如果一个用户的配额是 10GB ,并写入了 10GB 的可压缩数据,他们仍然可以存储更多的数据。如果他们稍后更新一个文件,比如一个数据库,其中包含更多或更少可压缩的数据,可用空间的数量将会改变。这可能导致一个奇怪的情况,即用户没有增加实际数据量( logicalused 属性),但压缩的变化导致他们达到了配额限制。

压缩可能会与备份产生类似的意外交互作用。配额通常用于限制数据存储,以确保有足够的备份空间可用。由于配额不考虑压缩,因此 ZFS 可能会写入比未压缩备份所能容纳的更多数据。

22.4.11. Zstandard 压缩

OpenZFS 2.0 增加了一种新的压缩算法。Zstandard(Zstd)提供比默认的 LZ4 更高的压缩比,同时比替代方案 gzip 速度更快。OpenZFS 2.0 可以通过 FreeBSD 12.1-RELEASE 的 sysutils/openzfs 包获得,并且自 FreeBSD 13.0-RELEASE 以来已成为默认选项。

Zstd 提供了多种压缩级别,可以对性能和压缩比进行细粒度控制。Zstd 的一个主要优势是解压速度与压缩级别无关。对于只写入一次但经常读取的数据, Zstd 允许使用最高的压缩级别而不会影响读取性能。

即使进行频繁的数据更新,启用压缩通常也能提供更高的性能。其中最大的优势之一来自于压缩的 ARC 功能。 ZFS 的自适应替换缓存(ARC)将数据的压缩版本缓存在 RAM 中,每次需要时进行解压缩。这使得相同数量的 RAM 能够存储更多的数据和元数据,从而增加了缓存命中率。

ZFS 提供 19 个级别的 Zstd 压缩,每个级别都可以在更慢的压缩速度下提供更多的空间节省。默认级别是 zstd-3,比 LZ4 提供更好的压缩效果,但速度不会慢太多。级别超过 10 需要大量的内存来压缩每个块,内存小于 16GB 的系统不应使用这些级别。ZFS 还使用了一系列的 Zstd_fast_ 级别,它们的压缩速度更快,但支持的压缩比较低。ZFS 支持 zstd-fast-1zstd-fast-10,以及以 10 为增量的 zstd-fast-20zstd-fast-100,还有 zstd-fast-500zstd-fast-1000,它们提供最小的压缩效果,但具有高性能。

如果 ZFS 无法获取所需的内存来使用 Zstd 压缩块,它将退回到存储未压缩的块。除非在内存受限的系统上使用 Zstd 的最高级别,否则这种情况不太可能发生。ZFS 通过 kstat.zfs.misc.zstd.compress_alloc_fail 统计自加载 ZFS 模块以来发生此情况的次数。

22.4.12. 去重

当启用时,去重 使用每个块的校验和来检测重复块。当一个新块是现有块的副本时,ZFS 会写入一个对现有数据的新引用,而不是整个重复块。如果数据包含大量重复的文件或重复的信息,则可以实现巨大的空间节省。警告:去重需要大量的内存,并且启用压缩可以在不增加额外成本的情况下提供大部分的空间节省。

要激活去重功能,请在目标池上设置 dedup 属性:

# zfs set dedup=on pool

去重只会影响写入池中的新数据。仅仅激活此选项不会对已经写入池中的数据进行去重。一个刚刚激活去重属性的池子将会像这个例子一样:

# zpool list
NAME  SIZE ALLOC  FREE   CKPOINT  EXPANDSZ   FRAG   CAP   DEDUP   HEALTH   ALTROOT
pool 2.84G 2.19M 2.83G         -         -     0%    0%   1.00x   ONLINE   -

DEDUP 列显示了池的实际重复消除率。1.00x 的值表示数据尚未进行重复消除。下面的示例将一些系统二进制文件复制了三次,分别放入在上述创建的重复消除池中的不同目录中。

# for d in dir1 dir2 dir3; do
> mkdir $d && cp -R /usr/bin $d &
> done

要观察冗余数据的去重,请使用:

# zpool list
NAME SIZE  ALLOC  FREE   CKPOINT  EXPANDSZ   FRAG  CAP   DEDUP   HEALTH   ALTROOT
pool 2.84G 20.9M 2.82G         -         -     0%   0%   3.00x   ONLINE   -

DEDUP 列显示了一个 3.00x 的因子。检测和去重数据的副本使用了三分之一的空间。节省空间的潜力是巨大的,但需要足够的内存来跟踪去重块的成本。

当池中的数据不冗余时,去重并不总是有益的。ZFS 可以通过在现有池上模拟去重来显示潜在的节省空间。

# zdb -S pool
Simulated DDT histogram:

bucket              allocated                       referenced
______   ______________________________   ______________________________
refcnt   blocks   LSIZE   PSIZE   DSIZE   blocks   LSIZE   PSIZE   DSIZE
------   ------   -----   -----   -----   ------   -----   -----   -----
     1    2.58M    289G    264G    264G    2.58M    289G    264G    264G
     2     206K   12.6G   10.4G   10.4G     430K   26.4G   21.6G   21.6G
     4    37.6K    692M    276M    276M     170K   3.04G   1.26G   1.26G
     8    2.18K   45.2M   19.4M   19.4M    20.0K    425M    176M    176M
    16      174   2.83M   1.20M   1.20M    3.33K   48.4M   20.4M   20.4M
    32       40   2.17M    222K    222K    1.70K   97.2M   9.91M   9.91M
    64        9     56K   10.5K   10.5K      865   4.96M    948K    948K
   128        2   9.50K      2K      2K      419   2.11M    438K    438K
   256        5   61.5K     12K     12K    1.90K   23.0M   4.47M   4.47M
    1K        2      1K      1K      1K    2.98K   1.49M   1.49M   1.49M
 Total    2.82M    303G    275G    275G    3.20M    319G    287G    287G

dedup = 1.05, compress = 1.11, copies = 1.00, dedup * compress / copies = 1.16

zdb -S 完成分析池之后,它会显示激活去重将实现的空间减少比例。在这种情况下, 1.16 是一个主要由压缩提供的较低的节省空间比例。在此池上激活去重将不会节省任何空间,并且不值得为启用去重所需的内存量。使用公式 ratio = dedup * compress / copies,系统管理员可以规划存储分配,决定工作负载是否包含足够的重复块来证明内存需求的合理性。如果数据可以合理压缩,空间节省可能会很好。良好的做法是首先启用压缩,因为压缩还可以大大提高性能。在节省可观且有足够可用内存用于 DDT 的情况下启用去重。

22.4.13. ZFS 和 Jails

使用 zfs jail 命令和相应的 jailed 属性将一个 ZFS 数据集委派给一个 jailzfs jail jailid 命令将数据集附加到指定的 jail,zfs unjail 命令将其分离。要在 jails 内控制数据集,需要设置 jailed 属性。由于被 jailed 的数据集可能具有会危及主机安全的挂载点,ZFS 禁止在主机上挂载被 jailed 的数据集。

22.5. 委派管理

一个全面的权限委派系统允许非特权用户执行 ZFS 管理功能。例如,如果每个用户的主目录是一个数据集,用户需要有权限创建和销毁其主目录的快照。执行备份的用户可以获得使用复制功能的权限。ZFS 允许一个使用统计脚本仅以访问所有用户的空间使用数据的方式运行。还可以委派委派权限的能力。权限委派对于每个子命令和大多数属性都是可能的。

22.5.1. 委托数据集创建

zfs allow someuser create mydataset 给予指定用户在选定的父数据集下创建子数据集的权限。但需要注意的是,创建新数据集需要挂载它。这要求设置 FreeBSD 的 vfs.usermount 系统控制变量为 1,以允许非 root 用户挂载文件系统。另外,为了防止滥用,非 root 用户必须拥有挂载点来进行文件系统的挂载。

22.5.2. 委派权限委派

zfs allow someuser allow mydataset 命令会赋予指定用户在目标数据集及其子数据集上分配其拥有的任何权限给其他用户的能力。如果一个用户拥有 snapshot 权限和 allow 权限,那么该用户可以将 snapshot 权限授予其他用户。

22.6. 高级主题

22.6.1. 调优

调整可调节参数以使 ZFS 在不同的工作负载下表现最佳。

  • vfs.zfs.arc.max 从 13.x 开始(12.x 为 vfs.zfs.arc_max) - ARC 的上限大小。默认值为所有 RAM 减去 1 GB ,或所有 RAM 的 5/8 ,以较大者为准。如果系统运行其他可能需要内存的守护程序或进程,请使用较低的值。可以使用 sysctl(8) 在运行时调整此值,并在 /boot/loader.conf/etc/sysctl.conf 中设置。

  • vfs.zfs.arc.meta_limit 从 13.x 版本开始(12.x 版本为 vfs.zfs.arc_meta_limit) - 限制用于存储元数据的 ARC 的数量。默认值为 vfs.zfs.arc.max 的四分之一。如果工作负载涉及大量文件和目录的操作,或者频繁的元数据操作,增加此值将提高性能,但会减少适应于 ARC 的文件数据量。可以使用 sysctl(8)/boot/loader.conf/etc/sysctl.conf 中在运行时调整此值。

  • vfs.zfs.arc.min 从 13.x 开始(12.x 为 vfs.zfs.arc_min) - 降低 ARC 的大小。默认值为 vfs.zfs.arc.meta_limit 的一半。调整此值以防止其他应用程序将整个 ARC 压力过大。可以使用 sysctl(8) 在运行时调整此值,并在 /boot/loader.conf/etc/sysctl.conf 中进行调整。

  • vfs.zfs.vdev.cache.size - 在池中的每个设备上预留的用作缓存的内存的预分配量。使用的总内存量将是此值乘以设备数量。在引导时和 /boot/loader.conf 中设置此值。

  • vfs.zfs.min_auto_ashift - 在创建池时自动使用的较低的 ashift(扇区大小)。该值是 2 的幂。默认值为 9 表示 2^9 = 512 ,即 512 字节的扇区大小。为了避免写放大并获得最佳性能,将此值设置为池中设备使用的最大扇区大小。

    普通硬盘的扇区大小为 4 KB 。在这些硬盘上使用默认的 ashift9 会导致写放大。一个 4 KB 的写入数据实际上会被分成八个 512 字节的写入操作。在创建存储池时,ZFS 会尝试从所有设备中读取原生扇区大小,但是具有 4 KB 扇区的硬盘会报告它们的扇区大小为 512 字节,以保证兼容性。在创建存储池之前,将 vfs.zfs.min_auto_ashift 设置为 122^12 = 4096)可以强制 ZFS 在这些硬盘上使用 4 KB 块以获得最佳性能。

    对于计划进行磁盘升级的存储池来说,强制使用 4 KB 块也是有用的。未来的磁盘使用 4 KB 扇区,并且在创建存储池后无法更改 ashift 值。

    在某些特定情况下,较小的 512 字节块大小可能更可取。当与用于数据库或作为虚拟机存储的 512 字节磁盘一起使用时,小规模随机读取时的数据传输量较少。这可以在使用较小的 ZFS 记录大小时提供更好的性能。

  • vfs.zfs.prefetch_disable - 禁用预读取。值为 0 表示启用,值为 1 表示禁用。默认值为 0,除非系统内存小于 4 GB。预读取通过读取比请求的块更大的块到 ARC 中,希望很快需要这些数据。如果工作负载有大量的随机读取,禁用预读取可能会通过减少不必要的读取来提高性能。可以随时使用 sysctl(8) 调整此值。

  • vfs.zfs.vdev.trim_on_init - 控制是否在将新设备添加到池中时对其运行 TRIM 命令。这可以确保 SSD 的最佳性能和寿命,但会花费额外的时间。如果设备已经进行了安全擦除,则禁用此设置将使新设备的添加更快。随时可以使用 sysctl(8) 调整此值。

  • vfs.zfs.vdev.max_pending - 限制每个设备的待处理 I/O 请求的数量。较高的值将保持设备命令队列满,并可能提供更高的吞吐量。较低的值将减少延迟。您可以随时使用 sysctl(8) 调整此值。

  • vfs.zfs.top_maxinflight - 每个顶级 vdev 的未完成 I/O 的上限。限制命令队列的深度以防止高延迟。该限制是针对每个顶级 vdev 的,意味着该限制独立应用于每个 镜像RAID-Z 或其他 vdev 。可以随时使用 sysctl(8) 调整此值。

  • vfs.zfs.l2arc_write_max - 限制每秒写入 L2ARC 的数据量。通过限制写入设备的数据量,此可调整参数可以延长固态硬盘(SSD)的使用寿命。您可以随时使用 sysctl(8) 调整此值。

  • vfs.zfs.l2arc_write_boost - 将此可调整值添加到 vfs.zfs.l2arc_write_max 中,并增加对 SSD 的写入速度,直到从 L2ARC 中驱逐第一个块。这个"Turbo Warmup Phase"可以减少重启后空的 L2ARC 带来的性能损失。随时可以使用 sysctl(8) 调整此值。

  • vfs.zfs.scrub_delay - 在进行 scrub 时,每个 I/O 之间的延迟时间。为了确保 scrub 不会干扰池的正常操作,如果有其他 I/O 正在进行, scrub 会在每个命令之间进行延迟。该值控制了 scrub 生成的总 IOPS(每秒 I/O 数)的限制。设置的粒度由 kern.hz 的值确定,默认为每秒 1000 个滴答。更改此设置会导致不同的有效 IOPS 限制。默认值为 4 ,因此限制为: 1000 个滴答/秒 ÷ 4 = 250 IOPS 。使用值为 20 将得到限制:1000 个滴答/秒 ÷ 20 = 50 IOPS。池上的最近活动限制了 scrub 的速度,由 vfs.zfs.scan_idle 确定。随时可以使用 sysctl(8) 调整此值。

  • vfs.zfs.resilver_delay - 在 重建 过程中,每个 I/O 之间插入的延迟时间,以毫秒为单位。为了确保 resilver 不会干扰池的正常操作,如果有其他 I/O 正在进行,resilver 将在每个命令之间延迟。该值控制由 resilver 生成的总 IOPS(每秒 I/O 数)的限制。ZFS 通过 kern.hz 的值确定设置的粒度,默认为每秒 1000 个滴答。更改此设置会导致不同的有效 IOPS 限制。默认值为 2 ,结果为:1000 个滴答/秒 ÷ 2 = 500 IOPS 。如果另一个设备故障可能导致池发生故障,从而导致数据丢失,则将池恢复到 在线 状态可能更为重要。值为 0 将使 resilver 操作与其他操作具有相同的优先级,加快修复过程。池上的其他最近活动会限制 resilver 的速度,由 vfs.zfs.scan_idle 确定。随时可以使用 sysctl(8) 调整此值。

  • vfs.zfs.scan_idle - 距离上次操作的毫秒数,用于判断池是否处于空闲状态。当池处于空闲状态时, ZFS 会禁用对 scrub重建 的速率限制。您可以随时使用 sysctl(8) 调整此值。

  • vfs.zfs.txg.timeout - 两个 事务组 之间的最长时间间隔,以秒为单位。当前事务组将数据写入池中,并且如果自上一个事务组以来经过了这么长时间,则会启动一个新的事务组。如果写入的数据足够多,事务组可能会提前触发。默认值为 5 秒。增大该值可能会通过延迟异步写入来提高读取性能,但这可能会导致写入事务组时性能不均衡。可以随时使用 sysctl(8) 调整此值。

22.6.2. i386 上的 ZFS

ZFS 提供的一些功能对内存要求较高,可能需要在内存有限的系统上进行调整以达到最佳效率。

22.6.2.1. 内存

作为一个较低的值,总系统内存应至少为 1GB 。推荐的 RAM 数量取决于存储池的大小和 ZFS 使用的功能。一个经验法则是每 1TB 存储空间需要 1GB 的 RAM 。如果使用去重功能,一个经验法则是每 1TB 存储空间需要 5GB 的 RAM 用于去重。虽然一些用户在较少的 RAM 下使用 ZFS,但在负载较重的系统下可能会因内存耗尽而发生崩溃。对于 RAM 低于推荐要求的系统,可能需要进一步调整 ZFS 。

22.6.2.2. 内核配置

由于 i386™ 平台的地址空间限制, i386™ 架构上的 ZFS 用户必须将此选项添加到自定义内核配置文件中,重新构建内核并重新启动:

options        KVA_PAGES=512

这将扩展内核地址空间,允许 vm.kvm_size 可调参数超过 1 GB 的限制,或者超过 PAE 的 2 GB 限制。为了找到最合适的选项值,将所需的地址空间以兆字节为单位除以四。在这个例子中,为 2 GB 选择 512

22.6.2.3. 加载器可调参数

在所有 FreeBSD 架构上增加了 kmem 地址空间。一个具有 1 GB 物理内存的测试系统通过将这些选项添加到 /boot/loader.conf 并重新启动来受益。

vm.kmem_size="330M"
vm.kmem_size_max="330M"
vfs.zfs.arc.max="40M"
vfs.zfs.vdev.cache.size="5M"

有关 ZFS 相关调优的更详细建议列表,请参阅 https://wiki.freebsd.org/ZFSTuningGuide

22.8. ZFS 功能和术语

ZFS 不仅仅是一个文件系统,它在根本上是不同的。ZFS 将文件系统和卷管理器的角色结合在一起,使新的存储设备能够添加到活动系统中,并且新的空间可以立即在该池中的现有文件系统上使用。通过结合传统上分开的角色, ZFS 能够克服以前阻止 RAID 组能够增长的限制。vdev 是池中的顶级设备,可以是简单的磁盘或 RAID 转换,如镜像或 RAID-Z 阵列。 ZFS 文件系统(称为 datasets )每个都可以访问整个池的合并空闲空间。池中使用的块会减少每个文件系统可用的空间。这种方法避免了广泛分区中常见的问题,即空闲空间在分区之间变得碎片化。

池(pool)

存储池是 ZFS 的最基本构建块。一个存储池由一个或多个 vdev 组成, vdev 是存储数据的底层设备。然后使用存储池来创建一个或多个文件系统(数据集)或块设备(卷)。 这些数据集和卷共享剩余的自由空间。每个存储池都有一个唯一的名称和 GUID 来进行标识。存储池上的 ZFS 版本号决定了可用的功能。

vdev 类型

一个池由一个或多个 vdev 组成,它们本身是一个单独的磁盘或一组磁盘,转换为 RAID 。当使用大量的 vdev 时, ZFS 会将数据分散在 vdev 上,以提高性能和最大化可用空间。所有的 vdev 都必须至少 128MB 大小。

  • 磁盘(Disk) - 最基本的 vdev 类型是标准块设备。这可以是整个磁盘(如 /dev/ada0/dev/da0)或一个分区(/dev/ada0p3)。在 FreeBSD 上,使用分区而不是整个磁盘不会有性能损失。这与 Solaris 文档的建议不同。

    强烈不建议将整个磁盘作为可引导池的一部分,因为这可能导致池无法引导。 同样,您不应将整个磁盘用作镜像或 RAID-Z vdev 的一部分。 在引导时可靠地确定未分区磁盘的大小是不可能的,也没有地方放置引导代码。

  • 文件(File) - 普通文件可以组成 ZFS 池,这对于测试和实验很有用。在 zpool create 中,使用文件的完整路径作为设备路径。

  • 镜像(Mirror) - 创建镜像时,使用 mirror 关键字后跟镜像的成员设备列表。镜像由两个或更多设备组成,将所有数据写入所有成员设备。镜像 vdev 将保存与其最小成员相同的数据量。镜像 vdev 可以承受除一个成员外的所有成员故障而不丢失任何数据。

    要随时将常规单磁盘 vdev 升级为镜像 vdev,请使用 zpool attach

  • RAID-Z - ZFS 使用 RAID-Z ,这是标准 RAID-5 的变种,提供更好的奇偶校验分布,并消除了“RAID-5 写入漏洞”,即在意外重启后数据和奇偶校验信息变得不一致的问题。 ZFS 支持三个级别的 RAID-Z ,它们在提供不同的冗余级别的同时降低可用存储级别。ZFS 使用基于阵列中奇偶校验设备数量和可以故障的磁盘数量的 RAID-Z1 到 RAID-Z3 。

    在一个由四个 1TB 磁盘组成的 RAID-Z1 配置中,可用存储空间为 3TB ,并且池仍能在一个故障磁盘的降级模式下运行。如果在替换和重新同步故障磁盘之前另一个磁盘离线,将导致丢失所有池数据。

    在一个由八个 1TB 磁盘组成的 RAID-Z3 配置中,该卷将提供 5TB 的可用空间,并且仍能在三个故障磁盘的情况下运行。 Sun™ 建议在单个 vdev 中不要使用超过九个磁盘。如果更多的磁盘组成配置,则建议将它们分成单独的 vdev ,并将池数据分布在它们之间。

    由每个包含 8 个磁盘的 RAID-Z2 vdev 组成的配置将创建类似于 RAID-60 阵列的东西。 RAID-Z 组的存储容量约为最小磁盘大小乘以非奇偶校验磁盘数量。 RAID-Z1 中的四个 1TB 磁盘的有效大小约为 3TB ,而 RAID-Z3 中的八个 1TB 磁盘阵列将提供 5TB 的可用空间。

  • 备份(Spare) - ZFS 有一种特殊的伪 vdev 类型,用于跟踪可用的热备。请注意,已安装的热备不会自动部署;请使用 zfs replace 手动配置它们以替换故障设备。

  • 日志(Log) - ZFS 日志设备,也称为 ZFS 意图日志( ZIL ),将意图日志从常规池设备移动到专用设备,通常是 SSD 。拥有专用的日志设备可以提高具有大量同步写入(如数据库)的应用程序的性能。日志设备的镜像是可能的,但不支持 RAID-Z 。如果使用大量日志设备,写入将在它们之间进行负载平衡。

  • 缓存(Cache) - 向池添加缓存 vdev 将将缓存的存储添加到 L2ARC 中。镜像缓存设备是不可能的。由于缓存设备仅存储现有数据的新副本,不存在数据丢失的风险。

事务组 (TXG)

事务组是 ZFS 将块更改分组并写入池的方式。事务组是 ZFS 用来确保一致性的原子单位。 ZFS 为每个事务组分配一个唯一的 64 位连续标识符。同时可以有最多三个活动事务组,分别处于以下三种状态之一:

* 打开(Open) - 新的事务组开始时处于打开状态并接受新的写入。始终有一个处于打开状态的事务组,但如果达到限制,事务组可能会拒绝新的写入。一旦打开的事务组达到限制,或达到了 vfs.zfs.txg.timeout ,事务组将进入下一个状态。 * 静默(Quiescing) - 一个短暂的状态,允许所有待处理的操作完成,而不会阻塞新的打开事务组的创建。一旦组中的所有事务完成,事务组将进入最终状态。 * 同步(Syncing) - 将事务组中的所有数据写入稳定存储。这个过程将进一步改变其他数据,如元数据和空间映射, ZFS 也会将其写入稳定存储。同步过程涉及多个步骤。首先是所有更改的数据块;接下来是元数据,可能需要多次通过才能完成。由于为数据块分配空间会生成新的元数据,因此同步状态无法完成,直到完成一个不使用任何新空间的步骤。同步状态也是 synctasks 完成的地方。 Synctasks 是完成超级块更改的创建或销毁快照和数据集等管理操作。一旦同步状态完成,处于静默状态的事务组将进入同步状态。所有管理功能,如 快照 ,都作为事务组的一部分进行写入。 ZFS 将创建的 synctask 添加到打开的事务组中,并尽快将该组推进到同步状态,以减少管理命令的延迟。

自适应替换缓存(ARC)

ZFS 使用自适应替换缓存(ARC),而不是更传统的最近最少使用(LRU)缓存。LRU 缓存是一个简单的缓存项列表,按照对象最近使用的时间排序,将新项添加到列表的头部。当缓存已满时,从列表的尾部逐出项以为更活跃的对象腾出空间。 ARC 由四个列表组成:最近最常使用(MRU)对象和最频繁使用(MFU)对象,以及每个列表的幽灵列表。这些幽灵列表跟踪被逐出的对象,以防止将它们重新添加到缓存中。这通过避免具有偶尔使用历史的对象来提高缓存命中率。同时使用 MRU 和 MFU 的另一个优点是,扫描整个文件系统将逐出 MRU 或 LRU 缓存中的所有数据,以便为这些新访问的内容腾出空间。在 ZFS 中,还有一个跟踪最常使用对象的 MFU,并保留最常访问块的缓存。

L2ARC

L2ARC 是 ZFS 缓存系统的第二级。 RAM 存储主要的 ARC 。由于可用的 RAM 数量通常有限, ZFS 还可以使用 缓存 vdevs 。由于与传统的旋转硬盘相比,固态硬盘(SSD)具有更高的速度和更低的延迟,因此通常将其用作缓存设备。 L2ARC 是完全可选的,但拥有一个 L2ARC 将提高从 SSD 读取缓存文件的速度,而不必从常规磁盘读取。 L2ARC 还可以加速 去重,因为不适合 RAM 但适合 L2ARC 的去重表(DDT)比必须从磁盘读取的 DDT 快得多。对缓存设备添加的数据速率限制可以防止额外的写入过早磨损 SSD 。在缓存填满之前(为了腾出空间而驱逐的第一个块),写入 L2ARC 的限制为写入限制和增加限制的总和,之后限制为写入限制。一对 sysctl 值控制这些速率限制。 vfs.zfs.l2arc_write_max 控制每秒写入缓存的字节数,而 vfs.zfs.l2arc_write_boost在“Turbo Warmup Phase(写入增强)”期间增加了这个限制。

ZIL

ZIL 通过使用比主存储池中使用的存储设备(如 SSD)更快的设备来加速同步事务。当应用程序请求同步写入(保证数据存储到磁盘而不仅仅是缓存以供以后写入时),将数据写入更快的 ZIL 存储,然后稍后将其刷新到常规磁盘上,大大降低了延迟并提高了性能。像数据库这样的同步工作负载将从 ZIL 中受益。而常规的异步写入(如复制文件)则根本不会使用 ZIL。

写时复制(COW)

与传统的文件系统不同,ZFS 在写入数据时不会直接覆盖旧数据,而是写入一个不同的块。完成写入后,元数据会更新以指向新的位置。当发生截断写入(系统崩溃或断电导致文件写入中断)时,文件的完整原始内容仍然可用,而 ZFS 会丢弃不完整的写入。这也意味着 ZFS 在意外关闭后不需要进行 fsck(8) 操作。

数据集(Dataset)

数据集 是 ZFS 文件系统、卷、快照或克隆的通用术语。每个数据集都有一个唯一的名称,格式为 poolname/path@snapshot。池的根也是一个数据集。子数据集具有层次结构的名称,类似于目录。例如,mypool/home,即 home 数据集,是 mypool 的子数据集,并从其继承属性。通过创建 mypool/home/user,可以进一步扩展此结构。这个孙子数据集将从父级和祖父级继承属性。在子数据集上设置属性以覆盖从父级和祖父级继承的默认值。可以将数据集及其子数据集的管理 委托 给其他用户。

文件系统

ZFS 数据集通常用作文件系统。与大多数其他文件系统一样,ZFS 文件系统会挂载到系统目录层次结构的某个位置,并包含具有权限、标志和其他元数据的文件和目录。

卷(Volume)

ZFS 还可以创建卷,它们会显示为磁盘设备。卷具有与数据集相似的许多功能,包括写时复制、快照、克隆和校验和。卷对于在 ZFS 上运行其他文件系统格式(如 UFS 虚拟化)或导出 iSCSI 扩展非常有用。

快照(Snapshot)

ZFS 的 写时复制 (COW)设计允许几乎瞬间创建具有任意名称的一致性快照。在对数据集进行快照或对包含所有子数据集的父数据集进行递归快照之后,新数据将进入新的块,但不会将旧块回收为可用空间。快照包含原始文件系统版本,活动文件系统包含自快照以来所做的任何更改,而不使用其他空间。写入活动文件系统的新数据使用新的块来存储这些数据。随着块在活动文件系统中不再使用,快照将增长,但仅在快照中使用。将这些快照以只读方式挂载可以恢复先前的文件版本。可以将活动文件系统回滚到特定快照的 回滚 是可能的,从而撤消在快照之后进行的任何更改。池中的每个块都有一个引用计数器,用于跟踪使用该块的快照、克隆、数据集或卷。随着文件和快照被删除,引用计数减少,当不再引用块时,回收可用空间。使用 保留 标记快照将导致任何试图销毁它的尝试返回 EBUSY 错误。每个快照可以具有唯一名称的保留。 释放 命令会移除保留,以便可以删除快照。快照、克隆和回滚适用于卷,但独立挂载不适用。

克隆(Clone)

克隆快照也是可能的。克隆是快照的可写版本,允许文件系统分叉为一个新的数据集。与快照一样,克隆最初不占用新的空间。当新数据写入克隆时,使用新的块,克隆的大小增长。当在克隆的文件系统或卷中覆盖块时,先前块的引用计数减少。删除克隆所依赖的快照是不可能的,因为克隆依赖于它。快照是父级,克隆是子级。克隆可以被 提升(promoted),反转这种依赖关系,使克隆成为父级,先前的父级成为子级。此操作不需要新的空间。由于父级和子级使用的空间相互转换,可能会影响现有的配额和预留空间。

校验和(Checksum)

每个块也都有校验和。所使用的校验算法是每个数据集的属性,参见 set 。在读取时,每个块的校验和会被透明地验证,这使得 ZFS 能够检测到静默损坏。如果读取的数据与预期的校验和不匹配,ZFS 将尝试从任何可用的冗余中恢复数据,例如镜像或 RAID-Z。可以使用 scrub 触发对所有校验和的验证。校验算法包括:

* fletcher2 * fletcher4 * sha256 fletcher 算法更快,但 sha256 是一种强大的加密哈希算法,具有更低的碰撞几率,但性能稍有损失。可以禁用校验和,但强烈不建议这样做。

压缩(Compression)

每个数据集都有一个压缩属性,默认为关闭状态。将此属性设置为可用的压缩算法。这将导致对写入数据集的所有新数据进行压缩。除了减少所使用的空间外,读写吞吐量通常也会增加,因为需要读取或写入的块较少。

* LZ4 - 在 ZFS 池版本 5000(feature flags)中添加,LZ4 现在是推荐的压缩算法。当处理可压缩数据时,LZ4 的工作速度比 LZJB 快约 50 %,处理不可压缩数据时,速度快三倍以上。LZ4 的解压速度也比 LZJB 快约 80 %。在现代 CPU 上,LZ4 通常可以以超过 500 MB/s 的速度进行压缩,并以超过 1.5 GB/s 的速度进行解压缩(每个单独的 CPU 核心)。

* LZJB - 默认的压缩算法。由 Jeff Bonwick( ZFS 的原始创建者之一)创建。与 GZIP 相比,LZJB 提供了较好的压缩效果,并且 CPU 开销较小。在将来,默认的压缩算法将更改为 LZ4 。

* GZIP - ZFS 中提供的一种流压缩算法。使用 GZIP 的主要优势之一是其可配置的压缩级别。在设置 compress 属性时,管理员可以选择压缩级别,从最低级别的 gzip1 到最高级别的 gzip9。这使管理员可以控制以多少 CPU 时间来换取节省的磁盘空间。

* ZLE - 零长度编码是一种特殊的压缩算法,仅压缩连续的零。当数据集包含大块的零时,这种压缩算法非常有用。

副本(Copies)

copies 属性设置为大于 1 的值时,ZFS 会在 文件系统 中维护每个块的副本。在重要的数据集上设置此属性可以提供额外的冗余,以便从中恢复不匹配其校验和的块。在没有冗余的存储池中,副本功能是唯一的冗余形式。副本功能可以从单个坏扇区或其他形式的轻微损坏中恢复,但它不能保护存储池免受整个磁盘的丢失。

去重(Deduplication)

在写入数据时,校验和可以检测重复的数据块。通过去重,现有相同块的引用计数增加,节省存储空间。ZFS 在内存中保留一个去重表(DDT)来检测重复的数据块。该表包含一系列唯一的校验和、这些块的位置和引用计数。在写入新数据时,ZFS 计算校验和并将其与列表进行比较。当找到匹配项时,它使用现有的数据块。使用 SHA256 校验和算法进行去重提供了安全的加密哈希。去重是可调整的。如果 dedup 设置为 on,那么匹配的校验和意味着数据是相同的。将 dedup 设置为 verify,ZFS 对数据执行逐字节的检查,确保它们实际上是相同的。如果数据不相同,ZFS 将记录哈希冲突并将这两个块分别存储。由于 DDT 必须存储每个唯一块的哈希,它会消耗大量的内存。一个经验法则是每 1TB 去重数据需要 5-6GB 的 RAM。在无法实际拥有足够的 RAM 来将整个 DDT 保留在内存中的情况下,性能将大大降低,因为 DDT 必须在写入每个新块之前从磁盘读取。去重可以使用 L2ARC 来存储 DDT ,提供了快速系统内存和较慢磁盘之间的折中方案。考虑使用压缩代替,压缩通常可以提供几乎相同的空间节省效果,而不需要增加内存使用量。

Scrub

fsck(8) 类似的一致性检查不同,ZFS 使用 scrub 命令。scrub 命令会读取存储在池中的所有数据块,并将它们的校验和与元数据中存储的已知良好校验和进行验证。定期检查池中存储的所有数据可以确保在需要之前恢复任何损坏的块。在非正常关闭后不需要进行 scrub ,但良好的做法是至少每三个月进行一次。 ZFS 在正常使用过程中会验证每个块的校验和,但 scrub 会确保对即使很少使用的块进行静默损坏检查。ZFS 在归档存储情况下提高了数据安全性。通过调整 scrub 的相对优先级,可以使用 vfs.zfs.scrub_delay 来防止 scrub 影响池上其他工作负载的性能。

数据集配额(Dataset Quota)

ZFS 提供了快速准确的数据集、用户和组空间账户,以及配额和空间预留。这使得管理员可以对空间分配进行精细控制,并允许为关键文件系统预留空间。

ZFS 支持不同类型的配额:数据集配额、引用配额 (refquota)用户配额组配额

配额限制了数据集及其后代的总大小,包括数据集的快照、子数据集和这些数据集的快照。 + [NOTE] ==== 卷不支持配额,因为 volsize 属性充当隐式配额。 ====

引用配额(Reference Quota)

引用配额限制了数据集可以消耗的空间量,通过强制执行一个硬限制。这个硬限制包括数据集本身引用的空间,但不包括后代使用的空间,比如文件系统或快照。

用户配额(User Quota)

用户配额对于限制指定用户使用的空间量非常有用。

组配额(Group Quota)

群组配额限制了指定群组可以使用的空间量。

数据集预留空间(Dataset Reservation)

reservation 属性使得可以为特定数据集及其子数据集保证一定的空间量。这意味着在 storage/home/bob 上设置 10 GB 的预留空间,可以防止其他数据集使用完所有的空闲空间,至少为该数据集保留 10 GB 的空间。与常规的 引用预留 不同,快照和子数据集使用的空间不计入预留空间。例如,如果对 storage/home/bob 进行快照,除了 refreservation 的空间量之外,必须存在足够的磁盘空间才能成功进行操作。主数据集的子数据集不计入 refreservation 的空间量,因此不会侵占设置的空间。

任何类型的预留空间在以下情况下非常有用:在新系统中规划和测试磁盘空间分配的适用性,或确保文件系统上有足够的空间用于音频日志或系统恢复程序和文件。

引用预留(Reference Reservation)

refreservation 属性使得可以为特定数据集的使用保证一定的空间,但不包括其子数据集。这意味着在 storage/home/bob 上设置 10GB 的保留空间,如果另一个数据集尝试使用空闲空间,至少要保留 10GB 的空间给该数据集。与常规的 预留空间 不同,快照和子数据集使用的空间不计入预留空间。例如,如果对 storage/home/bob 进行快照,除了 refreservation 的空间之外,必须存在足够的磁盘空间才能成功进行操作。主数据集的子数据集不计入 refreservation 的空间,因此不会侵占设置的空间。

重建(Resilver)

当替换一个故障的磁盘时, ZFS 必须用丢失的数据填充新的磁盘。重建 是使用分布在剩余驱动器上的奇偶校验信息来计算并将丢失的数据写入新驱动器的过程。

Online

处于 在线 状态的池或 vdev 具有其成员设备连接并完全运行。处于 在线 状态的个别设备正在正常运行。

离线(Offline)

如果存在足够的冗余以避免使存储池或虚拟设备进入 故障 状态,管理员会将单个设备设置为 离线 状态。管理员可以选择将磁盘设置为离线状态,以便准备更换它,或者为了更容易识别。

降级(Degraded)

处于 降级 状态的池或 vdev 有一个或多个磁盘消失或失败。池仍然可用,但如果其他设备失败,池可能变得无法恢复。重新连接丢失的设备或替换失败的磁盘将在重新连接或新设备完成 重建 过程后将池恢复到 在线 状态。

故障(Faulted)

处于 故障 状态的池或 vdev 不再可操作。无法访问数据。当缺失或故障设备的数量超过 vdev 中的冗余级别时,池或 vdev 进入 故障 状态。如果重新连接缺失的设备,池将返回到 在线 状态。冗余不足以弥补故障磁盘数量会导致池内容丢失,并需要从备份中恢复。


上次修改时间: September 18, 2024 by fiercex