迷彩 潜伏 隐蔽 伪装
分类: 服务器与存储
2014-04-17 13:21:22
原文地址:ZFS – 数据去重(Data Deduplication) 作者:nnusun
数据去重,简单地说就是重复数据删除。从某种意义上说也是一种数据压缩技术。
数据去重由于其在网络以及节省磁盘方面的绝对优势,已经被广泛应用到存储、网盘、云服务、邮件服务等方面。
分类标准 | 去重时间 | 去重粒度 | 分块方式 | 数据处理位置 |
分类结果 | 在线/离线去重 | 文件级别/块级别 | 定长块/边长块 | 源端/目的端 |
去重流程图
ZFS作为一个整合了卷管理系统以及文件系统的新型文件系统,也提供了去重功能。由于ZFS在存储数据时按照数据块处理,自然是按照块级别的去重粒度来设计。总的来说,ZFS的去重是使用定长块级别的在线去重方式。
由于使用去重功能会消耗较多内存和CPU,一定意义上会影响系统的性能,所以ZFS默认情况下是关闭这个功能的,由用户根据自己系统的使用情况决定是否开启。开启的方法也很简单:
# zfs set dedup=on filesystemname(文件系统名称)
或者可以指定指纹的计算算法:
# zfs set dedup=fletcher4 filesystemname(文件系统名称)
既然会影响性能,我们需要考虑以下两点来判断我们是否需要开启这项功能:系统的内数据的重复率是否达到需要去重的必要?系统是否有足够的内存来满足去重的标准?如果都满足,则可以开启去重功能。
# zdb -S tank
Simulated DDT histogram:
bucket allocated referenced
______ ______________________________ ______________________________
refcnt blocks LSIZE PSIZE DSIZE blocks LSIZE PSIZE DSIZE
------ ------ ----- ----- ----- ------ ----- ----- -----
1 27.1K 3.39G 3.39G 3.39G 27.1K 3.39G 3.39G 3.39G
2 35 4.38M 4.38M 4.38M 76 9.50M 9.50M 9.50M
4 1 128K 128K 128K 4 512K 512K 512K
32K 1 128K 128K 128K 46.8K 5.85G 5.85G 5.85G
Total 27.1K 3.39G 3.39G 3.39G 74.0K 9.25G 9.25G 9.25G
dedup = 2.73, compress = 1.00, copies = 1.00, dedup * compress / copies = 2.73
如果输出结果中的dedup值大于2,则可以考虑开启去重,小于2的话,说明数据重复率不高,没有必要开启。
每个DDT( Deduplication Data Table )在村村中大约占用320字节,将已经分配的block数目 * 320Byte 看内存是否满足。
当然,由于ZFS采用的是存储池管理文件系统的方式,我们可以对特定的文件系统设置去重功能,比如只开启home文件系统或是存放Software的文件系统的去重功能。
ZFS通过DDT(Dedup Data Table)的形式来实现去重功能。
ZFS通过dde_type枚举类型定义了DDT在磁盘上的存储方式(目前仅有一种存储方式:ZAP方式)
同时,定义了DDT的操作数组,便于对不同的DDT磁盘存储类型采用不同的操作方式,其中包括了DDT的创建、销毁、查找、预读取、更新、删除、遍历、统计等:由于C语言不存在面向对象的"类"的概念,所以通过下面这个结构体的定义,使得C语言也能够实现对于不同的DDT存储类型执行不同的操作。某种意义上实现了面向对象的功能。
点击(此处)折叠或打开
每个去重文件系统中的数据块都有对应的指纹,在磁盘上存储时对应一下ddt_key结构体:
点击(此处)折叠或打开
ddt_key中的ddk_prop为64位的整数,其中包含了数据块的压缩算法,占用的物理空间大小以及逻辑空间大小,每个位的具体分配如下:
+-------+-------+-------+-------+-------+-------+-------+-------+
| 0 | 0 | 0 | comp | PSIZE | LSIZE |
+-------+-------+-------+-------+-------+-------+-------+-------+
同时,ZFS也提供了一系列的宏定义来获取、设定comp、PSIZE、LSIZE的值。
存储时,通过ddt_object结构体来表示,其中包含了这个DDT的个数以及占用的磁盘空间和内存空间的大小:
点击(此处)折叠或打开
对于开启的文件系统来说,一个数据块可能同时被多个文件引用。如果这些数据块被损坏,那么影响的可能就不是一个文件那么简单了,它将会导致文件系统中多个文件不可用,这样的后果是很严重的,所以ZFS针对数据块被引用的次数的多少,提供不同份数的存储:引用次数多的将提供多个备份。备份数目通过以下变量定义:
点击(此处)折叠或打开
另外,ZFS也对DDT保留详细的统计信息,统计信息的结构体如下面两个结构体所示,单个的统计信息中包含统计的Block块的数目,实际磁盘存储空间大小(dds_lsize,dds_psize,dds_dsize)以及如果不使用去重应该占用的空间大小(dds_ref_blocks,dds_ref_psize,dds_ref_dsize)。
点击(此处)折叠或打开
在实际操作时,统计信息被按照引用计数的多少分成64类(通过ddt_histogram定义)。比如,引用计数是一个64位的整数,该整数的最高非0位是第N位,那么,这个统计信息就将被放到ddt_histogram对应的ddh_stat[N]中。比如对于前文提到的"zdb -S tank"的结果可以看出,被引用1次的block数目有27.1 * 1024个,被引用超过32×1024次的block有1个。接下来,我们将同样tank文件系统中的file1文件复制一份,将会得到下面的结果,由于file1文件是通过makefile命令创建出来的3.0G大小的文件(其中的内容可能是相同字符的填充),可以发现,dedup因子由2.73变成了3.61,重复率大大增加。
# pwd
/tank
# cp file1 file3
root@1dom_sun1# zdb -S tank
Simulated DDT histogram:
bucket allocated referenced
______ ______________________________ ______________________________
refcnt blocks LSIZE PSIZE DSIZE blocks LSIZE PSIZE DSIZE
------ ------ ----- ----- ----- ------ ----- ----- -----
1 26.2K 3.28G 3.28G 3.28G 26.2K 3.28G 3.28G 3.28G
2 914 114M 114M 114M 1.79K 228M 228M 228M
4 13 1.62M 1.62M 1.62M 64 8M 8M 8M
8 1 128K 128K 128K 8 1M 1M 1M
64K 1 128K 128K 128K 69.8K 8.73G 8.73G 8.73G
Total 27.1K 3.39G 3.39G 3.39G 97.9K 12.2G 12.2G 12.2G
dedup = 3.61, compress = 1.00, copies = 1.00, dedup * compress / copies = 3.61
在内存中,DDT通过下面的结构体来表示,其中包含了两棵AVL树(树中的每个节点都是ddt_entry),DDT的指纹算法,统计信息/DDT存储的对象编号等等。
点击(此处)折叠或打开
AVL树中的节点对应的ddt_entry定义如下:
点击(此处)折叠或打开
OK,至此,我们说明了一下ZFS去重功能在磁盘以及内存中涉及的一些结构体/枚举类型的定义,接下来说一下去重过程中的两个函数操作:
int ddt_ditto_copies_needed(ddt_t *ddt, ddt_entry_t *dde, ddt_phys_t *ddp_willref)
前文已经说到了,去重之后的数据块可能跟多个文件相关联,如果这样的数据块发生损坏将可能影响到多个文件。通过这个函数,ZFS设置了一定的规则,用以计算特定的数据块所需存储的份数。
zio_ddt_write函数流程
ZFS通过存储池管理文件系统,这样我们更加方便对不同的文件系统设定去重属性。对去重功能的合理使用不仅在可以节约磁盘空间,也可以节省内存的使用(原本需要在内存中缓存多个数据块,由于去重的功效,可能只需要存储一个数据块,从而节省内存空间)。
本文乃nnusun原创,请勿转载,如需转载请详细标明出处