ZFS 入门指北: 基础篇

File system can be fun!

对于很多人来说, 文件系统没什么好关心的. Windows 上只能用 NTFS 1, 而在 macOS 上, 要么 HFS+ 要么 APFS. Linux 上选择多一些, 但是一般情况下无脑选 XFS 或者 ext4 即可.

但是文件系统其实是可以很强大的! 一些现代文件系统可以实现很多惊人的功能. 这里着重介绍一个我很喜欢的文件系统: ZFS.

文件系统入门

早期

早期的文件系统 (例如 FAT2 和它的朋友们) 很简单. 你有一块磁盘 (或软盘), 文件系统指定一块区域作为存储目录的区域, 搞定. 当你添加一个文件的时候, 文件系统在目录中新增一条记录 (以记录文件的位置), 随后把新文件写到分配好的空间上. 而当你删除文件时, 将对应的记录删除即可. 简单粗暴3.

但是这么做有一个问题: 当遇到意外断电的时候, 这一类的文件系统变得十分脆弱. 如果文件系统在写入数据过程中发生了断电 (磁盘上的结构已经在写入过程中改变), 文件关联表就来不及更新. 这样的结果就是你有了一个损坏的文件系统, 任何新写入操作都有可能会导致数据丢失.

日志

为了应对这个问题, 人们给文件系统加入了日志功能. 支持日志的文件系统会在任何写入操作之前创建一条记录. 这样的话, 即使发生了意外断电, 文件系统的 "状态" 也会被保留下来. 在系统重新启动后文件系统即可使用这些记录来修复文件系统. 正在写入的数据仍会丢失, 但是至少已有的数据不会有问题.

大多数现代操作系统都使用日志文件系统, 比如 Windows 上的 NTFS, macOS 曾经的默认文件系统 HFS+ 和 Linux 上面的 ext3, ext4xfs.

Copy-on-Write (写入时复制)

断电问题的另一种解决方案是 CoW. 上面提到, 日志文件系统 在进行写入操作的时候仍是在原处进行修改, 只是在写入前后会记录日志. 在 CoW 文件系统当中, 当进行写入操作的时候, 实际上写入的位置是由文件系统分配的一块全新的区块. 在写入完成后, CoW文件系统 会将目录中的指针指到新位置上, 然后将旧块标注为空闲, 以便在未来使用.

在这种模型下, 即使在写入过程中发生了断电, 原数据块和目录表仍然完好. 而在新的位置上, 既然目录没有更改, 在目录中新位置仍然是未使用状态, 因此万事大吉, 文件系统状态完好.

除了避免意外断电带来的文件损失, 这种模型还有许多意外的好处. 最简单的例子即是 快照. 在日志文件系统上面, 创建和维护快照是一个费事费力的行为. 而在 CoW文件系统 上则非常简单: 在写入完成后, 不将原始区块标记为空闲, 而是标记为被某快照使用. 在访问快照时, 只需找出该文件对应的, 标记为快照的区块即可.

然而, 由于在机械硬盘的特性, 这种策略会产生比较严重的磁盘碎片. 不过既然现在 HDD 主要作为大容量慢速存储 (而且可以使用 SSD 作为存储池的读写缓存), 在现在这个时间点上这个问题已不明显.

ZFS 的优势

相对于传统的文件系统, CoW 文件系统天生有利于实现许多高级功能. 最明显的高级功能即是上面提到过的快照. 这里, 我们会介绍 ZFS 相对于其他 CoW 文件系统的几个独特功能.

自愈

听上去不是一个用来描述文件系统的词…

ZFS 没有 fsck (或者 CHKDSK, 如果你用的是 NTFS 的话), 所以说再也不会在开机界面卡半天了.

与之相对的, ZFS 的文件系统检查发生在每一次读取过程中. 每当一个存储块被读取时, ZFS 会计算这个块的校验和, 并与文件系统中记录的, 这个块应该有的校验和, 进行比对. 如果校验和匹配, 则一切正常. 如果不匹配的话, ZFS 就会自动从一个备份源中获取这个块的数据 (例如 内存中的缓存, RAIDZ (稍后会提) 中的冗余等), 将这个正确的数据块提交给程序, 并将损坏的块标注为不可靠以免未来再用.

但是, 这套机制只对热数据有用. 对于冷数据, ZFS 通过一个叫做 scrub 的过程维护数据. 这个过程简单来说就是强制读取一遍数据池中所有的数据并检验校验和. 合格的 ZFS 运维通常会通过计划任务的方式定期 (一般为一星期一次) 执行 scrub 指令.

RAIDZ

RAID 应该是一个很常见的概念了. ZFS 有一套软件 RAID 功能, 名叫 RAIDZ.

与传统的硬件 RAID 卡相比, RAIDZ 是基于数据块的 (而不是基于整块磁盘), 因此更加灵活和高效. 在硬盘失效发生时, 传统 RAID 5 阵列必须将硬盘阵列下线以进行耗时的重构, 而 ZFS 的自愈功能会自动从 RAIDZ 中的冗余盘中计算出数据并提供给应用程序 (当然, 有性能降级).

