第二十二章 Z文件系统(ZFS)

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

最初由Sun™开发,正在进行的开源ZFS开发已转移到OpenZFS项目。

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

【22.8. ZFS功能和术语】中提供了功能和术语的完整列表。

第二十二章 Z文件系统(ZFS)22.1. 是什么让ZFS与众不同22.2. 快速入门指南22.2.1. 单磁盘池22.2.2. RAID-Z22.2.3. 恢复RAID-Z22.2.4. 数据验证22.3. zpool 管理22.3.1. 创建和销毁存储池22.3.2. 添加和删除设备22.3.3. 检查池的状态22.3.4. 清除错误22.3.5. 更换功能正常的设备22.3.6. 处理故障设备22.3.7. 擦洗池22.3.8. 自愈22.3.9. 扩充池22.3.10. 导入和导出池22.3.11. 升级存储池22.3.12. 显示记录的池历史记录22.3.13. 性能监控22.3.14. 拆分存储池22.4. zfs 管理22.4.1. 创建和销毁数据集22.4.2. 创建和销毁卷22.4.3. 重命名数据集22.4.4. 设置数据集属性22.4.4.1. 获取和设置共享属性22.4.5. 管理快照22.4.5.1. 创建快照22.4.5.2. 比较快照22.4.5.3. 快照回滚22.4.5.4. 从快照还原单个文件22.4.6. 管理克隆22.4.7. 复制22.4.7.1. 增量备份22.4.7.2. 通过SSH发送加密备份22.4.8. 数据集、用户和组配额22.4.9. 预订22.4.10. 压缩22.4.11. Z标准压缩22.4.12. 去重22.4.13. ZFS和Jails22.5. 委托管理22.5.1. 委托数据集创建22.5.2. 委派权限委派22.6. 高级主题22.6.1. 调谐22.6.2. i386上的ZFS22.6.2.1. 内存22.6.2.2. 内核配置22.6.2.3. 装载机可调22.7. 更多资源22.8. ZFS特征和术语

22.1. 是什么让ZFS与众不同

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

22.2. 快速入门指南

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

然后启动服务:

本节中的示例假设有三个SCSI磁盘,设备名为da0、da1和da2。SATA硬件的用户应该使用ada设备名称。

22.2.1. 单磁盘池

要使用单个磁盘设备创建简单的非冗余池,请执行以下操作:

要查看新池,请查看 df 的输出:

此输出显示了 example 池的创建和装载,现在可以作为文件系统访问。创建供用户浏览的文件:

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

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

使用以下命令禁用压缩:

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

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

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

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

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

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

要销毁文件系统,然后销毁不再需要的池,请执行以下操作:

22.2.2. RAID-Z

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

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

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

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

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

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

创建文件系统快照以稍后回滚:

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

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

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

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

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

运行 df 并挂载以确认系统现在将文件系统视为真实的 /home

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

22.2.3. 恢复RAID-Z

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

如果出现问题,可能是磁盘处于Offline(脱机)状态,池状态将如下:

“OFFLINE”显示管理员使用以下命令使da1脱机:

现在关闭计算机电源并更换da1。打开计算机电源并将da1返回到池中:

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

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

22.2.4. 数据验证

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

通过以下方式验证数据校验和(称为scrubbing,清理)可确保 stroage 池的完整性:

擦除的持续时间取决于存储的数据量。更大量的数据将需要更长的时间来验证。由于清理是I/O密集型的,ZFS允许一次运行一次清理。清理完成后,使用 zpool status 查看状态:

显示上次清理的完成日期有助于决定何时开始另一次清理。常规清理有助于保护数据免受无声损坏,并确保池的完整性。

有关其他zfs选项,请参阅 zfs(8)zpool(8)

22.3. zpool 管理

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

22.3.1. 创建和销毁存储池

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

创建一个简单的镜像池:

要使用单个命令创建多个vdev,请指定由vdev type关键字分隔的磁盘组,在本例中为 mirror

