首页 > IT职业 > IT职场 > 存储大师班 | ZFS存储池块管理与事务模型
作者简介:肖文文,QingStor 文件存储开发工程师,主要负责文件存储的开发,有多年的存储开发经验,喜爱技术热爱技术。

1 ZFS 的诞生:ZFS 由 Sun 存储部门的 CTO 、研究员 Jeff Bonwick 带领团队开发。开发于 2001 年,作为 Sun Microsystems Solaris 操作系统的一部分。
2 ZFS 的开源:在2005年,Sun 开源 Solaris 的大部分,包括 ZFS 开源;开源项目 OpenSolaris。
3 illumos 项目成立,OpenZFS:
4 ZFS on Linux:2013年,Linux 上 ZFS 的第一个稳定版本,继续发展。

ZFS(Zettabyte File System)是一个跨时代的文件系统,被称为最后一个单机文件系统,已经被证实可以移植到多种 OS 平台上。它拥有如下特性:
1 完全兼容 posix 语义(ZPL: ZFS POSIX Layer)。
2 提供逻辑卷功能(ZVOL: ZFS Volumes)。
3 提交完善的管理功能:通过 libzfs 的工具,经过 ioctl 发送管理命令到zfs kernel module。
4 提供接近无限的存储空间:将物理存储设备进行池化管理,文件系统建立在设备存储池上。当文件系统空间不够,可以将物理设备动态添加存储池中。存储池上的单个文件系统极限值:
5 事务模型 + COW:
事务模型和 COW 是强绑定的关系。
6 端到端的数据安全性:

7 支持快照和克隆
8 支持数据去重功能
9 支持数据压缩功能
存储池 + 块管理
大多数单机文件系统是物理设备强绑定的,一旦在某个物理设备上格式化文件系统之后,该文件系统的可用空间和物理设备大小息息相关。但是 ZFS 将实际的物理设备和文件系统进行隔离,文件系统是建立在存储池上,存储池上的文件系统共用存储池的物理空间,同时可以动态添加物理设备到存储池中,以增加存储池的可用空间。
存储池中的设备管理是一种树形结构进行管理的,如下图所示:
使用如下命令创建一个存储池tank
zpool create -f tank sdc mirror sdd sde raidz1 sdf sdg sdh raidz2 sdi sdj sdk sdl
逻辑虚拟设备为VDEV1、VDEV2、VDEV3、VDEV4
VDEV1对应的物理设备为:sdc
VDEV2对应的物理设备为:sdd、sde组成的RAID1
VDEV1对应的物理设备为:sdf、sdg、sdh组成RAIDZ1
VDEV1对应的物理设备为:sdi、sdj、sdk、sdl组成的RAIDZ22.
Label 磁盘结构:
sdc 的 Label 信息:
[root@ks0 ~]# zdb -l /dev/sdc1
LABEL 0
version: 5000
name: 'tank'
state: 0
txg: 4
pool_guid: 2896170471310910827
errata: 0
hostname: 'ks0'
top_guid: 2751819047198290687 //vdev guid
guid: 2751819047198290687 //phyical dev guid
vdev_children: 4 //总共4个vdevs
vdev_tree:
type: 'disk'
id: 0 //vdev id编号
guid: 2751819047198290687 //vdev guid
path: '/dev/sdc1'
whole_disk: 1
metaslab_array: 148 //vdev的metaslab的个数
metaslab_shift: 27 //vdev的metaslab大小
ashift: 9 //metaslab分最小分配单元512B
asize: 21459632128 //vdev的大小
is_log: 0
create_txg: 4
features_for_read:
com.delphix:hole_birth
com.delphix:embedded_data
labels = 0 1 2 3
sdd 和 sdc 的 Label 信息
type: 'mirror' | type: 'mirror'
id: 1 | id: 1
guid: 14326320160136866584 | guid: 14326320160136866584
metaslab_array: 146 | metaslab_array: 146
metaslab_shift: 25 | metaslab_shift: 25
ashift: 9 | ashift: 9
asize: 5353504768 | asize: 5353504768
is_log: 0 | is_log: 0
create_txg: 4 | create_txg: 4
children[0]: | children[0]:
type: 'disk' | type: 'disk'
id: 0 | id: 0
guid: 14834175139806304144 | guid: 14834175139806304144
path: '/dev/sdd1' | path: '/dev/sdd1'
whole_disk: 1 | whole_disk: 1
create_txg: 4 | create_txg: 4
children[1]: | children[1]:
type: 'disk' | type: 'disk'
id: 1 | id: 1
guid: 4487740773939268111 | guid: 4487740773939268111
path: '/dev/sde1' | path: '/dev/sde1'
whole_disk: 1 | whole_disk: 1
create_txg: 4 | create_txg: 4
features_for_read: | features_for_read:
com.delphix:hole_birth | com.delphix:hole_birth
com.delphix:embedded_data | com.delphix:embedded_data
labels = 0 1 2 3 | labels = 0 1 2 3
传统方式:
谈到存储池,也免不了需要介绍块管理机制,ZFS 的块管理机制是不同于其他的单机文件系统的管理方式。首先需要介绍一下传统的几种块管理方式:
位图管理:最常用的空间管理方法是位图。最小空间单元状态(分配或者空闲)用一个 bit 位表示。
B*树:另外一个常用的管理空闲空间的方式使用 B-tree 管理,叶子节点存储实际的空闲的的偏移和大小。
ZFS 的管理方式
一种全新的空间管理方式:将空间的分配和释放行为作为 log 存入到磁盘中,而不是将空间的状态写入到磁盘。内存中维护的是可用空间的 range-tree,item 是 offset/size 。周期性的将分配和释放的记录写入到磁盘中。
当系统重新启动的时候,在内存中重做全部的 allocation 和 free 的记录,那么可以在内存中构建可用空间的 range-tree。
spacemap 使用的空间也会记录在 spacemap 的内容中。
由于 ZFS 中将 spacemap 也视作为一个文件的,所以更新的方式也是 COW 的,那就会存在每次 sync 中更新 spacemap 都会产生空间分配,那么 spacemap 的更新的怎么结束?ZFS 在更新 spacemap 的过程中取巧:spacemap 在一次 TXG-sync 逻辑中可能会更新多次,第一次更新是 COW 的,以后的更新都是 rewrite(Not COW)的;这样更新的好处是:

TX步骤:
TXG 步骤:

存在的问题:
受篇幅限制,本文仅介绍了 ZFS 中的存储池、块管理与事务模型,后续我们会继续分享 Intend log、ZIO、DMU、ARC、Snap、Clone等机制。
来自Linus的一盆冷水:[《Don’t Use ZFS on Linux: Linus Torvalds》]。我们并不需把 Linus 的每句话奉为圭臬,辩证的看待他的观点,并且 Linus 主要表达的还是对 Oracle 的不信任,以及可维护性方面担忧,并没有对 ZFS 优秀的设计喷垃圾话。直到今天再看二十年前设计的 ZFS,它的 COW,TXG,ZIO,LOG,块管理的设计理念依然让我惊叹不已,ZFS 针对数据存储的痛点,从最底层设计进行革命性的优化和创新。
om) 发布!
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/1446/viewspace-2797345/ ,如需转载,请注明出处,否则将追究法律责任。