Chinaunix首页 | 论坛 | 博客
  • 博客访问: 164319
  • 博文数量: 17
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 342
  • 用 户 组: 普通用户
  • 注册时间: 2014-03-19 11:38
个人简介

A ZFS fan

文章分类
文章存档

2014年(17)

分类: 服务器与存储

2014-04-18 23:26:04

对于很多的应用环境下,文件系统中数据完整性(Data Integrity)的重要性甚至高于文件系统的读写性能。因此,目前的文件系统或是存储系统都在数据完整性方面下了一番功夫。使用Checksum就是提高数据完整性的一种方式。

1. CheckSum数据校验


1.1 一般的Checksum校验方式

对于一般的文件系统来说,一个数据块的Checksum值是和对应的数据存放在一起的(如下图所示),在读取数据的同时就包含了对应数据的Checksum值,以此来检查数据是否有错误。

Normal_Filesystem_Checksum

一般文件系统Checksum方式

这样的checksum方式可以检测到数据块中某个位发生错误。但是这样的校验方式存在很多问题,比如无法检测到静默数据错误(Silent Data Corruption)。这种错误的发生一般是来自硬件本省,比如磁盘损坏,硬件驱动错误,RAID卡损坏等等。举一个形象点得例子,假如我们在网上买了件瓷器,卖家将完整的瓷器包装好,交给快递公司,快递公司之后将瓷器原封不动地交给我们手中时,这里面的瓷器一定是完好无损的?显然是不一定的,说不定运输过程中给颠碎了。同样磁盘也有这样的问题,这些问题虽然难以被检测到,但是这些问题确实经常发生,严重影响了数据的完整性。在ZFS文件系统之前,还没有文件系统能够处理好这些问题。而ZFS在设计时就将这一切考虑到了,我们来看一下ZFS是ZFS通过Checksum来实现完整性校验的。

1.2 ZFS的Checksum校验方式

在ZFS文件系统中,数据的Checksum值并不是与数据存储在一起的,而是将数据的Checksum值存储在指向它的块指针中(如下图所示)。这样数据块的损坏并不会影响到Checksum,这样ZFS就维护了这样一棵树,每个节点都包含了它的子节点的Checksum值。(这里留下一个问题,这棵树的根是没有父节点的,它怎么校验?)。通过这棵树,ZFS可以完成自检工作,在自上而下的自检过程中,由于上面的Checksum值是经过验证的的,所以如果下面的数据发生错误,将很容易被检测出来。

ZFS ChecksumType

ZFS Checksum存储方式

1.3 ZFS与ext4文件系统数据校验对比实例


下面,我们将通过一个实例来看看,如果我们偷偷修改了磁盘上的数据,ZFS和ext4文件系统将会有怎样的反应。

实验OS:Ubuntu 12.04(启动磁盘文件系统为ZFS)。


实验的主要流程:

1. 通过分别在ext4文件系统和ZFS文件系统中分别放置一个相同的文件allocator.txt。

2. 通过文件的在磁盘中的信息,找到第一个块位置(N),以这个blcok位界限,把整个磁盘分成三部分,N之前的部分,N,N之后的部分

3. 通过dd命令将三个部分分别独处放到三个不同的文件中

4. 修改分开后的N的文件,也就是文件的开头所在的block

5. 将三部分重新整合,重新挂载文件系统,看文件是否已经被修改过,文件系统能否发现文件已经被修改了。

 

首先,我们创建两个文件作为磁盘,分别在两个磁盘上创建ZFS文件系统和ext4文件系统。

# dd if=/dev/zero of=forzfsdiskfile bs=1024k count=100

# dd if=/dev/zero of=forextdiskfile bs=1024k count=100

# ls -lh

total 173M

-rw-r--r-- 1 root root 100M Apr 16 21:43 forextdiskfile

-rw-r--r-- 1 root root 100M Apr 16 21:42 forzfsdiskfile

# mkfs.ext4

mkfs.ext4     mkfs.ext4dev  

# mkfs.ext4 -b 4096 forextdiskfile

(省略创建过程中的输出)

# mount -oloop forextdiskfile ext4mountpoint/

# zpool create poolfortest /root/forzfsdiskfile

zfs create poolfortest/testpool



其次,我们分别放两个同样的文件到这两个新建的文件系统中。我们放入的文件是ZFS的首席设计师Jeff Bonwick之前提出的一篇大作:An Object-Caching Kernel Memory Allocator