池也可以使用分区而不是整个磁盘。将ZFS放在单独的分区中,允许同一磁盘具有其他分区用于其他目的。特别是,它允许添加带有引导所需的引导代码和文件系统的分区。这允许从也是池成员的磁盘启动。当使用分区而不是整个磁盘时,ZFS不会对FreeBSD造成性能损失。使用分区还允许管理员使用低于满容量的磁盘来进行资源调配。如果未来与原始磁盘标称大小相同的替换磁盘实际上容量稍小,则使用替换磁盘时,较小的分区仍将适用。

使用分区创建RAID-Z2池:

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

22.3.2. 添加和删除设备

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

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

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

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

ZFS在每个vdev上条带化数据。例如,对于两个镜像vdev,这实际上是一个RAID 10,它在两组镜像上进行条带写入。ZFS分配空间,使每个vdev同时达到100%满。具有不同可用空间量的vdev会降低性能,因为更多的数据写入到不太满的vdev。

将新设备连接到启动池时,请记住更新启动代码。

将第二个镜像组(ada2p3和ada3p3)连接到现有镜像:

从池中删除vdev是不可能的,如果有足够的剩余冗余,则从镜像中删除磁盘是独占的。如果镜像组中仍有一个磁盘,则该组将不再是镜像,而变成条带,如果剩余磁盘发生故障,整个池都将面临风险。

从三向镜像组中卸下磁盘:

22.3.3. 检查池的状态

池状态很重要。如果驱动器脱机或ZFS检测到读取、写入或校验和错误,相应的错误计数会增加。 status 输出显示池中每个设备的配置和状态以及整个池的状态。还显示了要采取的行动和上次擦洗的详细信息。

22.3.4. 清除错误

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

22.3.5. 更换功能正常的设备

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

更换池中正常工作的设备:

22.3.6. 处理故障设备

当池中的磁盘发生故障时,该磁盘所属的vdev将进入 degraded 状态。数据仍然可用,但性能降低,因为ZFS从可用冗余中计算缺失的数据。要将vdev恢复到完全功能状态,请更换发生故障的物理设备。然后指示ZFS开始 resilver 操作。ZFS根据可用冗余重新计算故障设备上的数据,并将其写入替换设备。完成后,vdev将返回 online 状态。

如果vdev没有任何冗余,或者如果设备发生故障并且没有足够的冗余来补偿,则池将进入 faulted 状态。除非有足够的设备可以重新连接,否则池将无法运行,需要从备份中恢复数据。

更换故障磁盘时,故障磁盘的名称将更改为新磁盘的GUID。如果替换设备具有相同的设备名称,则不需要为 zpool replace 设置新的设备名称参数。

使用 zpool replace 替换故障磁盘:

22.3.7. 擦洗池

定期 scrub (擦洗)池,最好每月至少一次。scrub 操作是磁盘密集型的,会降低运行时的性能。在安排 scrub 时避免高需求时段,或使用 vfs.zfs.crub_delay 调整 scrub 的相对优先级,以防止其减缓其他工作负载。

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

22.3.8. 自愈

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

下一个示例通过创建镜像磁盘池 /dev/ada0/dev/ada1 来显示这种自愈行为。

使用自愈功能将一些重要数据复制到池中以防止数据错误,并创建池的校验和以供以后比较。

通过将随机数据写入镜像中某个磁盘的开头来模拟数据损坏。为了防止ZFS在检测到数据时修复数据,请在损坏之前导出池,然后再次导入。

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

ZFS检测到错误,并使用未受影响的ada0镜像磁盘中存在的冗余进行处理。与原始校验和的比较将揭示池是否再次一致。

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

擦除操作从ada0读取数据,并在ada1上用错误的校验和重写任何数据,如 zpool status 的( repairing )输出所示。操作完成后,池状态更改为:

在清理操作完成并将所有数据从ada0同步到ada1后,通过运行 zpool clear 从池状态中清除错误消息。

池现在已恢复到完全工作状态,所有错误计数现在为零。

22.3.9. 扩充池

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

在每台设备上使用 zpool online -e 开始扩展。扩展所有设备后,多余的空间可供池使用。

22.3.10. 导入和导出池

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

导出未使用的池:

导入池会自动装载数据集。如果这是不希望的行为,请使用 zpool import -N 来防止它。zpool import -o 为此特定导入设置临时属性。zpool import altroot= 允许导入具有基本挂载点而不是文件系统根的池。如果该池上次是在其他系统上使用的,并且没有正确导出,请使用 zpool import -fzpool import -a 强制导入所有似乎没有被其他系统使用的池。

