22.8.ZFS 特性和术语

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

存储 是 ZFS 的最基本的构成要素。池由一个或多个 vdev 组成,是存储数据的基础设备。然后,池被用来创建一个或多个文件系统(数据集)或块设备(卷)。这些数据集和卷共享池中的剩余可用空间。每个池由一个名称和一个 GUID 来唯一识别。池的 ZFS 版本号决定了可用的功能。
vdev 类型

一个池由一个或多个 vdev 组成,它们本身是一个单一的磁盘或一组磁盘,被转化为 RAID。当使用大量的 vdev 时,ZFS 将数据分散到各个 vdev 中,以提高性能并最大限度地提高可用空间。所有的 vdev 必须至少有 128MB 大小。

  • 磁盘——最基本的 vdev 类型是一个标准块设备。这可以是整个磁盘(例如 /dev/ada0/dev/da0)或一个分区(/dev/ada0p3)。在 FreeBSD 上,使用一个分区而不是整个磁盘不会有任何性能损失。这与 Solaris 文档中的建议不同。
    当心
    强烈不建议使用整个磁盘作为可启动池的一部分,因为这可能导致池无法启动。同样地,你也不应该使用整个磁盘作为镜像或 RAID-Z vdev 的一部分。在启动时可靠地确定一个未分区的磁盘的大小是不可能的,因为没有地方可以放入启动代码。
  • 文件 - 普通文件可以组成 ZFS 池,这对测试和实验很有用。在zpool create中使用文件的完整路径作为设备路径。
  • 镜像_当创建一个镜像时,指定镜像关键字,然后是镜像的成员设备的列表。镜像由两个或多个设备组成,将所有数据写入所有成员设备。一个镜像 vdev 将容纳与其最小的成员一样多的数据。一个镜像 vdev 可以承受除一个成员外的所有故障而不丢失任何数据。
    注意
    要在任何时候将一个普通的单磁盘 vdev 升级为镜像 vdev,请使用zpool attach
  • RAID-Z - ZFS 使用 RAID-Z,这是标准 RAID-5 的一个变种,它提供更好的奇偶校验分布,并消除 "RAID-5 write hole",即数据和奇偶校验信息在意外重新启动后变得不一致。ZFS 支持三种级别的 RAID-Z,提供不同级别的冗余,以换取不断减少的可用存储空间。ZFS 使用 RAID-Z1 到 RAID-Z3,基于阵列中的奇偶校验设备的数量和在磁盘池停止运行前可能发生故障的磁盘数量。
  • 在一个有四个磁盘的 RAID-Z1 配置中,每个磁盘都是 1TB,可用的存储空间是 3TB,在有一个故障磁盘的情况下,磁盘池仍然能够在降级模式下运行。如果在更换和恢复故障磁盘之前,另一个磁盘脱机将导致所有的池数据丢失。
  • 在有 8 个 1TB 磁盘的 RAID-Z3 配置中,该卷将提供 5TB 的可用空间,并且在有 3 个故障磁盘的情况下仍能运行。Sun™ 建议在一个 vdev 中不超过九个磁盘。如果更多的磁盘组成配置,建议将它们分成独立的 vdev,并在它们之间进行磁盘池数据的剥离。
  • 一个由两个 RAID-Z2 vdev 组成的配置,每个 vdev 有 8 个磁盘,将创建类似 RAID-60 的阵列。一个 RAID-Z 组的存储容量大约是最小的磁盘的大小乘以非奇偶性磁盘的数量。在 RAID-Z1 中 4 个 1TB 的磁盘有大约 3TB 的有效大小,而在 RAID-Z3 中 8 个 1TB 的磁盘阵列将产生 5TB 的可用空间。
  • 备用 - ZFS 有一个特殊的伪 vdev 类型,用于跟踪可用的热备用。注意已安装的热备设备不会自动部署;使用zfs replace 手动配置它们来替换故障设备。
  • 日志 - ZFS 日志设备,也被称为 ZFS 意图日志(ZIL),将意图日志从常规池设备转移到一个专用设备,通常是一个 SSD。拥有一个专用的日志设备可以提高像数据库这样的有大量同步写入的应用程序的性能。日志设备的镜像是可能的,但不支持 RAID-Z。如果使用大量的日志设备,写操作将在它们之间进行负载平衡。
  • 缓存 - 在池里添加一个缓存 vdev 将把缓存的存储添加到 L2ARC 中。镜像高速缓存设备是不可能的。因为缓存设备只存储现有数据的新副本,所以没有数据丢失的风险。
