ZFS(Zettabyte File System)一个打破过去思维的文件系统,是 Sun Microsystems这家公司所开发出来的全新型态文件系统,因为License的问题所以目前只有在Solaris、Mac、BSD上看得到,ZFS是128bit的文件系统,而它到底有多强呢?别再等待了马上用了你就知道,只能说ZFS真是一个上帝赐给IT人员的好礼物。

为什么选择 ZFS

ZFS 非常的优秀。这是一个真正现代的文件系统,内置的功能对于处理大量的数据很有意义。
ZFS 消除了建立传统 RAID 阵列(独立磁盘冗余阵列)的需要。 相反,您可以创建 ZFS 池,甚至可以随时将驱动器添加到这些池中。 ZFS 池的行为操作与 RAID 几乎完全相同,但功能内置于文件系统中。
ZFS 也可以替代 LVM (逻辑盘卷管理),使您能够动态地进行分区和管理分区,而无需处理底层的细节,也不必担心相关的风险。
这也是一个 CoW (写时复制)文件系统。 这里不会提及太多的技术性,这意味着 ZFS 可以保护您的数据免受逐渐损坏的影响。 ZFS 会创建文件的校验和,并允许您将这些文件回滚到以前的工作版本。

安装 ZFS

1
2
3
4
5
6
7
在 Ubuntu 上安装 ZFS 非常简单,但对于 最新版本来说,这个 稍有不同。

# Ubuntu 16.04 LTS
sudo apt install zfs

# Ubuntu 17.04 及以后
sudo apt install zfsutils

zfs 中的 池,集,卷术语

ZFS有三个概念:

  1. Z池 - zpool

  2. Z集 - dataset

  3. Z卷 - volume

Z池 zpool create local /dev/sdb /dev/sdc /dev/sdd /dev/sde 这就创建了一个名叫local的Z池,Z池有如下类型:raid1,raid0, raid5,raid6 等。

Z集 当一个Z池创建出来,附带有一个同名的Z集也创建出来了,这里就是 叫 local 这个。注意这个Z集挂载在/local, 这个目录可以做一个正常的目录来用,可以在Z集上创建新的Z集:zfs create local/data

Z卷 创建Z卷:zfs create -V 10G local/vda Z卷是一个块设备,可以按需要分区,格式化:mkfs.xfs /dev/zvol/local/vda

创建池

在 ZFS 中,池大致相当于 RAID 。 它们很灵活且易于操作。
在这里插入图片描述

RAID0

1
2
3
4
5
#RAID0 只是把你的硬盘集中到一个池子里面,就像一个巨大的驱动器一样。 它可以提高你的驱动器速度,但是如果你的驱动器有损坏,你可能会失丢失数据。


sudo zpool create your-pool /dev/sdc /dev/sdd

RAID1(镜像)

1
2
3
您可以在 ZFS 中使用 mirror 关键字来实现 RAID1 功能。 RAID1 会创建一个一对一的驱动器副本。 这意味着您的数据一直在备份。 它也提高了性能。 当然,你将一半的存储空间用于了复制。

sudo zpool create your-pool mirror /dev/sdc /dev/sdd

RAID5/RAIDZ1

1
2
3
4
5
ZFS 将 RAID5 功能实现为 RAIDZ1。 RAID5 要求驱动器至少是 3 个。
并允许您通过将备份奇偶校验数据写入驱动器空间的 1/n(n 是驱动器数),留下的是可用的存储空间。
如果一个驱动器发生故障,阵列仍将保持联机状态,但应尽快更换发生故障的驱动器。

sudo zpool create your-pool raidz1 /dev/sdc /dev/sdd /dev/sde

RAID6/RAIDZ2

1
2
3
RAID6 与 RAID5 几乎完全相同,但它至少需要四个驱动器。 它将奇偶校验数据加倍,最多允许两个驱动器损坏,而不会导致阵列关闭。