列出所有可导入的池:

使用备用根目录导入池:

22.3.11. 升级存储池

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

升级v28池以支持 Feature Flags(功能标志):

zpool uograde 完成之前,ZFS的新功能将不可用。使用 zpool upgrade -v 查看升级提供了哪些新功能,以及已经支持哪些功能。

升级池以支持新功能标志:

22.3.12. 显示记录的池历史记录

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

输出显示 zpoolzfs 命令以某种方式更改池以及时间戳。不包括 zfs list 等命令。当未指定池名时,ZFS会显示所有池的历史记录。

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

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

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

将这两个选项组合到 zpool history 记录中,为任何给定的池提供尽可能详细的信息。池历史记录在跟踪执行的操作或需要更详细的调试输出时提供了有价值的信息。

22.3.13. 性能监控

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

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

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

22.3.14. 拆分存储池

ZFS可以将由一个或多个镜像vdev组成的池拆分为两个池。除非另有说明,否则ZFS会分离每个镜像的最后一个成员,并创建一个包含相同数据的新池。一定要先用 -n 对操作进行模拟。这显示了所请求操作的详细信息,而没有实际执行它。这有助于确认操作将按照用户的意图进行。

22.4. zfs 管理

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

22.4.1. 创建和销毁数据集

与传统的磁盘和卷管理器不同,ZFS中的空间不是预先分配的。对于传统的文件系统,在分区和分配空间后,如果不添加新磁盘,就无法添加新的文件系统。使用ZFS,可以随时创建新的文件系统。每个数据集 都有属性,包括压缩(compression)、重复数据删除(deduplication)、缓存(caching)和配额(quota)等功能,以及只读、区分大小写、网络文件共享和装载点等其他有用属性。数据集可以相互嵌套,子数据集将继承其祖先的属性。委托(delegate)、复制(replicate)、快照(snapshot)、jail允许将每个数据集作为一个单元进行管理和销毁。为每种不同类型或一组文件创建单独的数据集具有优势。拥有大量数据集的缺点是,像 zfs list 这样的一些命令会变慢,装载数百甚至数千个数据集会减缓FreeBSD的启动过程。

创建一个新的数据集并对其启用LZ4压缩:

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

销毁创建的数据集:

在ZFS的现代版本中, zfs destroy 是异步的,可用空间可能需要几分钟才能出现在池中。使用 zpool get freesing poolname 查看 freesing 属性,该属性显示哪些数据集在后台释放了块。如果有子数据集,如快照或其他数据集,则不可能销毁父数据集。要销毁数据集及其子数据集,请使用 -r 递归销毁数据集和子数据集。使用 -n -v 列出此操作销毁的数据集和快照,而不会实际销毁任何内容。还显示了通过销毁快照回收的空间。

22.4.2. 创建和销毁卷

卷(volume)是一种特殊的数据集类型。与其作为文件系统挂载,不如将其作为块设备暴露在 /dev/zvol/poolname/dataset 下。这允许将卷用于其他文件系统,备份虚拟机的磁盘,或使用iSCSI或HAST等协议将其提供给其他网络主机。

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

销毁卷与销毁常规文件系统数据集非常相似。该操作几乎是即时的,但可能需要几分钟才能在后台回收可用空间。

22.4.3. 重命名数据集

要更改数据集的名称,请使用 zfs rename 。要更改数据集的父级,也可以使用此命令。将数据集重命名为具有不同的父数据集将更改从父数据集继承的那些属性的值。重命名数据集会卸载数据集,然后将其重新装载到新位置(从新的父数据集继承)。要防止这种行为,请使用 -u

重命名数据集并将其移动到其他父数据集下:

重命名快照使用相同的命令。由于快照的性质,重命名无法更改其父数据集。要重命名递归快照,请指定 -r ;这也将重命名子数据集中同名的所有快照。

22.4.4. 设置数据集属性

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

要删除自定义属性,请使用带有-r的zfs inherite。如果自定义属性未在任何父数据集中定义,则此选项会将其删除(但池的历史记录仍会记录更改)。

