最近12个月以来,Linux已经巩固了其作为服务器操作系统的地位。就像集群(cluster)对于企业级的应用很重要那样,日志文件系统(journaling file system)也是同样重要的。
为什么日志文件系统很重要呢?它是怎样工作的呢?有哪些日志文件系统可以用于Linux?
日志文件系统比传统的文件系统安全,因为它用独立的日志文件跟踪磁盘内容的变化。就像关系型数据库(RDBMS),日志文件系统可以用事务处理的方式,提交或撤消文件系统的变化。
Ext2不能满足要求
尽管Linux可以支持种类繁多的文件系统,但是几乎所有的Linux发行版都用ext2作为默认的文件系统。Linux可以支持的文件系统有:FAT、VFAT、HPFS(OS/2)、NTFS(Windows NT)、Sun的UFS,等等。
ext2的设计者主要考虑的是文件系统的效率和性能方面的问题。ext2在写入文件内容的同时并没有同时写入文件的meta-data(和文件有关的信息,例如:权限、所有者以及创建和访问时间)。换句话说,Linux先写入文件的内容,然后等到有空的时候才写入文件的meta-data。如果在写入文件内容之后但在写入文件的meta-data之前,突然断电了,文件系统就会处于不一致的状态。在一个需要大量文件操作的系统中(例如,像Hotmail这样的免费的Web e-mail),出现这种情况会导致很严重的后果。日志文件系统可以帮助解决这个问题。
假定你正在更新一个目录项(directory entry)。你已经在这个巨大的目录项的第五个文件块(block)中改变了23个文件项(file entry)。当正在写这个文件块的时候突然间断电了,这个文件块还没有写完,也就是被损坏了。
重新启动的时候,Linux(就像其它的Unix)会运行一个叫做“fsck”(file system check)的程序,扫描整个文件系统,保证所有的文件块都被正确地分配或使用。它将找到这个被损坏的目录项并试图修复它,但是不能够保证fsck一定能够修复损坏。修复不了是经常的事。所以,当出现上面那种情况,目录项中所有的文件项可能会丢失,也就造成文件的丢失。
如果文件系统很大,fsck扫描要费很长时间。在一个有数十亿个文件的计算机上,fsck可能要运行10个小时以上。在这段时间内,系统是不可用的,也就是导致了很长的当机时间。日志文件系统可以避免这种情况。
文件系统是怎样工作的?
文件系统通过为每个文件分配文件块的方式把数据存储在存储设备中。这样就要维护每一个文件的文件块的分配信息,而分配信息本身也要存在磁盘上。DOS和Windows的用户可能还记得FAT这种文件系统吧。不同的文件系统用不同的方法分配和读取文件块。
有两种常用的文件系统的分配策略:块分配(block allocation)和扩展分配(extent allocation)。块分配当文件变大的时候每一次都为这个文件分配磁盘空间,而扩展分配则是当某个文件的磁盘空间不够的时候,一次性为它分配一连串连续的块。
传统的Unix文件系统使用的块分配的机制提供了一个灵活而高效的文件块分配策略。磁盘上的文件块根据需要分配给文件,这样可以减少存储空间的浪费。当一个文件慢慢变大的时候,就会造成文件中文件块的不连续。这就导致了过多的磁盘寻道时间,当读取一个文件的时候有可能要随机而不是连续地读取文件块,这样的效率很低。
可以通过优化文件块的分配策略(尽可能为文件分配连续的块)来避免文件块的随机分配。通过使用聪明的块分配策略,可以实现块的连续分配。这样就可以减少磁盘的寻道时间。但是,当整个文件系统的文件块的分配形成碎片的时候,就再也不可能连续分配了。
每一次当文件扩展的时候,块分配的算法就要写入一些关于新分配的块所在位置的信息。如果每一次文件扩展的时候只增加一个块,那么就需要很多额外的磁盘I/O用来写入文件块的结构信息。文件块的结构信息也就是上面说的meta-data。meta-data总是一起同时地写入存储设备的,这就意味着改变文件大小的操作要等到所有的meta-data的操作都完成之后才能进行。因此,meta-data的操作会显著地降低整个文件系统的性能。
基于扩展(Extent-based)的分配方式
扩展分配方式一次性为文件分配很多连续的块。当创建一个文件的时候,很多文件块同时被分配;当文件扩展的时候,也一次分配很多块。文件系统的meta-data在文件创建的时候被写入,当文件的大小没有超过所有已分配的文件块的大小,就不用写入meta-data。(直到需要再分配文件块的时候)
这样可以优化磁盘寻道的方式,可以成组地分配块,有利于一次写一大批数据到存储设备中,这样就可以减少SCSI设备写数据的时间。
基于扩展分配的文件系统在读取顺序文件的时候有很好的性能,因为文件块都是成组连续分配的。但是,如果I/O操作是随机的,基于扩展分配的文件系统的好处就非常有限了。例如,当我们要连续地读取一个基于扩展分配的文件的时候,我们只要读起始块号和文件长度就行了。然后,就可以连续地读取所有的文件块了,这样在顺序读取文件的时候,读meta-data的开销就很小。反之,如果随机地读取文件,我们就要先查找每一个所需块的块地址然后再读取块的内容,这样就和块分配方式很象了。
在ext2文件系统中,对写性能的增强是通过尽量延迟写的时间,这样就能一次写一大批数据而不是每次写一小点。随之而来的就是系统效率的提高。同样,当读的时候,ext2也是一次读取一整组的块,也就是采用预读策略。这样就能提高ext2文件系统的读性能,大量减少每次读取少量数据的I/O操作。
文件块的组或块簇(block cluster)的大小是在编译的时候确定的。怎样设定簇的大小不是这篇文章所要介绍的内容。但是,可以这么说,簇的大小对文件系统的性能确实有很大的影响,而且簇的大小也是文件系统设计的时候需要考虑的一个很重要的方面。
象Veritas这样的扩展分配的文件系统和象ext2这样的“成簇写”(write-clustering)的文件系统,在默认情况下都使用512字节的块而不用1k字节的块。如果ext2用4k而不是1k字节的块,大概会有20%的性能提升。但是,为了减少被浪费的空间ext2文件系统的设计者建议使用1k字节的块。
日志文件系统是怎样解决问题的?
先提醒你一下:这节标题可能容易导致误解。日志文件系统确实解决了上面提到的一些问题,但是又带来了新问题。
日志文件的设计思想是跟踪文件系统的变化而不是文件系统的内容。为了更好地解释这个问题,下面我用ext2文件系统和日志文件系统举一个例子:
当我们改变文件“test.file”的内容的时候会出现什么情况?先假定“test.file”的inode有四个数据块。用来保存“test.file”文件的数据块的块号分别为3110、3111、3506和3507(因为在3111和3506之间的块已经分配给其它文件了,所以这些块不连续)。当硬盘要先找到3100,读两块,在跳到3500,再读两块,才能读取整个文件。假定你改变了第三块,文件系统会读取第三块,改变它,然后重新写入第三块,这一块还在3506这个位置。如果你往文件中添加了一些内容,就要从别的地方另外分配一些空余的块。
如果在日志文件系统中,情况就有所不同。日志文件系统不会改变第3506块的内容,它会把“test.file”的inode的一个拷贝和新的第三块保存到磁盘上。在内存中的inode列表需要更新,让“test.file”使用新的inode。所有的变化、添加和改变需要被记录到一个文件系统中被称为“日志”的那部分中去。每隔一段时间,文件系统在“检查点”(check point)回更新在磁盘上的inode,并且释放文件中用不到的那些旧块(例如:“test.file”文件最初的第三块)。
在系统崩溃之后,日志文件系统很快就能恢复。它需要恢复的只是日志中记录下来的很少的几块。当断电之后,“fsck”只要用几秒钟的扫描时间。
这就是我所说的解决了一些问题!
但是,文件系统为得到额外的安全也是要付出代价的,这就是系统开销。每一次更新和大多数的“日志”操作都需要写同步,这样就需要更多的磁盘I/O操作。系统管理员就面临这样一个问题:为了有一个更安全的文件系统值不值得牺牲一部分性能?
大多数系统管理员会根据实际情况作出决定。没有必要把“/usr”目录放在日志文件系统上因为“/usr”目录大部分是只读的操作。但是,可以考虑把“/var”或包含e-mail spool文件的目录放在日志文件系统上。幸运的是在Linux系统中可以根据需要混合使用这些文件系统。
日志文件系统还有一个问题就是更容易产生碎片。因为它的文件分配方式与众不同,很容易在文件系统中到处产生碎片。ext2文件系统也会产生碎片但是可能不会有这么严重。每个月定期把文件系统备份到磁带中然后重新恢复,不仅可以解决这问题,而且可以检查备份/恢复的过程是否正确。
想得到一些好处,总是要付出一些代价的,不是吗?
可供选择的Linux日志文件系统
当我写这篇文章的时候,有两个日志文件系统还在开发,有三个日志文件系统可供使用。
SGI的xfs(http://oss.sgi.com/projects/xfs/)日志文件系统和Veritas(www.veritas.com)的文件系统和卷管理(volume manager)。这两个文件系统在五个月前就发布了,但是现在还不能得到源代码。SGI的xfs是基于Irix(SGI的Unix)上已经实现的xfs。SGI已经宣布xfs为Open Source的软件。
两个马上就可以得到的日志文件系统是reiserfs和IBM的jfs。这两文件系统都是开放源代码的而且很多有天赋的人在开发这两个文件系统。jfs(Journaled File System Technology for Linux)的开发者包括AIX(IBM的Unix)的jfs的主要开发者。
在AIX上,jfs已经经受住了考验。它是可靠、快速和容易使用的。Reiserfs应用了一些新的技术,例如,统一名字空间(unified name space),是非常有前途的(Namesys)可以参考。
有一些Linux的发行版已经包括了reiserfs文件系统,作为安装时的可选项。SuSE 6.4 就很容易使用reiserfs文件系统。reiserfs的最新版是3.5.12,经过测试reiserfs的基准测试的结果是很激动人心的。这个测试使用“postmark”基准测试,50,000个事务处理20,000个文件。结果是:
Sun E450 1 GB, Solaris 2.6, and vxfs (Veritas) file system: 22 transactions/second
Sun E450 1 GB, Solaris 2.6, and UFS file system: 23 transactions/second
Dual P-III, 1 GB Linux 2.2.13, standard ext2: 93 transactions/second
Dual P-III, 1 GB Linux 2.2.13, reiserfs 3.5.5 journaling beta: 196 transactions/second
Dual P-III, 1 GB Linux 2.2.13, reiserfs 3.5.5 journaling beta, mount options notail,
genericread: 847 transactions/second
(Sun的计算机用的是barracuda硬盘,x86的计算机用的是cheeta硬盘)
还有一个日志文件系统是jfs。jfs还没有被任何Linux发行版采用,因为它现在的版本是0.7。但是,jfs进展得很快。本文的作者已经加入jfs项目现在是jfs FAQ的维护者。
reiserfs和jfs都非常容易安装。下载完jfs的软件包之后,还有下面几个步骤要完成:
1) 解压jfsXXX.tar.gz。
2) 软件带有内核(2.2.14、2.2.15和2.3.99)的补丁。把补丁拷贝到内核源代码的目录,通常在“/usr/src”。把补丁打到内核上,用这个命令:
root@ maguro /usr/src # patch -p0
3) 重新编译内核
root@ maguro /usr/src # make menuconfig; make dep; make clean; make bzImage; make modules
安装reiserfs的步骤和上面类似:
2) 把补丁拷贝塔内核源代码所在的目录,通常在“/usr/src”,给内核打补丁:
root@ maguro /usr/src # zcat linux-2.2.11-reiserfs-3.5-patch.gz | patch -p0
3) 编译内核。
4) 用下面命令生成reiserfs文件系统:
root@ maguro /usr/src # /linux/fs/reiserfs/utils/mkreiserfs /dev/hda2
另一个选择
日志文件系统的另一个选择是ext2的后继者ext3fs文件系统。ext3fs文件系统正在Linux内核黑客Stephen Tweedie的领导下开发。
ext3fs还处于beta测试阶段,就像reiserfs和jfs,但是它工作得很好。Stephen预计2000年夏天可以正式发布ext3fs。ext3fs最大的优点是向下兼容ext2。而且ext3fs还支持异步的日志,这意味着它的性能可能还比ext2好。
安装ext3fs和安装jfs和reiserfs类似。从这下载补丁:ftp://ftp.uk.linux.org/pub/linux/sct/fs/jfs。
就像reiserfs,ext3fs可能不久就会成为标准Linux内核的一部分,可能是2.4版的Linux内核。