# ZFS 默认参数还不够好

在构建现代 NAS、海量数据存储或高性能虚拟化宿主机时，ZFS 是目前最强大的文件系统之一。然而，ZFS 的默认配置偏向于传统的 Unix 环境，直接使用默认命令建池往往无法发挥其在 Linux 下的最大潜力。

### 第一部分：创建命令

假设我们拥有 4 块硬盘，准备组建容错率为 2 块盘的 **RAIDZ2** 阵列：

#### 步骤 1：创建根池

<pre class="language-bash"><code class="lang-bash">sudo zpool create -o ashift=12 \
    -o compatibility=off \
    -O normalization=formC \
<strong>    -O compression=zstd \
</strong>    -O atime=off \
    -O xattr=sa \
    -O dnodesize=auto \
    -O acltype=posix \
    -O canmount=off \
    tank raidz2 \
    /dev/disk/by-id/ata-WD_RED_1... \
    /dev/disk/by-id/ata-WD_RED_2... \
    /dev/disk/by-id/ata-WD_RED_3... \
    /dev/disk/by-id/ata-WD_RED_4...
</code></pre>

#### 步骤 2：创建数据集

```bash
sudo zfs create tank/data
```

执行完毕后，你的数据就可以安全地存入 `/tank/data` 目录了。

### 第二部分：为什么叫 `tank`？

在几乎所有的 ZFS 官方文档和教程中，存储池的名字总是叫 `tank`：

1. **核心隐喻（水池与水箱）**：ZFS 的核心创新是抛弃了死板的分区，提出了“存储池（Storage Pool）”的概念。所有的底层硬盘汇聚成一汪可以流动的“水”（容量）。既然是 Pool（池子），那用来装载这些数据水流的容器，自然就是 Tank（水箱）。
2. **《黑客帝国》的终极彩蛋**：ZFS 诞生于 2001 年的 Sun 公司。当时电影《黑客帝国》（The Matrix）风靡硅谷。在电影中，主角飞船上那位**负责管理所有数据流、加载虚拟环境程序的操作员**，名字就叫 **Tank**。在当年早期的 ZFS 测试代码中，甚至还出现了 `dozer`（Tank 的哥哥）、`neo`（男主角）和 `zion`（人类家园）等命名。
3. **极简主义**：`tank` 只有四个字母，全部在键盘左手区，没有任何数字，敲击极快，且在 Linux 枯燥的命名（如 `vg0`, `md127`）中极具辨识度。

### 第三部分：配置解析

#### 1. 建池规则

* **使用硬盘 ID (`/dev/disk/by-id/...`)**
  * Linux 重启时，`/dev/sda`、`/dev/sdb` 这样的盘符可能会发生漂移变化。使用全球唯一的硬盘物理 ID 建池，可以保证无论硬盘插在哪个接口，ZFS 都能准确无误地识别并导入阵列。
* **`-o ashift=12`（建池后不可更改！）**
  * `ashift` 决定了底层最小写入扇区大小（2^12 = 4096）。现代硬盘即使物理扇区是 4K，为了兼容老系统，也会向操作系统“谎称”自己是 512 字节。如果不强制设为 `12`，ZFS 会信以为真，导致每次写入都需要进行极度消耗性能的“读-改-写”操作。强制 `ashift=12` 可消除这种写放大，保护性能和硬盘寿命。注意这里是小写的 `-o`，代表 Pool 级别的底层属性。
* **`-o compatibility=off`**
  * 关闭功能兼容模式，这意味着该存储池将启用当前 ZFS 版本支持的所有最新特性（Features）。这在不需要将该存储池导入到旧版本 ZFS 或其他操作系统（如旧版 FreeBSD/Solaris）时是最好的选择。

#### 2. 空间与性能

* **`-O normalization=formC`**
  * 开启 Unicode 字符归一化，这可以确保不同操作系统（如 Windows、macOS 和 Linux）通过 SMB/NFS 等网络协议访问时，文件名中的特殊字符（特别是带声调或组合符号的语言）不会出现乱码或找不到文件的问题。
* **`-O compression=zstd`（透明压缩）**
  * Zstandard 是目前最优秀的压缩算法。开启后，不仅凭空增加大量可用空间（文本、日志、虚拟磁盘等压缩率极高），而且由于写入物理硬盘的数据变少了，反而能**提升整体的 I/O 吞吐速度**。
* **`-O atime=off`（关闭访问时间）**
  * Linux 默认会在你每次“读取”文件时，向硬盘发起一次“写入”操作来更新访问时间。关闭它，让读取操作变成纯粹的只读，消除大量无谓的碎片化写入，大幅提升读取速度。

#### 3. 小文件优化

在 Linux 下运行 ZFS（尤其是提供 SMB 共享或存储大量小文件时），以下三个参数必须绑定开启：

* **`-O xattr=sa`（扩展属性存入系统属性）**
  * Linux 的扩展属性（如 SELinux 标签、Samba 权限）默认会被 ZFS 写在隐藏的独立目录中，导致每次读取都需要额外的磁盘寻道。设为 `sa` 后，这些属性会直接写在文件的 inode（dnode）内部，性能呈指数级提升。
* **`-O dnodesize=auto`（动态节点大小）**
  * 传统的 dnode 大小固定为 512 字节，如果复杂的权限属性塞不下，依然会导致性能下降。设为 `auto` 允许 ZFS 动态分配更大的 dnode 来一口气装下所有元数据。
* **`-O acltype=posix`（POSIX 访问控制列表）**
  * 启用高级权限控制。如果你需要为不同的用户组分配精细的权限，或者无缝对接 Windows 的 Samba 共享权限映射，这是必选项。

#### 4. 拓扑设计

* **`-O canmount=off`（禁止挂载根节点）**
  * 这是最高级的设计模式。我们在 `tank` 上打满了各种优化配置，但禁止挂载 `tank` 本身。此时，`tank` 变成了一个纯粹的“属性模板（Container/Template）”。
  * 随后我们通过 `zfs create tank/data` 创建子数据集时，`tank/data` 会**自动继承** `tank` 的所有优化属性（除了 `canmount` 会恢复正常挂载）。这保证了数据绝对不会意外写进根目录，且未来可以轻松地创建拥有不同特性的平行数据集（例如单独建一个 `tank/vm` 并将块大小 `recordsize` 改为 64K 专门放虚拟机），实现互不干扰、干净清爽的存储拓扑。


---

# Agent Instructions: 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:

```
GET https://blog.wh2099.com/linux/zfs-mo-ren-can-shu-hai-bu-gou-hao.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
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.