22.4.4.1. 获取和设置共享属性

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

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

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

22.4.5. 管理快照

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

ZFS中的快照提供了其他具有快照功能的文件系统所缺乏的各种功能。快照使用的一个典型示例是在执行软件安装或系统升级等有风险的操作时,作为备份文件系统当前状态的快速方法。如果操作失败,回滚到快照会使系统返回到创建快照时的相同状态。如果升级成功,请删除快照以释放空间。如果没有快照,失败的升级通常需要恢复备份,这是乏味、耗时的,并且可能需要停机,在此期间系统无法使用。即使在系统正常运行且停机时间很少或没有停机的情况下,回滚到快照也很快。考虑到从备份中复制数据所需的时间,使用数TB的存储系统可以节省大量时间。快照不能替代池的完整备份,但提供了一种在特定时间存储数据集副本的快速简便的方法。

22.4.5.1. 创建快照

要创建快照,请使用 zfs snapshot dataset@snapshotname 。添加 -r 会递归创建一个快照,在所有子数据集上使用相同的名称(@及后面的snapshotname)。

创建整个池的递归快照:

快照不会通过正常的 zfs list 操作显示。要列出快照,请将 -t snapshot 附加到 zfs list-t all 同时显示文件系统和快照。

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

同时显示数据集和快照可以揭示快照是如何以COW方式工作的。它们保存所做的更改(delta,增量),而不是重新保存完整的文件系统内容。这意味着快照在进行更改时占用的空间很小。通过将文件复制到数据集,然后创建第二个快照,可以更深入地观察空间使用情况:

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

22.4.5.2. 比较快照

ZFS提供了一个内置命令来比较两个快照之间的内容差异。当用户想要查看文件系统随时间的变化时,这对于随时间拍摄的许多快照很有帮助。例如, zfs diff 允许用户查找仍然包含意外删除的文件的最新快照。对上一节中创建的两个快照执行此操作会产生以下输出:

该命令列出了指定快照(在本例中为 mypool/var/tmp@my_recursive_snapshot )和实时文件系统之间的更改。第一列显示更改类型:

类型说明
+添加路径或文件。
-删除路径或文件。
M修改路径或文件。
R重命名路径或文件

将输出与表进行比较,很明显ZFS在创建快照 mypool/var/tmp@my_recursive_snapshot 后添加了passwd。这也导致了对 /var/tmp上挂载的父目录的修改。

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

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

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

22.4.5.3. 快照回滚

当至少有一个快照可用时,可以随时回滚到该快照。最常见的情况是,数据集的当前状态不再有效,或者更喜欢旧版本。本地开发测试出错、糟糕的系统更新阻碍了系统功能,或者需要恢复已删除的文件或目录等情况都很常见。要回滚快照,请使用 zfs rollback snapshotname 。如果存在很多变化,操作将需要很长时间。在此期间,数据集始终保持一致的状态,就像符合ACID原则的数据库正在执行回滚一样。当数据集处于活动状态且无需停机即可访问时,就会发生这种情况。快照回滚后,数据集的状态与最初拍摄快照时的状态相同。回滚到快照会丢弃该数据集中不属于快照的所有其他数据。当以后需要一些数据时,在回滚到上一个状态之前拍摄数据集当前状态的快照是一个好主意。这样,用户可以在快照之间来回滚动,而不会丢失仍然有价值的数据。

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

此时,用户注意到额外文件被删除,并希望将其恢复。ZFS提供了一种简单的方法,可以在定期执行重要数据的快照时使用回滚将其恢复。要获取文件并从上一个快照重新开始,请发出以下命令:

回滚操作将数据集还原到上一个快照的状态。也可以回滚到更早拍摄的快照和之后拍摄的其他快照。尝试执行此操作时,ZFS将发出以下警告:

此警告意味着数据集的当前状态和用户想要回滚到的快照之间存在快照。要完成回滚,请删除这些快照。ZFS无法跟踪数据集不同状态之间的所有变化,因为快照是只读的。ZFS不会删除受影响的快照,除非用户指定 -r 以确认这是所需的操作。如果这是意图,并且了解丢失所有中间快照的后果,请发出以下命令:

zfs list -t snapshot 的输出确认了由于 zfs rollback -r 而删除了中间快照。