sudo zpool create your-pool raidz2 /dev/sdc /dev/sdd /dev/sde /dev/sdf

RAIDZ3
3奇偶校验位,允许在丢失数据之前发生3个磁盘故障,性能与RAIDZ2和RAIDZ类似。例如,创建3奇偶校验6 VDEV池:

1
sudo zpool create example raidz3 /dev/sdb /dev/sdc /dev/sdd /dev/sde /dev/sdf /dev/sdg

RAID10(条带化镜像)

1
2
3
4
5
6
RAID10 旨在通过数据条带化提高存取速度和数据冗余来成为一个两全其美的解决方案。 你至少需要四个驱动器,但只能使用一半的空间。 您可以通过在同一个池中创建两个镜像来创建 RAID10 中的池。

sudo zpool create your-pool mirror /dev/sdc /dev/sdd mirror /dev/sde /dev/sdf
或者
sudo zpool create example mirror /dev/sdb /dev/sdc
sudo zpool add example mirror /dev/sdd /dev/sde

嵌套RAIDZ (RAID50, RAID60)

1
2
$ sudo zpool create example raidz /dev/sdb /dev/sdc /dev/sdd /dev/sde
$ sudo zpool add example raidz /dev/sdf /dev/sdg /dev/sdh /dev/sdi

池的操作

还有一些管理工具,一旦你创建了你的池,你就必须使用它们来操作。 首先,检查你的池的状态。

1
sudo zpool status

当你更新 ZFS 时,你也需要更新你的池。 当您检查它们的状态时,您的池会通知您任何更新。 要更新池,请运行以下命令。

1
sudo zpool upgrade your-pool

你也可以更新全部池。

1
sudo zpool upgrade -a

您也可以随时将驱动器添加到池中。 告诉 zpool 池的名称和驱动器的位置,它会处理好一切。

1
sudo zpool add your-pool /dev/sdx

如果您想破坏池,则可以使用以下命令

1
sudo zpool destroy pool-name

ZIL (ZFS Intent Log)

ZIL 我们一般使用一个或多个硬盘,正常情况下使用ssd的磁盘,ZIL能记录所有的写操作,然后写到ZIL 的磁盘,因为zfs copy on write的机制,ZIL实际的存储信息很小, 只保存修改的数据,或者是数据块的外部指针, 假如你写入一个大的数据文件,如果设置logbias=troughput, 那么这个所有的写操作是直接到data pool的不经过ZIL, ZIL中只保存数据的指针, 所以说如果设置logbias=troughput,那么使用独立的ZIL设备是没有意义的。 如果logbias=latency,由于zfs对zil的使用有阈值限制, 例如单次提交的写超过阈值则直接写data pool, 否则会写入ZIL之后sync到data pool。这个阈值是通过设置zfs_immediate_write_sz (/etc/system文件)的大小来确定。所有的这些参数的调整要更据你的具体应用场景来确定。

ZFS 文件系统也有类似数据库的重做日志,被称为 ZFS Intent Log ,简称 ZIL, 还有个术语,被称为 Separate Intent Log, 简称 SLOG,是指存储 ZIL日志的独立的设备, 手册上提到使用 ZIL 可以提高文件系统性能,还能够用来恢复文件系统

这里如果我们不添加独立ZIL设备,实际上pool里面的数据盘上也会又ZIL的存储, 只要是ZFS sync=standard, 值得一提的是sync=always的话,所有文件系统的transaction是直接写到data pool.

同步写操作减少对整个data pool的IOPS影响, 使用ssd做ZIL可用降低同步写的延迟, 这一点非常重要,NFS/ISCSI Target/OLTP Database等都是同步写,这里会大大提升性能。

稍微总结下:

