# 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 专门放虚拟机），实现互不干扰、干净清爽的存储拓扑。
