> For the complete documentation index, see [llms.txt](https://freebsd-journal-cn.bsdcn.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://freebsd-journal-cn.bsdcn.org/2014-0102-freebsd-10/the-future-of-storage.md).

# 存储的未来：Z 文件系统

作者：Allan Jude

Z 文件系统（ZFS）由 Sun Microsystems 的 Jeff Bonwick 和 Matthew Ahrens 创建，与以往的文件系统有根本性不同。关键区别在于 ZFS 实际上不止是文件系统，它同时承担 RAID 控制器、卷管理器和文件系统的角色。大多数以往的文件系统设计为在单个设备上使用。为克服此限制，RAID 控制器和卷管理器将若干磁盘组合成单个逻辑卷，再呈现给文件系统。ZFS 的强大之处在于文件系统密切感知底层存储设备的物理布局，从而能就如何可靠存储数据和管理 I/O 操作做出更明智的决策。

ZFS 最初作为 OpenSolaris 的一部分发布。Sun Microsystems 被 Oracle 收购后，决定在封闭许可下继续开发 ZFS。这使社区停留在原始通用开发与分发许可证（CDDL）下的 ZFS v28。为继续开发和改进这一开源 ZFS 分支，创建了 OpenZFS 项目——这是 FreeBSD、Illumos、ZFS-On-Linux 项目以及众多其他开发者和厂商的联合努力。这个新的 OpenZFS（包含在 FreeBSD 8.4 和 9.2 及以后版本中）将版本号改为“v5000 - Feature Flags”，以避免与 Oracle 继续的专有 ZFS 开发（目前为 v34）混淆，并确保各种开源 ZFS 版本之间的兼容性和清晰度。OpenZFS 不再继续递增版本号，而是改用“Feature Flags”来添加新特性。池用 `feature@featurename` 属性标记，只有兼容的 ZFS 版本才会导入该池。其中一些较新的属性是只读向后兼容的，意味着较旧的实现可以导入池并读取，但不能写入，因为它们缺乏对新特性的支持。

## ZFS 有何不同？

ZFS 中最重要的特性集是那些为确保数据完整性而设计的特性。ZFS 是写时复制（COW）文件系统，这意味着数据从不就地覆盖，而是将更改的块写入磁盘上的新位置，然后更新元数据指向新位置。这确保在发生截断写入（即正在写入某块时被中断）时，数据的原始版本不会丢失或损坏，而传统文件系统则会如此。在电源故障或系统崩溃的情况下，文件处于不一致状态，其中混合了新数据和旧数据。写时复制还启用了另一项强大功能——快照。ZFS 允许你即时创建数据集（及其所有子数据集，可选）的一致时间点快照。新快照不占用额外空间（除少量元数据外）且是只读的。后来，当某块被更改时，旧块成为快照的一部分，而非作为空闲空间回收。此时存在两个不同的文件系统版本：快照（快照拍摄时文件系统的样子）和实时文件系统（当前的样子）。唯一消耗的额外空间是那些已更改的块；未更改的块在快照和实时文件系统之间共享，直到被修改。这些快照可挂载以恢复其中包含的文件旧版本，或可将实时文件系统回滚到快照拍摄时的时间点，丢弃快照以来的所有修改。快照是只读的，但可用于创建文件系统的克隆。克隆是新的实时文件系统，包含其父级的所有数据，但在被写入前不消耗额外空间。

这些特性保护你的数据免受常见问题：崩溃、电源故障、意外删除/覆盖等。但问题不那么明显的情况呢？磁盘可能遭受静默损坏、位翻转、线缆故障和控制器故障。为解决这些问题，ZFS 为它写入的每个块计算校验和，并将校验和与元数据一起存储。读取块时，再次计算校验和并与存储的校验和比较；如果两个值不匹配，说明出了问题。传统文件系统无法知道存在问题，会直接返回损坏的数据。而 ZFS 会尝试从 ZFS 支持的各种冗余形式中恢复数据。遇到错误时，ZFS 递增 `zpool status` 命令显示的相关计数器。如果冗余可用，ZFS 会尝试纠正问题并正常继续；否则，它会返回错误而非损坏的数据。

校验和算法默认为 fletcher，但也可用 SHA256 加密哈希算法，以性能损失换取小得多的哈希碰撞概率。

## 面向未来的存储

ZFS 旨在克服以往文件系统上施加的任意限制。例如，EXT3 文件系统上单个文件的最大大小为 2^31（2 TiB），EXT4 上为 2^44（16 TiB），UFS2 上为 2^55（32 PiB），而 ZFS 上为 2^64（16 EiB）。EXT3 限制为 32,000 个子目录，EXT4 限制为 64,000 个，而 ZFS 每个目录可包含多达 2^48 个条目（文件和子目录）。ZFS 中的限制设计得如此之大，以至于永远不会遇到，而非只是未来几年够用。由于 ZFS 既是卷管理器又是文件系统，可以向运行中的系统添加额外存储设备，新空间立即对该池中所有现有文件系统可用。

zpool 中的每个顶层设备称为 vdev，可以是简单磁盘或 RAID 变换，如镜像或 RAID-Z 阵列。ZFS 文件系统（称为数据集）各自可访问整个池的组合空闲空间。随着块被分配，池（和文件系统）可用的空闲空间减少。这种方式避免了广泛分区时常见的空闲空间在分区之间碎片化的陷阱。

## 软件实现更好？

最佳实践建议将原始磁盘驱动器的无障碍访问权交给 ZFS，而非由硬件 RAID 控制器创建的单个逻辑卷。RAID 控制器通常会屏蔽错误并尝试解决而非向 ZFS 报告，使 ZFS 不知道存在问题。如果使用硬件 RAID 控制器，建议将其设置为 IT“Target”或 JBOD 模式，而非提供 RAID 功能。ZFS 包含自己的 RAID 功能，且更优越。

创建 ZFS 池（zpool）时，有若干冗余级别可选：条带（RAID0，无冗余）、镜像（RAID1 或更好的 n 路镜像）和 RAID-Z。ZFS 镜像工作方式与传统 RAID1 非常相似（区别在于可将 3 块或更多驱动器放入单个镜像集以获得额外冗余）。但 RAID-Z 与类似的传统 RAID 配置（RAID5/6/50/60）有一些重要区别。与 RAID5 相比，RAID-Z 提供更好的奇偶校验分布，并消除了“RAID5 写入空洞”——即数据和奇偶校验信息在意外重启后变得不一致。当数据写入传统 RAID5 阵列时，奇偶校验信息非原子更新，意味着奇偶校验必须在数据更新后单独写入。如果某事（如电源故障）中断此过程，奇偶校验数据实际是不正确的，如果包含数据的驱动器故障，奇偶校验将恢复不正确的数据。ZFS 提供 3 级 RAID-Z（Z1 到 Z3），冗余级别递增，可用存储递减。阵列可承受的驱动器故障数对应名称，因此 RAID-Z2 阵列可同时承受两块驱动器故障。

如果创建多个 vdev（例如两个独立的镜像集），ZFS 会跨两个镜像条带化数据，提供更高的性能和 IOPS。创建由两个或更多 RAID-Z2 vdev 组成的 zpool 实际上会创建 RAID60 阵列，跨冗余 vdev 条带化数据。

ZFS 还支持数据集属性 `copies`，控制每个块存储的副本数。默认为 1，但增大此值后，ZFS 会多次存储每个块，提高在故障或数据损坏时恢复的可能性。

## 更快总是更好！

除提供非常有效的数据完整性检查外，ZFS 的设计也考虑了性能。第一层性能由自适应替换缓存（ARC）提供，完全驻留在 RAM 中。传统文件系统使用最近最少使用（LRU）缓存，这只是按每个对象最近使用时间排序的缓存项列表。新项添加到列表顶部，一旦缓存满，列表底部的项被驱逐以腾出空间给更活跃的对象。ARC 由四个列表组成——最近最常用（MRU）和最频繁使用（MFU）对象，加上各自的幽灵列表。这些幽灵列表跟踪最近被驱逐的对象，防止它们被加回缓存。这通过避免只有偶尔使用历史的对象来提高缓存命中率。同时使用 MRU 和 MFU 的另一个优势是，扫描整个文件系统通常会将所有数据从 MRU 或 LRU 缓存中驱逐，以容纳这些新访问的内容。在 ZFS 的情形，由于还有只跟踪最频繁使用对象的 MFU，最常访问块的缓存得以保留。ARC 可检测内存压力（当其他应用需要内存时），并释放部分为 ARC 保留的内存。在 FreeBSD 上，ARC 默认最多使用除 1 GB 外的所有 RAM，但可通过 `vfs.zfs.arc_max` 加载器可调参数限制。

ARC 可选择由二级 ARC（L2ARC）增强。这是一块或多块 SSD，用作读缓存。当 ARC 满时，其他常用对象写入 L2ARC，从那里可比从主存储池更快读回。数据加入缓存设备的速率有限制，以防止过多写入过早磨损 SSD。写入 L2ARC 受 `vfs.zfs.l2arc_write_max` 限制，“Turbo Warmup Phase”除外；在 L2ARC 装满前（第一块被驱逐以腾出空间给新内容），写入限制提高 `vfs.zfs.l2arc_write_boost` 的值。OpenZFS 还具有由 `secondarycachecompress` 数据集属性控制的 L2ARC 压缩。这通过压缩率增大 L2ARC 的有效大小，同时也提升读性能，因为数据尽可能快地读取然后解压，从而获得更高的有效读速度。L2ARC 压缩仅使用 LZ4 算法，因为其极高的解压性能。

## 细粒度控制

ZFS 的强大很大程度来自每个数据集有一组控制其行为的属性，且这些属性被其子级继承。常见最佳实践是将 `atime` 属性（跟踪每个文件的最后访问时间）设为“off”。这避免每次访问文件时都写入元数据更新。ZFS 的另一个强大特性是透明压缩。可按数据集启用和调优，因此可压缩 **/usr/src** 和 **/usr/ports** 但禁用 **/usr/ports/distfiles** 的压缩。OpenZFS 包含若干不同的压缩算法可选，包括：LZJB（适度压缩、适度 CPU 使用）、GZIP1-9（更好压缩，但更多 CPU 使用，可调）、ZLE（压缩 0 的序列，特定情况有用）和 LZ4（v5000 中加入，比 LZJB 更好压缩且更少 CPU 使用）。LZ4 是采用 BSD 许可的新高性能、多核可扩展压缩算法。除在更少时间内更好压缩外，还具有极快的解压速率。与 ZFS 使用的默认 LZJB 压缩算法相比，LZ4 在压缩可压缩数据时快 50%，在尝试压缩不可压缩数据时快三倍以上。不可压缩数据上的性能大幅提升来自“早期中止”特性。如果 ZFS 检测到压缩节省小于 12.5%，则中止压缩并写入未压缩数据，但一旦解压，提供更高的有效吞吐量。此外，解压大约快 80%；在现代 CPU 上，LZ4 可按每 CPU 核 500 MB/s 压缩和 1500 MB/s 解压。这些数字意味着对于某些工作负载，压缩实际上会带来更高性能——即使有 CPU 使用惩罚——因为数据可以与未压缩数据相同的速度从磁盘读取，但解压后提供更高的有效吞吐量。LZ4 在 8k 块上 1.5 GB/s 的解压速度意味着额外延迟仅 5 微秒，比目前最快的 SSD 还要快一个数量级。

ZFS 还提供非常快速和准确的数据集、用户和组空间核算，以及配额和空间预留。这为管理员提供了对空间分配的细粒度控制，允许关键文件系统预留空间以确保其他文件系统不会占用所有空闲空间。

在所有这些之上，ZFS 还具有完整的委派功能集。委派各种管理功能（如配额控制、快照、复制、ACL 管理，以及对数据集 ZFS 属性的控制）可提高安全性和灵活性，减轻管理员工作量。使用这些功能，无需 root 权限即可基于快照做一致备份。管理员还可选择为每个用户的家目录使用单独的数据集，并将快照创建和压缩设置的控制委派给该用户。

## 复制——超越节点的冗余

ZFS 还具有强大的复制系统。使用 `zfs send` 和 `zfs receive` 命令，可将数据集（及其子级，可选）发送到另一个数据集、另一个池或完全另一个系统。ZFS 复制还支持增量发送，仅发送一对快照之间已更改的块。OpenZFS 包含对此功能的增强，提供需要发送多少数据的估计，以及数据传输期间的反馈。这是 PC-BSD 的 Life Preserver 功能的基础。未来计划的功能还将允许恢复中断的 ZFS send/receive 操作。

## 利用固态硬盘的强大功能

除前面讨论的 L2ARC 读缓存外，ZFS 还支持可选的日志设备，也称 ZFS Intent Log（ZIL）。某些工作负载（尤其是数据库）要求保证它们写入磁盘的数据确实已到达“稳定存储”。这些称为同步写入，因为系统调用在数据安全写入磁盘前不会返回。这种额外安全传统上以性能为代价，但有了 ZFS 就不必如此。ZIL 通过使用比主池更快、延迟更低的存储设备（如 SSD）来加速同步事务。当数据正在写入且应用请求保证数据已安全存储时，数据写入更快的 ZIL 存储，然后稍后刷新到常规磁盘，大幅降低同步写入的延迟。系统崩溃或断电时，ZFS 文件系统再次挂载后，ZIL 中的未完成事务会重放，确保所有数据安全就位于主存储池中。日志设备可镜像，但不支持 RAID-Z。指定多个日志设备时，写入会跨所有设备负载均衡，进一步提高性能。ZIL 仅用于同步写入，因此不会提高异步工作负载的性能（也不会被其拖累）。

OpenZFS 还获得了 TRIM 支持。固态硬盘（SSD）的工作方式与传统旋转磁盘略有不同。由于闪存单元随时间磨损，SSD 的闪存转换层（FTL）——使 SSD 对系统看起来像典型旋转磁盘——经常将数据移动到不同物理位置以均匀磨损单元，并绕过磨损的单元。为有效执行此操作，SSD 的 FTL 需要知道何时块已释放（其上存储的数据可被覆盖）。没有关于哪些块不再使用的信息，SSD 必须假设任何曾写入的块仍在使用，这导致碎片化和大幅降低的性能。

初始化新池和向现有池添加设备时，ZFS 会执行整盘 TRIM，擦除设备上所有块以确保最佳起始性能。如果设备是全新的或之前已擦除，将 `vfs.zfs.vdev.trim_on_init` sysctl 设为 0 可跳过此步骤。TRIM 操作的统计信息由 `kstat.zfs.misc.zio_trim` sysctl 暴露。为避免过多 TRIM 操作和增加 SSD 磨损，ZFS 在块释放时将 TRIM 命令排队，但（默认）等待 64 个事务组后才将命令发送到驱动器。如果块在此时间内被重用，则从 TRIM 列表中移除。L2ARC 也支持 TRIM，但基于时间限制而非事务组数量。

## OpenZFS——下一步走向何方？

最近成立的 OpenZFS 项目（<https://open-zfs.org/>）创建目标明确：提升对开源 ZFS 的认知、鼓励各种实现和厂商之间的开放沟通，并确保所有 ZFS 发行版之间一致的可靠性、功能和性能。该项目还有许多关于 ZFS 未来改进的想法，包括：可恢复的 send/receive、ZFS 通道程序（允许多个操作原子完成）、设备移除、统一的 ashift 处理（用于 4k 扇区“高级格式”驱动器）、将最大记录大小从 128KB 增加到 1MB（最好以与 Oracle ZFS v32 兼容的方式）、平台无关的加密，以及去重改进。

***

**Allan Jude** 是 ScaleEngine Inc.（一家全球 HTTP 和视频流内容分发网络）的运营副总裁，在该公司大量使用 FreeBSD 上的 ZFS。他也是视频播客“BSD Now”（与 Kris Moore 一起）和 JupiterBroadcasting.com 上“TechSNAP”的主持人。此前他在加拿大汉密尔顿的 Mohawk College 教授 FreeBSD 和 NetBSD，拥有 12 年 BSD Unix 系统管理经验。


---

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

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

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

```
GET https://freebsd-journal-cn.bsdcn.org/2014-0102-freebsd-10/the-future-of-storage.md?ask=<question>&goal=<endgoal>
```

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

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

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