ZIL提供了断电情况下的数据保护。
在每次同步写发生的情况下避免更新整个磁盘的data structure.
大量的同步写操作会增加了整个data pool的IOPS的压力,所有需要使用独立的ZIL设备。
通过SSD作为独立ZIL设备可以加速同步写。甚至我们可以使用多个SSD将ZIL做成Mirror,防止单盘的失效。
如果你的data pool是全SSD的,又没有独立的ZIL设备,可以直接将logbias=throughput。
1
2
For example, to add a SSDs to the pool 'mypool', use: 
$ sudo zpool add mypool log /dev/sdg -f

需要注意的是存放log的硬盘若损坏有可能导致数据丢失,如果不能接受这个风险的话考虑给log盘做成mirror模式,命令如下:

1
2
3
4
5
# 备注: 给 mypool 池增加 SLOG 设备,并且 sda1 , sda2 做成镜像。 
$ sudo zpool add mypool log mirror /dev/sda1 /dev/sda2

$ sudo zpool add ftp-pool log mirror /dev/sda /dev/sdb

ZFS Cache Drives 拿 SSD 做快取

高速缓存设备在主内存和磁盘之间提供了一个额外的高速缓存层。它们对于提高主要是静态数据的随机读取性能特别有用。

1
2
3
4
5
6
7
Fox example, to add a cache drive /dev/sdh to the pool 'mypool', use:

$ sudo zpool add mypool cache /dev/sdh -f

# 通过 zpool add 加上一顆 SSD 硬盘作為快取使用 (关鍵字是 cache):
$ sudo zpool add ftp-pool cache /dev/disk/by-id/ata-INTEL_SSDSC2BB240G7_PHD12345ABC

ZFS与数据去重

什么是Deduplication?
Deduplication是消除重复数据的过程。去重过程可以基于file-level文件级,block-level块级或者byte-level字节级。使用非常高可能性的hash算法来唯一标识数据块(文件,块,字节)。当使用安全hash,例如SHA256时,hash碰撞的可能性为2的256次方,2^256 = 10\67 或者表示为0.00000000000000000000000000000000000000000000000000000000000000000000000000001。

选择哪个等级的去重,files,blocks,bytes?
数据可以在文件级,块级,字节级被去重。
文件级的去重对文件作为整体来计算hash签名,如果处理的是自然的文件,则此方法需要的消耗最小,但是缺点是对文件的任何修改都需要重新计算文件的hash签名,即对文件的任何修改,将使得此文件之前节约的空间消失,因为两个文件将不再相同。此中方法比较适合类似于文件JPEG,MPEG,但是对于像虚拟机镜像(大文件)文件将无效,因为即使他们只是很小的一部分不同,但是文件级别他们将是不同的文件。
块级别的去重(相同大小的块),相比文件级的去重,需要更多的计算消耗,但是他能够很好地对像虚拟机镜像类似的大文件去重。大部分的虚拟机镜像文件是重复数据,例如镜像文件中的操作系统部分。使用块级的去重将使得只有镜像特别的数据才占用额外的空间,相同的数据将共享。
字节级别的去重,将需要更多的计算消耗来决定重复数据的开始和结束区域,不管怎么说,此方法对mail服务器来说是理想的选择,例如一个邮件的附件可能出现很多次,但是使用块级别去重时他们并不能被优化。此类型的去重一般用来一些应用程序的去重中,例如exchangeserver,因为应用程序知道他管理的数据,可以在内部容易地去重。
ZFS提供了块级别的去重技术,此种技术更适合通用的情况。且ZFS使用SHA256来计算hash签名。

什么时候去重,现在还是将来?
除了以上描述的文件级,块级,字节级的区别外,去重还可以分为同步(实时或内置)和异步(批处理或离线处理)。在同步去重中,重复文件在出现的时候即被去除,在异步去重中,文件已经存储在磁盘上,然后通过后续的处理来去重(例如在夜间来处理)。异步去重典型地被用在拥有有限的cpu和多线程的存储系统,最小化对日常工作的影响。但是如果cpu的性能足够,同步去重是推荐的方法,因为避免了无用的磁盘的写操作。