事务组 (TXG)

事务组是 ZFS 将块变化分组并写入池中的方式。事务组是 ZFS 用来确保一致性的原子单元。ZFS 给每个事务组分配一个唯一的 64 位连续标识符。一次最多可以有三个活动的事务组,在这三种状态中各有一个。
Open——一个新的事务组在开放状态下开始,接受新的写入。总是有一个交易组处于开放状态,但如果交易组达到一个限制,它可能会拒绝新的写入。一旦开放的事务组达到限制,或达到 vfs.zfs.txg.timeout,事务组将进入下一个状态。Quiescing——一个短暂的状态,允许任何未决的操作完成,而不阻止创建一个新的开放事务组。一旦该组中的所有事务都完成,该事务组将进入最后一个状态。同步——将交易组中的所有数据写入稳定的存储。这个过程将反过来改变其他数据,如元数据和空间图,ZFS 也将把这些数据写到稳定存储中。同步的过程包括几个环节。在第一次和最大的一次,所有改变的数据块;接下来是元数据,这可能需要几次才能完成。由于为数据块分配空间会产生新的元数据,同步状态不能完成,直到一个不使用任何新空间的过程完成。同步状态也是同步任务完成的地方。同步任务是一些管理操作,如创建或销毁快照和数据集,完成 uberblock 的变化。一旦同步状态完成,处于静止状态的事务组就会推进到同步状态。所有的管理功能,如 快照 写入作为事务组的一部分。ZFS 将创建的同步任务添加到开放的事务组中,该事务组尽可能快地推进到同步状态,以减少管理命令的延迟。