22.4.5.4. 从快照还原单个文件

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

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

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

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

快照根据自快照以来父文件系统的更改程度来消耗空间。快照的 written 属性跟踪快照使用的空间。

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

22.4.6. 管理克隆

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

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

克隆的一个典型用途是对特定的数据集进行实验,同时保留快照以防出现问题。由于快照无法更改,请创建快照的读/写克隆。在克隆中达到预期结果后,将克隆升级为数据集并删除旧文件系统。删除父数据集并不是绝对必要的,因为克隆和数据集可以共存而不会出现问题。

创建克隆使其成为拍摄快照时数据集所处状态的精确副本。现在可以独立于其原始数据集更改克隆。两者之间的联系就是快照。ZFS在属性 origin 中记录此连接。使用 zfs promote 升级克隆会使克隆成为一个独立的数据集。这将删除 origin 属性的值,并将新独立的数据集与快照断开连接。这个例子说明了这一点:

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

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

22.4.7. 复制

将单个池中的数据保存在一个位置会使其面临盗窃、自然或人为灾害等风险。对整个池进行定期备份至关重要。ZFS提供了一个内置的序列化功能,可以将数据的流表示发送到标准输出。使用此功能,可以将此数据存储在连接到本地系统的另一个池中,就像通过网络将其发送到另一个系统一样。快照是此复制的基础(请参阅【22.4.5. ZFS快照】一节)。用于复制数据的命令是 zfs sendzfs receive

这些示例显示了使用这两个池的ZFS复制:

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

现在快照已经存在,使用 zfs send 创建一个表示快照内容的流。将此流存储为文件或在另一个池中接收它。将流写入标准输出,但重定向到文件或管道或出现错误:

要使用 zfs send 备份数据集,请重定向到位于装载的备份池上的文件。确保池有足够的可用空间来容纳发送的快照的大小,这意味着快照中包含的数据,而不是对上一个快照的更改。

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

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

22.4.7.1. 增量备份

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

创建第二个名为 replica2 的快照。第二个快照包含从现在到上一个快照 replica1 对文件系统所做的更改。使用 zfs send -i 并指示快照对会生成一个包含更改数据的增量副本流。如果接收端已存在初始快照,则此操作成功。

增量流复制了更改的数据,而不是整个 replica1 。单独发送差异所需的传输时间要短得多,并且通过每次不复制整个池来节省磁盘空间。当在慢速网络上复制或对每个传输字节收费时,这很有用。

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

22.4.7.2. 通过SSH发送加密备份

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

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

按如下方式更改配置:

在接收系统上:

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

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

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

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

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

要为存储 /home/bob 强制10 GB的数据集配额:

要为存储 /home/bob 强制10 GB的参考配额:

要删除存储 /home/bob 的10GB配额:

一般格式为 userquota@user=size ,用户名必须采用以下格式之一:

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

要删除任何配额,请执行以下操作:

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

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

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

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

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

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

22.4.9. 预订

Reservations —— 预订保证了数据集上始终可用的空间量。保留的空间将不可用于任何其他数据集。此有用功能可确保为重要数据集或日志文件提供可用空间。

reservation 属性的一般格式是 reservation=size ,因此要在 storage/home/bob 上设置10GB的预留,请使用:

要清除任何预订:

同样的原则也适用于设置参考预留的 refreservation 属性,其通用格式为 refreservation=size

此命令显示 storage/home/bob 上存在的任何保留或重新保留:

22.4.10. 压缩

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

ZFS提供了不同的压缩算法,每种算法都有不同的权衡。ZFS v5000中引入了LZ4压缩,可以压缩整个池,而不会像其他算法那样牺牲大量性能。LZ4的最大优点是早期中止(early abort)功能。如果LZ4在数据的头部部分没有实现至少12.5%的压缩,ZFS会在未压缩的情况下写入块,以避免浪费CPU周期来压缩已经压缩或不可压缩的数据。有关ZFS中可用的不同压缩算法的详细信息,请参阅术语部分中的compression条目。

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

数据集使用了449 GB的空间(已使用的属性)。如果没有压缩,它将占用496 GB的空间( logicalused 属性)。这导致了1.11:1的压缩比。

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

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