如何使用ZFS的去重

使用非常的简单,如果你有存储池tank,你需要对tank使用zfs,则设置为:

1
2
3
4
zfs set dedup=on tank

zfs set dedup=on local
zfs set compress=lz4 local

是否需要ZFS的去重的权衡

主要还是取决于你的数据。如果你的数据确实不包含重复,则开启去重功能则会带来额外的开销且没有任何的好处。但是如果你的数据包含重复,则使用zfs的去重可以节约空间而且提高性能。节约空间是显而易见的,性能的提高是因为减少了重复数据的写磁盘消耗和内存页的置换。

大部分的存储环境都是多种数据的混合,zfs支持对有重复的部分开启去重操作,例如你的存储池包含了home目录,虚拟机镜像目录,源代码目录。此时你可以设置如下:

1
2
3
4
5
zfs set dedup=off tank/home

zfs set dedup=on tank/vm

zfs set dedup=on tank/src

信任或验证

如果两个数据的hash相同,我们则认为两个数据是同一个数据,hash例如SHA256,两个不同数据hash相同的可能性为1/2^256, 是个很小的概率。当然zfs也提供了验证的选项,此选项对两个数据进行全比较来确定他们是否相同,如果不相同则处理,指定verify的语法如下:

1
zfs set dedup=verify tank

hash的选择

因为不同种类的hash所需要的运算也不相同,一种推荐的方法是使用较弱的hash(需要的运算较少)加verify来提供快速的去重,zfs对应的选项为:

1
zfs set dedup=fletcher4,verify tank

不像SHA256,fletcher4不能被信任为无碰撞,只适合与verify一起使用,verify来确保碰撞的处理。总的来说性能相对要好些。

通常如果不确定哪种的hash的效率跟高的话,即使用默认的设置dedup=on

扩展性和效率

大部分的去重方案都只能针对有限的数据,一般在TB等级,因为他们需要去重数据表常驻在内存。ZFS对数据大大小没有限制,可以处理PB级的数据。但是如果去重数据表常驻内存的话,性能比较好。

ZFS与数据压缩

什么是ZFS压缩?
ZFS中的压缩是一个非常简洁的特性:它可以动态压缩文件,因此可以使用有限的存储空间存储更多的数据。

为什么使用ZFS压缩?
显然,ZFS压缩的最大好处是可以节省相当多的空间。你的服务器或桌面上的大文件可能会压缩得很好,那么为什么要浪费空间呢?

以下是一些非常适合ZFS压缩场景的文件:
新OS版本的ISO映像(不知道您的情况,但我几乎每天都下载)
VirtualBox、KVM或任何其他解决方案的VM映像
Docker容器镜像
大量应用程序或服务器日志

为什么禁用压缩?
像所有好东西一样,ZFS压缩也有成本,特别是压缩和解压缩ZFS数据需要额外的CPU周期。我对这一点的看法是,在桌面系统上,ZFS压缩绝对是一个很好的主意-您很少会将CPU的最大化扩展到ZFS压缩可以注意到的程度,但优化宝贵的SSD存储空间的好处是巨大的。
在较小的系统上,比如我的一些用于远程RSyslog的嵌入式服务器,这可能是一个破坏者:进入的日志数量可能会高到超出舒适水平的CPU使用率峰值。

如何检查ZFS压缩

1
2
3
4
5
6
7
8
9
10
11

$ zfs get all work/share/sean |grep compress
work/share/sean compressratio 4.32x -
work/share/sean compression lz4 inherited from work/share
work/share/sean refcompressratio 4.32x

# 下面是没有开启压缩功能的显示结果
$ zfs get all newvol | grep compress
work compressratio 1.00x -
work compression off local
work refcompressratio 1.00x -