自适应替换缓存(ARC)ZFS 使用自适应替换缓存(ARC),而不是更传统的最近使用最少的缓存(LRU)。LRU 缓存是一个简单的缓存项目列表,按照对象最近被使用的时间排序,将新项目添加到列表的头部。当缓存已满时,从列表的尾部驱逐项目,为更多的活动对象腾出空间。一个 ARC 由四个列表组成;最近使用(MRU)和最常使用(MFU)的对象,加上每个对象的幽灵列表。这些幽灵列表跟踪被驱逐的对象,以防止将它们重新添加到缓存中。这样就避免那些有偶尔使用历史的对象,从而提高了缓存的命中率。同时使用 MRU 和 MFU 的另一个好处是,扫描整个文件系统会驱逐 MRU 或 LRU 缓存中的所有数据,以支持这些新访问的内容。在 ZFS 中,也有一个 MFU 来跟踪最经常使用的对象,而最经常访问的块的缓存仍然存在。
L2ARCL2ARC 是 ZFS 缓存系统的第二层。RAM 存储主 ARC。由于可用的 RAM 数量通常是有限的,ZFS 也可以使用 cache vdevsopen in new window。固态硬盘(SSD)经常被用作这些缓存设备,因为与传统的旋转磁盘相比,它们的速度更高,延迟更低。L2ARC 完全是可有可无的,但是拥有它可以提高 SSD 上缓存文件的读取速度,而不必从普通磁盘上读取。L2ARC 也可以加快 重复数据删除open in new window 的速度,因为不适合在 RAM 中,但适合在 L2ARC 中的重复数据删除表(DDT)将比必须从磁盘读取的 DDT 快得多。对添加到缓存设备的数据速率的限制可以防止因额外的写入而过早地磨损 SSD。在缓存满之前(第一个块被驱逐以腾出空间),对 L2ARC 的写入限制在写入限制和提升限制之和,之后限制在写入限制。一对 sysctl(8)open in new window 的值控制着这些速率限制。vfs.zfs.l2arc_write_maxopen in new window 控制每秒写入缓存的字节数,而 vfs.zfs.l2arc_write_boostopen in new window 在 "Turbo Warmup Phase"(Write Boost)增加这个限制。
ZILZIL 通过使用比主存储池中使用的存储设备(如 SSD)更快的存储设备,来加速同步。当一个应用程序要求同步写入时(保证数据被存储到磁盘,而不仅仅是为以后的写入进行缓存),将数据写入更快的 ZIL 存储,然后再将其冲出到常规磁盘上,这就大大降低延迟,提高性能。像数据库这样的同步工作负载将单独从 ZIL 中获益。常规的异步写操作,如复制文件,则根本不会用到 ZIL。
写入时复制与传统的文件系统不同,ZFS 写一个不同的块,而不是覆盖原地的旧数据。当完成这个写入时,元数据会更新以指向新的位置。当发生 shorn write(在写文件的过程中系统崩溃或断电)时,文件的整个原始内容仍然可用,ZFS 会丢弃不完整的写操作。这也意味着 ZFS 在意外关机后不需要进行 fsck(8)open in new window
数据集数据集是 ZFS 文件系统、卷、快照或 Clone 的通用术语。每个数据集都有一个唯一的名字,格式为 poolname/path@snapshot。池的根也是一个数据集。子数据集有像目录一样的分层名称。例如,mypool/home,home 数据集,是 mypool 的一个子集,从它那里继承属性。通过创建 mypool/home/user 进一步扩展。这个孙子数据集将继承父母和祖父母的属性。在子集上设置属性以覆盖从父集和祖集上继承的默认值。数据集及其子集的管理可以被 委托open in new window
文件系统一个 ZFS 数据集最常被用作一个文件系统。像大多数其他文件系统一样,ZFS 文件系统挂载在系统目录层次中的某个地方,并包含自己的文件和目录,有权限、标志和其他元数据。
ZFS 也可以创建卷,它看起来像磁盘设备。卷有很多与数据集相同的功能,包括写时拷贝、快照、Clone 和校验。卷对于在 ZFS 之上运行其他文件系统格式非常有用,比如 UFS 虚拟化,或者导出 iSCSI 扩展。
快照ZFS 的 写入时复制open in new window(COW)设计允许几乎即时的、一致的、具有任意名称的快照。在对数据集进行快照后,或对包括所有子数据集的父数据集进行递归快照后,新数据会进入新的区块,但不会将旧的区块回收为自由空间。快照包含原始文件系统的版本,而实时文件系统包含自拍摄快照以来的任何变化,不使用其他空间。写入实时文件系统的新数据使用新的块来存储这些数据。当这些区块不再用于实时文件系统,而是单独用于快照时,快照将增长。将这些快照挂载为只读允许恢复以前的文件版本。将实时文件系统 回滚open in new window 到一个特定的快照是可能的,撤消在拍摄快照后发生的任何变化。池中的每个区块都有一个参考计数器,用于跟踪使用该区块的快照、Clone、数据集或卷。当文件和快照被删除时,参考计数就会减少,当不再参考一个区块时,就会回收自由空间。用 holdopen in new window 标记快照的结果是,任何试图销毁它的行为都会返回一个 EBUSY 错误。每个快照都可以有一个独特名称的 hold。releaseopen in new window 命令会移除 hold,这样快照就可以被删除。快照、Clone 和回滚可以在卷上进行,但独立挂载则不行。
CloneClone 一个快照也是可能的。Clone 是快照的一个可写版本,允许文件系统作为一个新的数据集分叉。与快照一样,Clone 最初不消耗新的空间。当写入 Clone 的新数据使用新的块时,Clone 的大小会增长。当 Clone 的文件系统或卷中的块被覆盖时,先前块上的参考计数会减少。移除作为 Clone 基础的快照是不可能的,因为 Clone 依赖于它。快照是父母,而 Clone 是孩子。Clone 可以被提升,扭转这种依赖关系,使 Clone 成为父,而先前的父成为子。这个操作不需要新的空间。由于父代和子代所使用的空间量是相反的,它可能会影响现有的配额和保留。
校验和每个区块也会被校验。使用的校验算法是每个数据集的属性,见 setopen in new window。每个块的校验和在读取时被透明地验证,允许 ZFS 检测无声的损坏。如果读取的数据与预期的校验和不一致,ZFS 将尝试从任何可用的冗余中恢复数据,如镜像或 RAID-Z。使用 scrubopen in new window 对所有校验和进行验证。校验和算法包括 fletcher2*fletcher4*sha256fletcher 算法更快,但是 sha256 是一个强大的加密散列,以牺牲一些性能为代价,碰撞的机会更少。停用校验和是可能的,但强烈不建议这样做。
压缩

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

  • LZ4——在 ZFS 池版本 5000 中添加(feature flags),LZ4 现在是推荐的压缩算法。当对可压缩数据进行操作时,LZ4 的工作速度比 LZJB 快 50% 左右,而在对不可压缩数据进行操作时,LZ4 的工作速度超过三倍。LZ4 的解压速度也比 LZJB 快大约 80%。在现代 CPU 上,LZ4 的压缩速度通常超过 500MB/s,解压速度超过 1.5GB/s(每个 CPU 核心)。
  • LZJB——默认的压缩算法。由 Jeff Bonwick(ZFS 的原始创建者之一)创建。与 GZIP 相比,LZJB 提供良好的压缩效果,而且 CPU 开销较少。在未来,默认的压缩算法将改变为 LZ4。
  • GZIP——ZFS 中可用的一种流行的流压缩算法。使用 GZIP 的主要优势之一是其可配置的压缩级别。当设置compress 属性时,管理员可以选择压缩级别,范围从最低压缩级别的 gzip1 到最高压缩级别的gzip9。这使管理员能够控制用多少 CPU 时间来换取节省的磁盘空间。
  • ZLE——零长度编码是一种特殊的压缩算法,可以单独压缩连续运行的零。当数据集包含大量的零块时,这种压缩算法很有用。