22.4.11. Z标准压缩

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的级别需要大量内存来压缩每个块,RAM小于16GB的系统不应使用它们。ZFS也使用了一系列Zstd_fast_级别,这些级别相应地更快,但支持较低的压缩比。ZFS支持 zstd-fast-1zstd-fast-10zstd-fass-20zstd-fast-100 ,增量为 10 ,以及 zstd-fast-500zstd-fast-1000 ,它们提供最小的压缩,但提供高性能。

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

22.4.12. 去重

启用后,重复数据删除使用每个块的校验和来检测重复块。当一个新块是现有块的副本时,ZFS会向现有数据写入一个新的引用,而不是整个副本块。如果数据包含大量重复文件或重复信息,则可以节省大量空间。警告:重复数据删除需要大量内存,启用压缩可以节省大部分空间,而不会产生额外成本。

要激活重复数据消除,请在目标池上设置重复数据消除属性:

重复数据消除仅影响写入池的新数据。仅激活此选项不会对已写入池的数据进行重复数据消除。具有新激活的重复数据删除属性的池看起来像这样:

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

要观察冗余数据的重复数据删除,请使用:

DEDUP 列显示系数为 3.00x 。检测和删除数据副本会占用三分之一的空间。节省空间的潜力是巨大的,但代价是有足够的内存来跟踪重复数据消除的块。

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

zdb -S 完成对池的分析后,它显示了激活重复数据删除将实现的空间缩减率。在这种情况下,1.16 是主要由压缩提供的较差的空间节省率。激活此池的重复数据删除功能不会节省任何空间,也不值得启用重复数据删除所需的内存量。使用公式 ratio=dedup * compress / copy ,系统管理员可以规划存储分配,决定工作负载是否包含足够的重复块来满足内存要求。如果数据具有合理的可压缩性,那么节省的空间可能会很好。好的做法是先启用压缩,因为压缩也可以大大提高性能。在节省可观且DDT有足够可用内存的情况下,启用重复数据删除。

22.4.13. ZFS和Jails

使用 zfs jail 和相应的 jailed 属性将zfs数据集委托给jail。zfs jail jailid 将数据集附加到指定的jail,zfs unjail 将其分离。要从监狱内控制数据集,请设置 jailed 属性。ZFS禁止在主机上挂载被监禁的数据集,因为它可能有会危及主机安全的挂载点。

22.5. 委托管理

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

22.5.1. 委托数据集创建

zfs allow someuser create mydataset 授予指定用户在所选父数据集下创建子数据集的权限。警告:创建新数据集需要挂载它。这需要将FreeBSD vfs.usermount sysctl(8) 设置为 1 ,以允许非root用户挂载文件系统。另一个旨在防止滥用的限制是:非root用户必须拥有装载文件系统的装载点。

22.5.2. 委派权限委派

zfs allow someuser allow mydataset 允许指定用户将他们对目标数据集或其子数据集的任何权限分配给其他用户。如果用户具有快照权限和允许权限,则该用户可以将快照权限授予其他用户。

22.6. 高级主题

22.6.1. 调谐

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

22.6.2. i386上的ZFS

ZFS提供的一些功能是内存密集型的,可能需要在RAM有限的系统上进行调优以提高效率。

22.6.2.1. 内存

作为较低的值,总系统内存应至少为1GB。推荐的RAM数量取决于池的大小以及ZFS使用的功能。一般经验法则是每1TB的存储需要1GB的RAM。如果使用重复数据消除功能,一般经验法则是每TB存储需要5 GB RAM进行重复数据消除。虽然一些用户使用RAM较少的ZFS,但负载过重的系统可能会因内存耗尽而死机。对于RAM要求低于推荐要求的系统,ZFS可能需要进一步调整。

22.6.2.2. 内核配置

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

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

22.6.2.3. 装载机可调

增加所有FreeBSD架构上的kmem地址空间。具有1GB物理内存的测试系统受益于将这些选项添加到 /boot/loader.conf ,然后重新启动:

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

22.7. 更多资源

OpenZFS FreeBSD Wiki - ZFS Tuning Calomel Blog - ZFS Raidz Performance, Capacity and Integrity

22.8. ZFS特征和术语

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