# debugfs forextdiskfile

 debugfs 1.42 (29-Nov-2011)

debugfs:  stats

...

Directory Hash Seed:      ea28afd5-8b52-4023-8957-f29217db095f

Journal backup:           inode blocks

Directories:              2

 Group  0: block bitmap at 8, inode bitmap at 24, inode table at 40

           23760 free blocks, 25589 free inodes, 2 used directories, 25589 unused inodes

      !!!放置文件之前,空闲的block数目位23760个 !!!

           [Checksum 0x8aed]

# du -sh ext4mountpoint/allocator.txt 

52Kext4mountpoint/allocator.txt

 


使用debugfs命令查看一下ext4文件系统中allocator.txt文件的相关信息

# debugfs forextdiskfile

 debugfs 1.42 (29-Nov-2011)

debugfs:  stats

...

Journal backup:           inode blocks

Directories:              2

 Group  0: block bitmap at 8, inode bitmap at 24, inode table at 40

           23747 free blocks, 25588 free inodes, 2 used directories, 25587 unused inodes

      !!!放置文件之后,空闲的block数目为23747个 !!!

           [Checksum 0x3bdb]

debugfs:  ls


 2  (12) .    2  (12) ..    11  (20) lost+found    13  (4052) allocator.txt   

debugfs:  ncheck 13

InodePathname

13//allocator.txt

。。。

EXTENTS:

(0-12):24592-24604  <— 注意这个值,它表明了这个文件所处的数据块。

 
我们来看一下,allocator.txt文件的前几行信息:
---------------------------------------------------------------------------------------------------------------------
 
                             The Slab Allocator:
                  An Object-Caching Kernel Memory Allocator
 
                                 Jeff Bonwick
                               Sun Microsystems
 
                                   Abstract
 
This paper presents a comprehensive design overview of the SunOS 5.4 kernel
---------------------------------------------------------------------------------------------------------------------
由于我们在创建ext4文件系统时候就已经确定了,该文件系统的Block大小为4096 Bytes。同时我们注意到,在放置文件之前,文件系统的空闲block数目是:23760,放置之后空闲block数目是23747。也就是说,allocator.txt文件一共占用了13个block。同时它对应的inode为13。通过debugfs我们也可以看到,它的13个block位置:(0-12):24592-24604。


下面,将把forextdisk分成三个部分,修改文件,然后重组,确认:

# dd if=forextdiskfile of=/tmp/forextdisk_part1 count=24592 bs=4096

# dd if=forextdiskfile of=/tmp/extfirstblock bs=4096 skip=24592 count=1

# dd if=forextdiskfile of=/tmp/forextdisk_part2 skip=24593 bs=4096

# vi /tmp/extfirstblock  <— 这里修改了allocator.txt文件的一些信息,把“An Object-Caching” 改成 “An Object-Cachedd”,这里需要注意的是,修改这样的文件需要保持大小(字符多少)不变。

# dd if=/tmp/extfirstblock of=/tmp/extfirstblock2 ibs=4096 obs=4095 count=1

# 以下开始合并磁盘文件

# cat /tmp/extfirstblock2 >> /tmp/forextdisk_part1 

# cat /tmp/forextdisk_part2 >> /tmp/forextdisk_part1 

 

# cp -pRf /tmp/forextdisk_part1 forextdiskfile、

# 一下开始重新挂载文件系统

# fsck.ext4

fsck.ext4     fsck.ext4dev  

# fsck.ext4

fsck.ext4     fsck.ext4dev  

# fsck.ext4 -v forextdiskfile 

e2fsck 1.42 (29-Nov-2011)

forextdiskfile: clean, 12/25600 files, 1853/25600 blocks

 

# mount -o loop forextdiskfile ext4mountpoint/

# 这里你可以通过debugfs命令查看forextdiskfile磁盘文件中allocator.txt文件的信息,其结果与之前看到的相同。这是我们再来看allocator.txt的前几行

# head ext4mountpoint/allocator.txt 

 

                             The Slab Allocator:

                  An Object-Cachedd Kernel Memory Allocator

                            这里已经不是“Caching“了,但是文件系统什么也没发现。

                                 Jeff Bonwick

                               Sun Microsystems

 

                                   Abstract

 

 

This paper presents a comprehensive design overview of the SunOS 5.4 kernel


