ZFS文件系统在设计上与传统的文件系统有很大的不同。我们需要对ZFS的几个基本概念有所了解:
Record Size
Record Size 也就是通常所说的文件系统的block size。ZFS采用的是动态的Record Size,也就是说ZFS会根据文件大小选择*适合*的512字节的整数倍作为存储的块大小,最大的RecordSize为128KB。通常没有必要手工设置Record Size。如果手工把Record Size调小,将不会得到空间的节省,并且常常会影响性能。ZFS的Block是计算checksum的单位,一个文件用到的block越多,计算checksum以及维护metadata的开销就会越大。
目前ZFS的版本下,只有一种情况需要手工调小Record Size:大文件上的小数据量的更新。常见的情况是数据库的数据文件在ZFS上,并且是做OLTP为主的应用。原因是大文件的blocksize是128KB,如果只更新其中一小部分数据,由于ZFS是copy-on-write的方式来更新数据,这种情况下仍然要读写128KB的数据,造成了不必要的开销。另外,修改了Record Size后,对之前创建的文件不起作用。
ARC (Adaptive Replacement Cache)
这实际上是ZFS的文件系统的cache。它是一个可以自动伸缩的cache。目前一个操作系统实例里只有一个ARC,不管这个操作系统上有多少个ZFS Pool。而且metadata和实际的数据都共用这个ARC。
如果没有其它应用或操作系统本身争用内存,ZFS会尽可能多地使用物理内存作为cache。如果事先知道应用程序或操作系统需要使用多少内存,可以考虑限制ARC的大小。在/etc/system中设置,
例子:set zfs:zfs_arc_max=0x200000000 (设置ARC最大为8GB)
还可以使用如下工具来监测ARC的使用情况。
bash-3.00# ./zfs_arcstat.pl
Time read miss miss% dmis dm% pmis pm% mmis mm% arcsz c
14:41:03 10M 2M 21 2M 21 0 0 97K 77 2G 2G
14:41:04 890 228 25 228 25 0 0 8 100 2G 2G
14:41:05 1K 273 25 273 25 0 0 7 100 2G 2G
14:41:06 880 221 25 221 25 0 0 5 100 2G 2G
14:41:07 1K 272 25 272 25 0 0 10 52 2G 2G
14:41:08 1K 244 24 244 24 0 0 4 100 2G 2G
14:41:09 1K 294 25 294 25 0 0 10 100 2G 2G
14:41:10 1K 258 25 258 25 0 0 6 66 2G 2G
各列的含义,在这个Perl脚本里有描述。该工具的下载地址是:
http://www.solarisinternals.com/wiki/index.php/Arcstat
TXG (Transaction Groups)
应用的文件写操作一般分为两种,同步写和普通写操作。同步写是指写操作必须物理地写到存储介质上才能返回给应用,通常这种情况发生在open文件时指定了O_DSYNC或O_SYNC属性,以及调用fsync()等;普通写操作是指写操作写到文件系统cache里即可返回给应用,通常以普通方式打开文件后的write(),fwrite()操作都是这种操作。TXG (Transaction Groups)则是负责管理如何将文件系统cache(ARC)里的数据同步到磁盘上的机制。
对于写进文件系统cache里的每一个数据都会被编入一个TXG,通常情况下每隔5秒钟或文件系统cache里有太多脏数据(待写入磁盘的数据达到ARC的一半),当前TXG里的所有数据会被同步到磁盘,同时启动下一个TXG。每一个ZFS Pool同时最多会有3个TXG,分别处于3种状态:open, quiescing, syncing。当TXG不能在5秒内把数据同步到磁盘上时将会影响open TXG,从而引起应用写操作的阻塞。这个现象叫做写抑制(throttling)。
当前的Solaris 版本(Solaris 10 8/07)对写抑制的控制不是非常好,当进行密集的写操作并且ARC较大、磁盘系统较慢时容易使TXG同步数据的时间非常长,发生throttling从而使应用的后续写操作的响应时间非常长。在新的opensolaris发行包里,这个问题已经有了很大的改进,ZFS会监测ARC接收数据的速度和ARC数据同步到磁盘上的速度,并自动调整ARC接收数据的速度。另外ARC脏数据的大小限制改为1/8物理内存。通过这种控制可以使应用的写操作响应时间更为均匀稳定。
ZIL (ZFS Intent Log)
对于同步写操作,ZFS为系统中每一个ZFS文件系统维护一个ZIL(ZFS Intent log)。同步写操作的数据会先写入ZIL,并且会把磁盘的write cache的数据同步到磁盘上,然后应用的写操作返回。当文件提交命令发生时,ZFS会把ZIL里该文件的数据同步到磁盘上。
缺省情况下,ZIL是在ZFS存储池中动态分配的。某些同步写操作(比如数据库的联机重做日志或NFS COMMIT)如果响应时间慢将会极大影响性能。为了不使ZIL受其它I/O操作的影响,可以考虑采用一个专门的快速设备如NVRAM,SSD(Solid State Disk)等做ZIL,以下命令为ZFS存储池添加专用的ZIL:
zpool add <pool> log <log devices>
另外,由于每次同步写操作会使ZFS把磁盘 write cache的数据同步到磁盘上,对于SAN的存储,频繁地flush cache会严重影响性能并且是不必要的。这个问题在OpenSolaris里已经得到解决,解决办法是让设备驱动程序向存储系统发出适当的命令选项,从而使存储系统可以忽略从non-volatile cache同步到磁盘的请求。前提是存储系统可以识别驱动程序发出的命令选项。
I/O scheduler and priorities
缺省情况下,ZFS会限制每一个它能看到的存储池里的设备的I/O队列长度为35,并且通常读操作的优先级高于写操作。这种做法的目的是保证较好的I/O service time。
根据实际的应用需求和存储设备的不同,可能需要调整I/O队列的大小。比如为了得到更好地响应时间,在/etc/system里设置:
set zfs:zfs_vdev_max_pending = 10
Prefetch (file level and device level)
和其它文件系统一样,ZFS也有预读的功能。ZFS文件系统的预读分为file-level和devicelevel。
file-level的预读是指ZFS可以检测到文件读取的模式,预先读入文件的后续部分从而减少I/O
操作以提高性能。对于大并发量的针对不同文件的读操作,file-level的预读通常不会带来好处,可以把预读关掉:
在/etc/system里设置:set zfs:zfs_prefetch_disable = 1 。
Device-level的预读是指在读取数据块时,会读取设备相邻的数据,假定相邻数据随后会被用到。Device-level的预读有时会读入不需要的数据反而会影响性能,在新的opensolaris发行包里,Device-level的预读只预读metadata。
RAIDZ and RAIDZ2
ZFS提供了类似传统RAID5和RAID6的功能,分别叫做RAIDZ和RAIDZ2。但RAIDZ的实现与传统RAID5有很大不同:RAIDZ的所有写操作都是Full-Stripe write,结合ZFS的copyon-write和checksum的机制,可以保证数据的一致性,无需像传统的RAID5那样需要昂贵的NVRAM来保证条带数据的一致性。
RAIDZ的stripe size是完全动态的。每一个文件系统块(record)在存储时会平均分配到RAIDZ group里的所有磁盘上,并以512字节为单位。例如,假设一个4KB的文件,如果存储在4+1的RAIDZ group,则4KB的数据会分散到4个磁盘上,每个磁盘上存储1KB。因此一个RAIDZ group里的设备不应过多,否则会影响读操作的性能。一般建议为3~9个RAIDZ比较适合大文件的顺序读写。
由于RAIDZ在设计上的特殊性,在数据恢复时需要通过metadata来恢复,要遍历整个pool。当某块磁盘出现故障时,RAIDZ重建的速度与metadata的数量有关。当有大量metadata数据时,比如大量snapshots或pool里的文件数量庞大,数据重建的速度会较慢。在这种情况下,可以考虑创建多个pool。当然,对一个很空的文件系统,RAIDZ的重建时间会很短。
本文出自 “zfs存储世界” 博客,谢绝转载!