如何开启ZFS压缩?
ZFS有多种压缩算法:
gzip - standard UNIX compression.
gzip-N - selects a specific gzip level. gzip-1 provides the fastest gzip compression. gzip-9 provides the best data compression. gzip-6 is the default.
lz4 - provides better compression with lower CPU overhead 这种是比较通用,常用的。
lzjb - optimized for performance while providing decent compression
zle - zero length encoding is useful for datasets with large blocks of zeros

1
2
3
4
5
6
# 比较常用的 是 lz4 这个,

$ zfs set compression=lz4 newvol

$ zfs set compress=lz4 pool/fs

如何禁用ZFS压缩?

1
2
root@server:~ # zfs set compression=off newvol

压缩只对新生成的数据有效。
一件非常重要的事情:当您启用或禁用ZFS压缩时,实际上并没有更改ZFS文件系统上的任何数据。
这些更改ZFS的行为,意味着将来的任何数据会生效,但当前的数据将保持原样。

其他补充

下面坐了3组对比,来观察 去重,和压缩 打开关闭的效果。
这个是打开了 dedup=on 去重复数据的开关的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$ zpool list
NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT
work 10.9T 119M 10.9T - - 0% 0% 101.00x ONLINE -

$ sudo zdb -b work

Traversing all blocks to verify nothing leaked ...

loading concrete vdev 1, metaslab 348 of 349 ...

No leaks (block sum matches space maps exactly)

bp count: 59858
ganged count: 0
bp logical: 7825691648 avg: 130737
bp physical: 7813446656 avg: 130533 compression: 1.00
bp allocated: 11724856320 avg: 195877 compression: 0.67
bp deduped: 11599872000 ref>1: 590 deduplication: 1.99
Normal class: 124984320 used: 0.00%

additional, non-pointer bps of type 0: 32
Dittoed blocks on same vdev: 114


这个是没有打开 dedup=on 去重复数据的开关的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ zpool list
NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT
work 10.9T 7.28G 10.9T - - 0% 0% 1.00x ONLINE -

$ sudo zdb -b work

Traversing all blocks to verify nothing leaked ...

loading concrete vdev 5, metaslab 115 of 116 ...

No leaks (block sum matches space maps exactly)

bp count: 59830
ganged count: 0
bp logical: 7825523712 avg: 130795
bp physical: 7813270528 avg: 130591 compression: 1.00
bp allocated: 7816024064 avg: 130637 compression: 1.00
bp deduped: 0 ref>1: 0 deduplication: 1.00
Normal class: 7816024064 used: 0.07%

additional, non-pointer bps of type 0: 40

打开去重和压缩的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ zpool list
NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT
work 10.9T 15.3M 10.9T - - 0% 0% 101.00x ONLINE -

$ sudo zdb -b work

Traversing all blocks to verify nothing leaked ...

loading concrete vdev 5, metaslab 115 of 116 ...

No leaks (block sum matches space maps exactly)

bp count: 59899
ganged count: 0
bp logical: 7825843200 avg: 130650
bp physical: 948596736 avg: 15836 compression: 8.25
bp allocated: 951911424 avg: 15891 compression: 8.22
bp deduped: 936140800 ref>1: 590 deduplication: 1.98
Normal class: 15770624 used: 0.00%

additional, non-pointer bps of type 0: 40

销毁池

1
sudo zpool destroy work

6个硬盘,3个组成raid5,然后在2组组成raid0了。可用空间大概是4个2TB。

1
2
3
sudo zpool create work raidz1 /dev/sd{b,c,d}1
sudo zpool add -f work raidz1 /dev/sd{e,f,g}1
work 7.1T 7.3G 7.1T 1% /work

6个硬盘,直接组成raid0,

1
2
sudo zpool create work /dev/sd{b,c,d,e,f,g}1
work 11T 128K 11T 1% /work

打开去重和压缩功能

1
2
$ sudo zfs set dedup=on work
$ sudo zfs set compression=lz4 work