ext4对于我们偷偷修改数据的行为一点也不知情,接下来,我们看看ZFS有什么表现:

# ls -i /poolfortest/testpool/

7 allocator.txt

zdb -dddddd  poolfortest/testpool 7

Dataset poolfortest/testpool [ZPL], ID 40, cr_txg 697, 81.5K, 7 objects, rootbp DVA[0]=<0:48400:200> DVA[1]=<0:122e800:200> 。。。

Indirect blocks:

               0 L0 0:32e00:ce00 ce00L/ce00P F=1 B=702/702


segment [0000000000000000, 000000000000ce00) size 51.5K

# 从这里我们可以看出,整个文件放在一个连续的空间内,位于pool中的0号磁盘,0x32e00偏移处,大小为0xce00;其次,从DVA中的<0:48400:200>可以判断Block大小为0x200,即512字节。

# 那么我们可以计算allocator文件的第一个Block的位置。(0x400000 + 0x32e00)/ 512 = 8599

# dd if=forzfsdiskfile of=/tmp/zfsdiskfile_part1 bs=512 count=8599

# dd if=forzfsdiskfile of=/tmp/zfsheadfile bs=512 skip=8599 count=10

# dd if=forzfsdiskfile of=/tmp/zfsdiskfile_part2 bs=512 skip=8609 

# vi /tmp/zfsheadfile <— 这里修改文件的内容,跟ext4实验时一样,将”Caching”改成“Cachedd”。

# 以下开始,我们将修改过的三部分文件重新整合到一起

# cp -pRf /tmp/zfsdiskfile_part1 /root/forzfsdiskfile 

# dd if=/tmp/zfsheadfile of=/tmp/zfsheadfile_changed bs=5120 count=1

# cat /tmp/zfsheadfile_changed >> /root/forzfsdiskfile

# cat /tmp/zfsdiskfile_part2 >> /root/forzfsdiskfile

# zpool import -d /root/ poolfortest

# zpool status poolfortest

  pool: poolfortest

 state: ONLINE

  scan: none requested

config:

 

NAME                    STATE     READ WRITE CKSUM

poolfortest             ONLINE       0     0     0 <— 这里貌似一切OK

  /root/forzfsdiskfile  ONLINE       0     0     0

 

errors: No known data errors

# 我们来看看文件 

# ls /poolfortest/testpool

allocator.txt <— 文件也好好地存在着;然后我们访问一下看看

# head /poolfortest/testpool/allocator.txt 