Copies当设置为一个大于 1 的值时,copies 属性指示 ZFS 维护[文件系统](https://docs.freebsd.org/en/books/handbook/zfs/#zfs-term-filesystem或open in new window 中每个块的副本。在重要的数据集上设置这个属性提供额外的冗余,可以从中恢复不符合其校验和的块。在没有冗余的池中,副本功能是唯一的冗余形式。复制功能可以从单个坏扇区或其他形式的轻微损坏中恢复,但它不能保护池免受整个磁盘的损失。
去重校验和使其有可能在写入数据时发现重复的块。通过重复数据删除,一个现有的、相同的块的参考计数增加,节省存储空间。ZFS 在内存中保留一个重复数据删除表(DDT)来检测重复的块。该表包含一个唯一的校验和的列表,这些块的位置,以及参考计数。当写入新数据时,ZFS 计算校验和并将其与列表进行比较。当找到一个匹配时,它使用现有的块。使用 SHA256 校验算法和重复数据删除提供一个安全的加密哈希。重复数据删除是可调整的。如果 dedup 设置为 on,那么一个匹配的校验和意味着数据是相同的。将 dedup 设置为 verify,ZFS 会对数据进行逐个字节的检查,确保它们实际上是相同的。如果数据不完全相同,ZFS 将注意到哈希碰撞,并分别存储这两个块。由于 DDT 必须存储每个独特区块的哈希值,它消耗大量的内存。一般的经验法则是每 1TB 的重复数据需要 5-6GB 的内存)。在不实际的情况下,要有足够的内存来保持整个 DDT 在内存中,性能将受到很大影响,因为 DDT 必须在写入每个新块之前从磁盘读取。重复数据删除可以使用 L2ARC 来存储 DDT,在快速系统内存和较慢的磁盘之间提供一个中间地带。考虑使用压缩来代替,这通常能提供几乎同样多的空间节省,而不需要增加内存。
清洗ZFS 有 scrub,而无需 fsck(8)open in new window 那样的一致性检查。scrub 读取存储在池中的所有数据块,并根据存储在元数据中的已知良好的校验和验证它们。对存储在池中的所有数据的定期检查确保在需要之前恢复任何损坏的块。在一次不正常的关机后无需清洗,但好的做法是至少每三个月清洗一次。ZFS 在正常使用期间会验证每个块的校验和,但是清洗可以确保检查即使是不经常使用的块是否有不易发现的损坏。ZFS 提高了存档存储情况下的数据安全性。。用 vfs.zfs.scrub_delay 调整清洗的相对优先级,以防止清洗降低池上其他工作负载的性能。
数据集配额