而且, 当 RAID 5 阵列重构失败时, 你只能将剩下的拷出来, 然后重新构建阵列. 而在 RAIDZ 中, ZFS 可以自动重建所有正常的块, 并通知管理员哪些块无法重建.

透明压缩

ZFS 支持透明压缩. 也就是说, (如果启用的话) ZFS 会自动压缩所有你放进去的文件. 这不仅仅可以节省空间, 甚至在慢速的硬盘上甚至可以提高性能 (因为压缩速度比写入速度快得多).

数据集 (dataset)

ZFS 的 数据集 有点类似于 LVM 的逻辑卷, 但是它内部仍然是 ZFS 的数据结构.

数据集的主要目的在于方便细化管理. 例如, 在一个存放图片的数据集上就没有必要打开压缩了 (因为 JPG 等早就被高度压缩了). 在存储冷数据的数据集上, 则可以打开速度慢但是压缩比高的压缩算法以节省空间.

ZFS 的问题

没有事物是完美的.

许可证不兼容 (Linux)

讨论这个问题之前, 首先我们得了解一下 ZFS 的历史. Sun Microsystems (在大陆通常被译为 太阳计算机系统) 开发了 ZFS 以替换 Solaris 上面老旧的 UFS 文件系统. 在 2005年, ZFS的源代码以 OpenSolaris 一部分的身份被开源. 问题是, 当时发布时, Sun 选用了 CDDL 作为开源许可协议, 而这个协议与 Linux 届常用的 GPL 并不兼容.

这种不兼容直接导致了 ZFS 的代码并不能合并入 Linux 主线代码库中, 而且分发者 (各种发行版, ubuntu, Arch, etc.) 不能直接分发编译好的内核模块. 这就意味着如果用户想要使用 ZFS, 他们必须在设备上自行编译 ZFS. 这意味着相对于主线支持的文件系统, ZFS 安装起来相对麻烦. 以及, 在维护无法启动的系统, 很难找到支持 ZFS 的维护系统.

无法并入主线内核的另一个后果是, 目前为止 ZFSonLinux (以下简写为 ZoL) 项目都是在内核源码树之外单独开发. 因此, 有时 ZoL 很难跟上 Linux 主线代码的发展.

也是因为这个原因, Linux 的开发者们另起炉灶, 开发了 BtrFS. 但目前为止, ZFS 的稳定性和功能集仍然领先.

高 RAM 占用

ZFS 使用了自己的 ARC 缓存模型. 这带来了更高的缓存命中率, 但是这也导致了这部分缓存不在内核的 cached 内存内. 因此, 当内存不够时, 这部分缓存无法被释放. 因此, 推荐使用 ZFS 的系统准备充足的内存.

无法从阵列中移除硬盘

目前为止, ZFS 无法从一个 RAIDZ 阵列中移除部分硬盘. 同样, 你也不能缩小一块硬盘上的 ZFS 分区大小. 如果是在 NAS 上使用 ZFS 的话, 这不算什么. 但是如果是在一台个人计算机上使用而且你还经常折腾系统的话, 这个缺点非常恼人.

好消息是, ZFSonLinux 从 v0.8.0 开始支持了从 镜像阵列 和 简单阵列(类似 RAID 0) 中移除单块硬盘了.

尝试!

如果这都没有吓跑你的话, 好极了!

目前对 ZFS 支持最佳的操作系统毫无疑问是 FreeBSD. 由于 FreeBSD 使用相对宽松的 BSD协议, ZFS 可以被合法地整合入 FreeBSD. 因此, FreeBSD 原生支持 ZFS, 你可以毫无问题地使用 ZFS 作为 FreeBSD 的根文件系统. 然而, 由于 FreeBSD 的桌面支持欠佳, 它更适合被安装在一台纯粹的服务器上 (例如 NAS).

Linux 上的 ZFS 支持由 ZFSonLinux 项目开发. 由于上文提到过的原因, 它只能作为树外模块手动安装. 但是只要不是在刚刚发布的内核版本上, ZoL 一般都工作良好. 而且感谢 DKMS, 其实安装起来也不麻烦.

macOS 也有自己的 ZFS 移植版本. 它叫 OpenZFS on OS X. 从名字即可知这玩意有点年头了, 也是一个很稳定的移植.

最近, ZFS 甚至被移植到了 Windows 上面, 叫 ZFSin. 这个移植版本相对较新, 而且由于 Windows 闭源且架构和 UNIX 不同, 开发比较艰难. 目前为止, 这个版本还不是很稳定 (经常造成蓝屏…), 但是开发很活跃, 并且正在变得越来越稳定.

更多阅读

文件系统是一个很有趣也很高深的话题, 况且 ZFS 可能是目前最为复杂的文件系统. 如果你对这一块感兴趣的话, 可以读一读/看一看以下内容.

致敬!

感谢所有 创造了 ZFS 的人们!


1

实际上, Windows 也有自己的 CoW 文件系统, 叫 ReFS. 可惜这个文件系统没什么基于 CoW 的高级功能, 而且微软似乎对它也不是很上心, 因此这里忽略它.

2

FAT 文件系统其实得名于 File Association Table (即 文件关联表) 而不是因为这个文件系统很肥 (

3

我在这里简化了 很多, 但不要在意这些细节 :)

发表于 2020-02-09
JS
Arrow Up