head: error reading `/poolfortest/testpool/allocator.txt': Input/output error <— 终于出错了,文件访问出错,这时再来看一下Pool的状态

# zpool status -v

  pool: poolfortest

 state: ONLINE

status: One or more devices has experienced an error resulting in data

corruption.  Applications may be affected.

action: Restore the file in question if possible.  Otherwise restore the

entire pool from backup.

   see:

  scan: none requested

config:

 

NAME                    STATE     READ WRITE CKSUM

poolfortest             ONLINE       0     0     2   <— Checksum显示有问题了。

  /root/forzfsdiskfile  ONLINE       0     0     4

 

errors: Permanent errors have been detected in the following files: <— 同时,也给出了发生问题的文件

 

 

        /poolfortest/testpool/allocator.txt


操作过程中,我们有一个计算Block的公式:(0x400000 + 0x32e00)/ 512 = 8599,这里的0x400000,表示了,磁盘的最前面4M空间(保留空间+Vdev Label),如果有疑问的话,可以看一下我前面两篇博客: ZFS - Ondiskformat 第一章 虚拟设备(vdevs),Vdev Label以及Boot BlockZFS的校验算法,这里面说明了前面4M的详细分配情况, ZFS - Ondiskformat 第二章 块指针、间接块,这里面有说明如何计算偏移地址(2.1节)。

OK,到这里,我们通过ext4与ZFS两种文件系统在应对静默数据损坏时的效果,可以看出ZFS的完美地发现了我们修改数据的事情。需要说明的是,我们实验的时候使用的是条带化的磁盘,如果我们创建pool的时候使用mirror的话,ZFS将会自动从有正确数据的磁盘上帮我们修复错误数据,这个过程无需我们手动干预。

其实ZFS在保证数据完整性方面不仅仅体现在这一个方面。下面我们在给出一些介绍:

2. 其它实现数据完整性方式


2.1 元数据的保护

元数据在文件系统中是十分重要的,一个元数据块的损坏可能导致多块数据无法访问。因此,ZFS采用多份元数据的方式来保护它们。在 ZFS - Ondiskformat 第二章 块指针、间接块这篇博客中我们可以知道,ZFS的块指针中有三个DVA(数据虚拟地址),对于普通的数据块,ZFS只使用一个DVA,也就是说数据只存储一份,对于普通的元数据,ZFS使用两个DVA,这样的元数据将会在磁盘中有相同的两个备份,对于更重要的元数据,比如文件系统的根,则使用三个DVA,这样的数据将被存储三份。

2.2 Mirror与RAID

ZFS是一个整合了文件系统与卷管理系统的新型文件系统,所以ZFS也提供了Mirror和RAID功能。这里需要的是,如果同时使用了硬件的RAID,则可能会与ZFS的RAID相冲突,这将导致ZFS将只会检测数据的完整性,而并不会自动修复错误的数据。

如果我们只有一块磁盘,而且我们创建pool时也没有做RAID或是mirror,我们也可以让ZFS为我们的每个数据存储多份来保护我们的数据。这个只要设置文件系统的copies属性就好了,但是如果设置了copies属性为2,那么磁盘空间也会相应地减半(因为我们每个数据都存储了两次),依次类推。


3. ZIO流水线中的Checksum的实现

点击(此处)折叠或打开

  1. zio_checksum_info_t zio_checksum_table[ZIO_CHECKSUM_FUNCTIONS] = {
  2.     {{NULL,            NULL},            0, 0, 0, "inherit"},
  3.     {{NULL,            NULL},            0, 0, 0, "on"},
  4.     {{zio_checksum_off,    zio_checksum_off},    0, 0, 0, "off"},
  5.     {{zio_checksum_SHA256,    zio_checksum_SHA256},    1, 1, 0, "label"},
  6.     {{zio_checksum_SHA256,    zio_checksum_SHA256},    1, 1, 0, "gang_header"},
  7.     {{fletcher_2_native,    fletcher_2_byteswap},    0, 1, 0, "zilog"},
  8.     {{fletcher_2_native,    fletcher_2_byteswap},    0, 0, 0, "fletcher2"},
  9.     {{fletcher_4_native,    fletcher_4_byteswap},    1, 0, 0, "fletcher4"},
  10.     {{zio_checksum_SHA256,    zio_checksum_SHA256},    1, 0, 1, "sha256"},
  11.     {{fletcher_4_native,    fletcher_4_byteswap},    0, 1, 0, "zilog2"},
  12.     {{zio_checksum_off,    zio_checksum_off},    0, 0, 0, "noparity"},
  13. };

  14. typedef struct zio_checksum_info {
  15.     zio_checksum_t    *ci_func[2]; /* checksum function for each byteorder */
  16.     int        ci_correctable;    /* number of correctable bits    */
  17.     int        ci_eck;        /* uses zio embedded checksum? */
  18.     int        ci_dedup;    /* strong enough for dedup? */
  19.     char        *ci_name;    /* descriptive name */
  20. } zio_checksum_info_t

通过这两个变量的定义,在ZIO流水线中,我们使用一个整数来代表Checksum算法,通过这个整数在上面的zio_checksum_table中查找,直接就可以调用该checksum的计算算法,对数据计算对应的checksum值。

对于ZFS的Checksum设计,源码中还给出了以下的几个原因:

1) 不同类型的数据需要不同强度的Checksum值,比如SPA的元数据就需要高强度的Checksum算法,而对于用户数据,则可以由用户来在速度和强度之间进行权衡选择。

2) 加密用的算法是在不断的发展的,说不定未来的某一天会出现一种更强但是计算速度同样更快的算法,通过这种方式就很容易将该算法加入到代码中。

3) 如果有人设计了速度更快的硬件,我们也可以迅速利用那样的硬件。

 

4. 总结

ZFS是一种新型的文件系统,它在保证数据完整性方面做足了功夫以保护用户的数据。很多的设计都具有颠覆性的意义,比如将RAID,Mirror整合到文件系统中等等。这一切让我们更加放心地使用ZFS。


阅读(5024) | 评论(1) | 转发(0) |
给主人留下些什么吧!~~

nnusun2014-04-20 12:45:54

关于文中的示例可以参考ZFS Internals(Google一下就好了),这里面使用了较多的例子将ZFS与ext3进行了比较