ZFS 提供快速和准确的数据集、用户和组空间核算,以及配额和空间保留。这使管理员能够精细地控制空间分配,并允许为关键文件系统保留空间。ZFS 支持不同类型的配额:数据集配额、参考 配额(refquota)用户配额组配额。配额限制一个数据集及其后代的总大小,包括数据集的快照、子数据集和这些数据集的快照。
ZFS 卷不支持配额,因为 volsize 属性作为一个隐含的配额。

引用配额引用配额通过强制执行一个硬限制来限制数据集可以消耗的空间量。这个硬性限制包括仅由数据集引用的空间,不包括由后代使用的空间,如文件系统或快照。
用户配额用户配额对于限制指定用户使用的空间数量很有用。
组配额组的配额限制一个指定的组所能消耗的空间量。
数据集保留

reservation 属性使得为一个特定的数据集及其后代保证一定的空间成为可能。这意味着在 storage/home/bob 上设置一个 10GB 的保留,可以防止其他数据集使用所有的自由空间,为这个数据集保留至少 10GB 的空间。与普通的 refreservation 不同,快照和后裔使用的空间不计入保留中。例如,如果拍摄 storage/home/bob 的快照,必须有足够的磁盘空间,而不是刷新 refreservation 的数量,这样操作才能成功。主数据集的后代不会被计算在 refreservation 数量中,因此不会占用空间集。
任何形式的保留在一些情况下都是有用的,比如在一个新系统中计划和测试磁盘空间分配的适宜性,或者确保文件系统中有足够的空间用于音频日志或系统恢复程序和文件。

引用保留refreservation 属性使得保证一个特定数据集的使用空间量成为可能,但不包括其后代。这意味着在 storage/home/bob 上设置一个 10GB 的保留,另一个数据集试图使用空闲空间时会为这个数据集保留至少 10GB 的空间。与普通的 refreservationopen in new window 相比,快照和子代数据集使用的空间不计入保留。例如,如果拍摄 storage/home/bob 的快照,必须有足够的磁盘空间,而不是 refreservation 总量,这样操作才能成功。主数据集的后代不会被计算在 refreservation 数量中,因此不会占用空间集。
Resilver当替换一个损坏的磁盘时,ZFS 必须用丢失的数据填充新的磁盘。Resilvering 是使用分布在剩余驱动器上的奇偶校验信息来计算并将丢失的数据写入新的驱动器的过程。
Online处于 Online 状态的池或 vdev 有其成员设备连接并完全运行。处于 Online 状态的单个设备正在运行。
Offline如果存在足够的冗余以避免将池或 vdev 置于 Faultedopen in new window 状态,则管理员将单个设备置于 Offline 状态。管理员可能会选择将一个磁盘脱机,以准备替换它,或者使它更容易被识别。
Degraded一个处于Degraded状态的池或 vdev 有一个或多个磁盘消失或失效。该池仍然可以使用,但是如果其他设备发生故障,该池可能变得无法恢复。重新连接丢失的设备或替换失败的磁盘,在重新连接的设备或新设备完成 Resilveropen in new window 过程后,池将恢复到 Online 状态。
Faulted处于Faulted状态的池或 vdev 不再运行。不能再访问数据。当丢失或故障的设备数量超过 vdev 的冗余水平时,池或 vdev 将进入故障状态。如果重新连接丢失的设备,池将返回到 Online 状态。如果没有足够的冗余来补偿故障磁盘的数量,就会丢失池里的数据,需要从备份中恢复。