版权声明:欢迎转载,但是看在我辛勤劳动的份上,请注明来源:http://blog.csdn.net/yinwenjie(未经允许严禁用于商业用途!)http://blog.csdn.net/yinwenjie/article/details/52208294
1、概述
在“系统存储”专题中,我们将按照“从上至下”的顺序向读者介绍整个“系统存储”体系。在这个专题中我们将至少介绍机械硬盘的主要结构、磁盘阵列的分类、操作系统的EXT文件系统、NAS文件共享存储方案、分布式文件系统重要技术点和分布式文件系统示例。最后如果有时间我们将自行设计一款分布式文件系统。下图可以大致描述笔者的写作思路:
本专题首先会花费几篇文章向读者介绍块存储的知识,包括最底层机械硬盘、固态硬盘的构造结构和工作过程。块存储的知识中我们还将介绍磁盘阵列技术,包括磁盘阵列的组织方式和设备类型。最后块存储知识中我们介绍操作系统中的文件系统,包括EXT系列文件系统和XFS文件系统(会顺带提到Windows操作系统使用的NTFS文件系统)。接下来的几篇文章我们会介绍搭建在块存储方案上的关系型数据库,文章会主要讲解Mysql数据库,包括MySQL数据库中最常被使用的InnerDB存储引擎、索引构造方式以及MySQL数据库集群的几种构造方式。
由于在《标准Web系统的架构分层》这篇文章中,笔者已经大致说明了系统存储专题的写作过程,所以本文就直接开始块存储技术的介绍了。笔者计划花费两到三篇文章的篇幅,向大家介绍块存储技术的主要知识点。
2、块存储方案基本构成
块存储技术的构成基础是最下层的硬件存储设备,这些硬件存储设备可能是机械硬盘也可能是固态硬盘。一个操作系统下可以独立控制多个硬件存储设备,但是这些硬件存储设备的工作相对独立,通过Centos操作系统的df命令看到的也是几个独立的设备文件。通过阵列控制层的设备可以在同一个操作系统下协同控制多个存储设备,让后者在操作系统层被视为同一个存储设备。
- 操作系统控制多个独立的存储设备。下图表示的三个硬盘设备在操作系统下独立工作,在操作系统下显示了三个设备文件:
- 操作系统通过阵列控制设备控制多个存储设备。阵列控制设备在市场上从低端到高端有很多选择。目前市场上的一些主板就自己集成了阵列控制芯片,如果中小企业需要硬盘阵列设备可以购买民用级产品,市场价格普遍在几千块左右。高端设备可以购买IBM/EMC提供的商业级/工业级设备,市场价格从几万到几千万不等。笔者将在本专题中详细介绍硬盘阵列的重要技术知识点。
为什么操作系统层可以做到以上两种存储设备的组织方案同时兼容呢?因为操作系统层通过各种文件系统规则,可以过滤掉各种存储设备的不同硬件特性。EXT1、EXT2、EXT3、EXT4系列文件系统和XFS文件系统都是Linux操作系统下常用的文件系统,Btrfs文件系统是最近今年Linux操作系统下越来越流行的新一代操作系统。这些文件系统的共同特点是:通过文件系统内部能够辨识的一个文件索引结构规范对下层的硬件设备结构进行封装,以便起到屏蔽和代理操作下层硬件结构的目的。 那么当下层存储设备不是机械硬盘而是固态硬盘又或者是阵列控制设备时,又是怎样的工作原理呢?本专题中将会一一解答这些问题。对于操作系统中的文件系统,我们将着重介绍EXT系列文件系统的结构。
3、机械硬盘
3-1、机械硬盘结构
现在的机械硬盘技术都非常成熟了,存储容量也出现了几何级增长。但是机械硬盘的基本结构却一直没有太大的变化:它基本上是由机械臂(磁头臂)、主轴、多个成对的磁头和磁面构成。在每个磁面上被分成多个磁道、多个扇面和多个扇区,它们的具体存在形式如下:
-
磁面上会有多个磁道,它们在一个磁面上拥有不同的磁道编号。从磁面最外围离主轴最远的磁道到磁面最内侧离主轴最近的磁道,它们的编号从0开始,分别是0、1、2、3、4…….N(N大于等于10000,依据硬盘制作工艺不同而不同)。磁头将会顺着磁道基于磁面的转动读取数据,并且可以在各个磁道间切换位置。
-
实际情况下,外部将要存储到硬盘上的数据不会一次性写满整个磁道,读取数据到外部时也不需要读取整个磁道的数据。这是因为一个磁道被分为多个弧段,这些弧段称之为扇区。扇区是硬盘上存储数据的最基本物理单元。注意,目前市场上无论哪个供应商提供的机械硬盘产品,每个物理扇区单元固定的存储容量都是512字节。只是根据硬盘密度不一样,单位磁面下的扇区数量也不一样。既然扇区是数据存储的基本单元,就意味着一次硬盘读写操作的最小数据量就是512字节。
-
那么问题来了,是不是觉得512字节太小了点?是的,很多时候操作系统层面请求读写操作的文件都会大于一个扇区的单位容量。那么在物理层面上就需要两个甚至更多个扇区来存储这个文件,那么怎样来规划存储一个文件的若干扇区在磁面上的分布,从而达到减少读取时间的目的呢?
-
操作系统层面会将物理硬盘上两个或者多个能够连续读取的扇区组成合并成一个区域,称之为“簇”。注意,这两个或者多个能够连续读取的扇区不一定在物理上是连续的。这是什么原因呢?这是因为硬盘转动的速度很快(标准速度为7200转/分),当磁头完成一个“扇区1”的读写后还来不及读取下一个连续的“扇区2”相邻的扇区就“飞”过去了,要等磁面再转动一圈到预定“扇区2”的位置才能继续进行读写。
-
所以一个簇在物理磁面的分布可能是不连续的。实际上各个硬盘生产商都会设置一个“跳跃因子”来确定能够连续读取的扇区。如下图所示,四个不存在于连续物理位置的扇区构成一个簇。这样保证了磁面在旋转一圈的情况下就可以完成一个簇的全部读写。
最后说明一点,虽然扇面是硬件层面上机械磁盘读取数据的最小单元,但是“簇”才是操作系统层面上读取磁盘数据的最小单元。我们后文将讲解EXT文件系统和Btrfs文件系统,这两种文件系统定义的簇大小是不一样的。例如EXT文件系统的族大小可以有1KB(两个扇区)、2KB(四个扇区)、4KB(八个扇区)等多种选择。那么如果一个文件太小,不需要用完一个簇怎么办呢?没办法,它有需要使用至少一个簇的硬盘空间。也就是说,在操作系统存储一个文件时,即使一个簇没有占满,剩下的簇空间也不能再使用了。这就是为什么一个文件在操作系统上有两个大小:一个是文件的实际大小、另一个是文件占用硬盘空间的大小。
3-2、机械硬盘性能瓶颈
3-2-1、影响机械硬盘性能的因素
当需要从硬盘上读取一个文件是,首先会要求磁头定位到这个文件的起始扇区。这个定位过程包括两个步骤:一个是磁头定位到对应的磁道,然后等待主轴马达带动盘片转动到正确的位置,这个过程所花费的时间被称为寻址时间。也就是说寻址时间实际上包含两部分:磁头定位到磁道的时间为寻道时间,等待盘片转动到正确位置的时间称为旋转等待时间。
硬盘寻址的目的是为了找到将要读取的文件的起始扇区,并开始去取数据。这就可以解释为什么硬盘上读取一个100MB大小的文件和读取1000个100KB大小的文件时间是完全不一样的现象了:通常来说一个100MB的文件是存储在硬盘上可以连续读取的扇区上的,也就是说当硬盘需要读取这个文件时只需要进行一次寻址(为什么说是“通常”呢?因为前提是硬盘上至少要有一端连续空白的扇区,如果此时硬盘上碎片太多可能就找不到这样的连续空白区域了);而读取1000个文件时,由于这些文件的起始存储位不连续,所以每次都要进行寻址操作。寻址时间是评价机械硬盘性能的重要指标,这个指标和硬盘转数、磁头数有直接关系:
-
硬盘转速越快的硬盘,在同样的寻址算法控制条件下将能够更快的将正确的扇区转动到磁头下方。但是硬盘转速也不能做得无限快,因为硬盘转速越快要求的磁面工艺、主马达工艺也就越高,并且产生的噪音、温度也会成几何级增加。现在民用级、商用级和工业级硬盘上最常采用三种转速选择:5400转/分、7200转/分和10000转/分。
-
磁头数,现在的机械硬盘中一般都包含了多个盘片并且分别使用独立的磁头。这样做的主要作用是在硬盘密度不变的情况下增加机械硬盘的容量。实际上这样的做法也可以增加一定的硬盘性能,因为读取存储在不同磁面上的文件时,它们的寻道时间可以相对独立。但是其对性能的提示只能是有限的,因为这些盘片共享同一个主轴马达。
机械硬盘的工作原理导致了它的工作性能会远逊于内存,现在主流的7200转硬盘外部传输速度的理论峰值大概也就是200MB/s。那么问题又来了,天才的硬件工程师难道就没有办法增加机械硬盘读写性能了吗?
答案是否定的,硬件工程师为机械硬盘集成了缓存,并采用预读机制读取当前扇区的临近扇区。举个例子,当硬盘读取一个文件所在扇区时,会将这个扇区临近的若干扇区上的数据一同读取出来并存储到硬盘缓存中。这样做的原因是依据计算机科学中得一个著名原理:局部性原理。
局部性原理包括三层含义。时间局部性,如果一个信息项正在被访问,那么在近期它很可能被再次访问。空间局部性,在近期将要被使用的信息很可能与现在正在使用的信息在空间地址上是临近的。顺序局部性,在典型程序中,除转移类指令外,大部分指令是顺序进行的。局部性是所有高速缓存设计的基本原理依据。
说人话,局部性原理在硬盘硬件设备设计中被应用的依据是:当一个文件被读取时,在它临近扇区所存储的文件数据也将在近期被读取。所以硬盘会预先读取后者到缓存中,以便在不久的将来,这些数据被请求读取时直接从缓存中向外部设备输出文件数据。
局部性原理不止适合高速缓存这样的硬件设计,它也适用于软件设计:一个程序90%的时间运行在10%的代码上。
3-2-2、顺序读写和随机读写
上一小节已经提到硬盘的顺序读写和随机读写有非常大的性能差异,其中主要的原因是两者寻址操作上所耗费的时间存在巨大差异。那么顺序读写和随机读写的差异到底有多大呢?仅仅是靠上一小节的文字描述显然不能给读者数值化的认识,所以在本小节中我们将使用一款名叫CrystalDiskMark的测试软件,让读者具体体会一下两者的巨大区别:
以上是笔者编写文稿使用的某品牌笔记本上5400转机械硬盘的测试截图(题外话,这款笔记本用起来真想骂人),这个机械硬盘属于中低端硬盘,但是相同级别的硬盘在万元级以下的笔记本上却被大量使用,所以这个测试结果很能说明一些问题。首先解释一下以上截图中的几个主要项目:
-
在测试结果的上方写明了这是一个针对E盘符的测试,每一单项所测试的读写数据总量为500MB,并且每个单项测试分别执行5次。执行5次后综合每次的测试结果取平均值,最终形成这个单项的测试结果。
-
“Seq”和“Seq Q32T1”这两个选项分别代表没有IO队列的单线程顺序读写和一个深度为32的IO队列的单线程顺序读写,其中Q32代表队列深度为32,T1表示IO线程数为1。这个队列长度和IO线程数量都可以在CrystalDiskMark进行调整。
-
4K表示进行小文件读写测试时这些小文件的大小,这些4K文件将进行随机读和随机写测试。4K测试项是衡量固态硬盘性能的重要测试项,直接描述了固态硬盘的性能,这个我们将在下文进行说明。
从上图的测试结果来看,顺序读写的性能远远高于随机读写的性能,使用了IO队列的读性能又远远高于没有使用IO队列的读性能(且写性能基本持平)。当然以上的测试数值和选用的机械硬盘型号是密切相关的,但无论数值结果如何变化,最后都会符合以上所描述的性能规则。
例如如果您对7200转企业级硬盘进行测试,那么所有测试项的横向数值会高出很多;如果您的INTEL SSD 企业级固态硬盘卡进行测试,那么横向数值又会再高出很多倍。下图展示了INTEL SSD 750 企业级固态硬盘卡的测试结果(截图来源于网络):
4、固态硬盘
看了以上来源于网络的截图后,是不是感觉整个世界瞬间清爽了。目前固态硬盘的生产工艺日趋成熟,单位容量价格也趋于下降通道。固态硬盘替代机械硬盘是一个必然趋势,迟早有一天后者将被完全替换。本节向读者介绍固态硬盘大致的构成结构和工作过程。
4-1、固态硬盘结构
固态硬盘的结构和工作原理和机械硬盘大不一样。它主要由大量NAND Flash颗粒、Flash存储芯片、SSD控制器控制芯片构成。他们三者的关系通过下图进行表示:
在一个固态硬盘上会有若干Flash存储芯片(可能有2颗、4颗、8颗等数值),每颗存储芯片内部包含大量NAND Flash颗粒,目前(2016年)NAND Flash颗粒的制作工艺已经达到12nm(2012年的主流工艺是90nm)。无论NAND Flash颗粒的制作工艺如何,每一个单位的存储容量都是512字节。多个Flash存储芯片被一个SSD主控芯片,目前能够生成SSD主控芯片的厂商不多,例如:SandForce、Marvell、三星、JMicron、Intel等。SSD主控芯片的主要工作包括识别来自于外部接口(PCI-E、SATA等)的控制指令、在将数据写入Flash存储芯片前接收和压缩这些数据、在将数据送入内存前解压从Flash存储芯片读取的数据、完成 LBA和PBA的映射转换(这个过程将在下一小节进行介绍)等工作。
虽然一个单位的NAND Flash颗粒存储容量是512字节,但是SSD主控芯片进行数据操作的最小单位并不是512字节。在SSD操作规则中定义的一个数据操作的最小单位为4KB,它包括了8个单位的NAND Flash颗粒称为Flash Page(有的资料中称为Host Page)。如果将固态硬盘和机械硬盘进行对比,那么机械硬盘中的一个最小的操作单位就是扇区,单位大小就是512字节;固态硬盘中的一个最小操作单位就是Flash Page,单位大小为4K。这就解释了上文中提到的硬盘读写性能测试中,为什么测试软件会专门针对4K大小的文件进行读写测试了。
既然固态硬盘中最小的操作单位为4K,那么在进行操作系统文件系统格式化的时候就需要注意了。文件系统中设置的一个簇大小不能小于4K且最好为4K的整数倍,这样才能保证充分发挥固态硬盘的性能。
固态硬盘不依靠磁头定位和主马达旋转确定数据的物理位置,所以固态硬盘没有寻址时间。SSD控制芯片拥有的多个控制通道可以让它同时向多个Flash存储芯片发送读写指令,这类似于机械磁盘上可以独立工作的磁头臂,但却没有共享主轴马达的限制。这些特点足以保证固态硬盘的性能远远高于机械硬盘。这也让我们认识到,以下因素影响着固态硬盘的最终性能:
-
SSD主控芯片:上文已经说到SSD主控芯片几乎完成了固态硬盘上所涉及的所有控制指令操作和数据读写操作。不同的SSD主控芯片内置的FTL算法不一样、数据压缩/解压算法不一样、控制通道数也不一样,所以固态硬盘采用哪种SSD主控芯片将直接影响其性能,目前最好的控制芯片来自于Intel,最广泛使用的控制芯片来自于三星。
-
FLash存储芯片工艺:本文提到的NAND Flash颗粒只是其中一种使用最广泛的型号,实际上还有NOR Flash颗粒等。NAND Flash颗粒又分为两种子类型:MLC存储颗粒与SLC存储颗粒。在相同单位体积下,MLC可以提供两倍于SLC的存储空间,而后者在存储响应时间和存储稳定性上又高于前者。所以MLC存储颗粒市场占有率更高,SLC存储颗粒更倾向于企业级市场。
4-2、固态硬盘工作过程
本小节我们要解决一个关键问题:既然机械硬盘和固态硬盘从工作原理、制作工艺、技术规范等多个方面都完全不一样,那为什么无论硬件层是使用机械硬盘还是固态硬盘操作系统却都可以进行识别,并在其上进行数据读写呢?
这个问题中,计算机系统不同层次对数据操作最小单位的定义不一致都还是一个小问题:虽然机械硬盘上数据操作单元为512字节、固态硬盘上数据操作单元为4KB、操作系统层面定义的数据操作单元可能是1KB\2KB\4KB\8KB等等。但是只要这些层次上的文件起始地址都是固定的,则各层的地址对应关系就可以找到的。也就是说操作系统上的地址X可以映射到机械硬盘的地址Y又或者映射到固态硬盘的地址Z,只不过存储小文件时的真实可用空间可能产生误差。
但是这里有一个固态硬盘上的操作规则会引起比较大的问题,这就是固态硬盘对数据的删除操作:固态硬盘在进行数据删除时是按照“块”单位进行的,一个“块”包含128个或者256个Flash Page。当进行删除操作时,SSD主控芯片会首先将这个块中还“有效”的数据移动到属于其它“块”的另一些Flash Page中,然后再进行“无效”数据的清理。也就是说以前操作系统通过地址X的可以读取的文件数据,现在通过地址X就可能读取不到了。那么这些固态硬盘的底层操作过程对于操作系统来说应该是完全透明的,否则操作系统就不能将固态硬盘当成机械硬盘进行操作。
这就是说,对于操作系统来说以前使用地址X进行存储的文件,无论什么是否都能够再通过地址X读取到。这里提到了两种地址:一种是操作系统读写文件的地址——称为逻辑地址;另一种是固态硬盘进行文件操作的真实地址——称为物理地址。
FTL(Flash translation layer)闪存地址转换是SSD固态硬盘控制芯片需要负责的主要工作之一,FTL的主要作用就是记录物理地址和逻辑地址的转换关系,FTL的核心是一张物理地址和逻辑地址的映射表,这张映射表存储在固态硬盘一个专门的SRAM/DRAM芯片上或者若干独立的NAND Flash Page 上。正是SSD固态硬盘的控制芯片有这样一个转换过程,操作系统才能将固态硬盘当做机械硬盘进行操作,并且SSD固态硬盘主控制芯片上FTL算法的性能直接影响着整个SSD固态硬盘的性能。请看下图的FTL转换示例:
-
操作系统对磁盘读写操作的最小单位为“簇”(EXT文件系统称为block size)。以NTFS文件系统为例,默认的“簇”大小为4K(当然您可以选择更大的“簇”大小,这样会浪费更多的存储空间,但是可以加快读写性能)。即使一个文件的大小不到4K,也会占用一个“簇”的大小。如果一个文件为210KB,那么理论上就需要占用53个“簇”空间(4KB),或者需要占用105个“簇”空间(2KB)。
-
当一个210KB大小文件的写请求从操作系统层传来时,都以逻辑地址进行描述。当SSD固态硬盘主控芯片收到这个文件的写请求时,会到FTL映射表中寻找53个空闲的Flash Page来存储这些数据,并将物理位置和逻辑位置的映射关系记录到FTL映射表中;当一个210KB大小文件的读请求从操作系统传来时,SSD固态硬盘控制芯片会首先在FTL映射表中寻找逻辑为止对应的若干物理位置,以便知晓到固态硬盘的哪些Flash Page去读取数据。
-
还需要注意,在进行数据写操作时,如果没有寻找到足够的空闲的Flash Page位置那么有两种可能:第一种可能是,在SSD固态硬盘上确实已经没有210KB的空间了,这时操作系统就会收到磁盘空间已满的信息;另一种情况是有部分空间被“无效”数据占用(这些“无效”数据来是前被操作系统删除的数据),这时固态硬盘就要进行无效数据清理。SSD固态硬盘的数据清理是将若干Flash Page的区域全部清理,称为块。清理操作过程已经在上文中大致介绍过。这也是为什么固体硬盘在使用一段时间后(特别是存储空间被占用满后),固态硬盘的性能会出现明显下降的原因。
-
最后需要注意,实际上由操作系统传来的数据和操作请求也不是直接就发送到SSD主控芯片了,而是需要经过软件和硬件的多层传递。在硬件层面上来说,数据一般需要通过主板上的南桥芯片(在经过磁盘阵列控制芯片)才能传送到SSD固态硬盘的外部接口(例如SATA3.0、USB3.0等),最后再达SSD主控芯片。
5、磁盘阵列方案
单块硬盘进行数据存储可能会存在以下问题:
-
硬盘容量有限制,当容量不足时不能进行硬件扩容。现在磁盘技术在磁盘容量上已经有了长足的发展,目前(2016)机械硬盘的主流容量已经达到6TB,固态硬盘的主流容量也达到512GB。但是单块硬盘始终都存在较严重的容量扩充问题,除非读者在扩容时手动迁移数据。
-
数据可靠性性问题。单块硬盘不存在任何备份机制,虽然现在有很多扇区检测软件可以帮助开发人员/运维人员提前发现硬盘损坏的磁道,但是都不能保证99.99%的运行可靠性。一旦硬盘由于各种原因损坏(电压不稳、磁头位移等),存储在其上的数据就可能永久丢失。
-
读写性能瓶颈。这个问题在SSD固态硬盘上还不太明显,目前主流的固态硬盘的外部传输速度可达到550MB/S,这个速度基本上达到了SATA 3/USB 3.0接口规范的理论峰值。但这个问题对于机械硬盘来说却很明显了,由于机械硬盘的读写性能受到磁头数量、盘片转速、盘片工艺等因素的影响,所以机械硬盘的读写性能一直没有一个质的飞跃。如果将单个硬盘应用在生产系统上,那么磁盘读写性能无疑将会整个系统的性能瓶颈。另外SATA 3结构理论6Gbps的传输带宽必要时也需要找到替代方案。
为了解决以上这些问题,硬件工程师将多个硬盘按照不同的规则组合在一起形成各种集群化的数据存储结构,这些存储结构被称为磁盘阵列(Redundant Arrays of Independent Disks,RAID)。磁盘阵列解决以上这些问题的基本思路有:
- 通过硬盘横向扩展或者纵向扩展的方式,解决整个磁盘整理存储容量限制的问题。而对于上层操作系统来说,看到的都只是一个磁盘设备文件/操作盘符而已。
- 通过数据镜像或者数据校验的方式解决数据冗余和数据恢复的问题。
- 通过阵列控制芯片分发数据读写请求的方式,将原本集中在一块硬盘上的数据读写请求分散到多块硬盘上,从而解决磁盘性能的问题。
目前磁盘阵列结构有多种,包括 RAID 0、RAID 1、RAID 2、RAID 3、RAID 4、RAID 5、RAID 10/01、RAID 50等。其中RAID 2、RAID 3、RAID 4这三种阵列结构常用于阵列研究,生产环境中常使用的阵列结构为RAID 0、RAID 1、RAID 5和RAID 10/01。下面我们就对这些磁盘阵列结构逐一进行介绍。
5-1、磁盘阵列结构
5-1-1、RAID 0
RAID 0阵列结构是所有阵列结构中读写性能最好的,也是所有阵列结构中实现思路最简单的:
RAID 0阵列结构没有数据冗余机制和数据恢复机制,它至少需要两个硬盘进行构造。整个RAID 0阵列结构就是将参与RAID 0阵列构建的所有硬盘进行容量累加,从而形成一个更大的、对上层操作系统统一的存储容量。所以RAID 0阵列的存储容量就是这些硬盘的容量进行累加。
当需要写入的数据到达阵列控制器,后者会向其下的硬盘设备分发这些数据。这样原来可能只由一个硬盘承担的读写压力就会被分担到多个硬盘上,最终提高了整个阵列的读写性能。RAID 0阵列结构存储速度的优势非常明显,且参与构造阵列的磁盘数量越多阵列速度越快(峰值速度最终会受到总线、外部接口规范、控制芯片制造工艺等因素的限制)。但是RAID 0阵列结构的缺点也很明显:由于阵列结构没有容错机制或者数据恢复机制,当阵列中的一个或者多个磁盘发生故障时,整个阵列结构就会崩溃并且不能恢复。所以在实际应用中,只有那些单位价值不高且每天又需要大量存储的数据才会使用RAID 0阵列结构进行存储,例如日志文件数据。
5-1-2、RAID 1
RAID 1阵列结构又被称为磁盘镜像阵列或者磁盘冗余阵列。它的构造特点是阵列结构中的每一个磁盘互为镜像:
当有外部数据需要存储时,RAID 1阵列控制器将会首先把这个数据做成N个副本(N的数量和阵列结构中物理磁盘的数量相等),实际上镜像副本的单位为扇区或者Flash Page。这些副本会分别存储到阵列结构的各个磁盘中。在进行数据读取时,RAID 1 阵列结构中的某一块磁盘将会作为主要的数据读取源头,当这个源头出现吞吐量瓶颈时,RAID 1阵列控制器会主动到其它镜像磁盘读取数据。所以RAID 1阵列的数据读取性能还是要比单个磁盘的性能要好,但是写入性能却差了很多。
从以上介绍可以看出,RAID 1阵列结构设计之初的主要目的并不是提高存储设备的读写性能,而是保证高价值数据的存储可靠性。由于RAID 1阵列结构中需要保证每个磁盘的镜像数据完全一致,所以它还要求参与RAID 1阵列结构的每一个磁盘的容量必须相同,否则RAID 1阵列结构会以最小的那个磁盘容量为自己的标准容量。
5-1-3、RAID 10和RAID 01
RAID 0和RAID 1都有自己的优缺点,并且这些特点都很突出:RAID 0虽然速度快但是没有任何数据保障措施,所以一味地快意义并不大;RAID 1虽然保证了数据的可靠性,但是却牺牲了大量空间和读写速度。所以以上两种阵列结构特别是RAID 0,在企业级/工业级环境中使用的情况还是比较少。那么有没有一种阵列结构在融合了RAID 0和RAID1两者优点的同时又避免了各自的缺点呢?
答案是:有的!RAID10和RAID01两种阵列结构就是为了实现RAID 0和RAID 1的融合而被设计的。在RAID10结构中,它首先将参与阵列结构组建的磁盘进行分组,形成若干组独立的RAID 1阵列结构,然后再将这些独立的RAID 1阵列结构形成RAID 0结构,如下图所示:
上图中有四块硬盘参与RAID 10阵列结构的组建,四块硬盘是组建RAID 10阵列结构的最小要求(实际上两块也行,但是那样的RAID 10没有任何意义)。它们首先被两两分组形成两个独立的RAID 1结构,这也意味着这些硬盘的容量最好是一样的,否则每组RAID 1结构会基于容量最小的那块硬盘确认自己的容量。接着独立工作的两组RAID 1再组成RAID 0阵列结构。
假设参与RAID 10构建的硬盘大小都为6TB,则两组独立的RAID 1阵列结构的容量分别为12TB,最终整个RAID 10阵列结构的存储容量为12TB。可以看到RAID10阵列结构的存储容量和独立磁盘的大小、分组数量有关。我们可以得到以下的计算公式:
RAID 10总容量 = N / G * 单个硬盘的存储容量
这个公式假设的前提是参与RAID 10构建的每个硬盘的存储容量都相同。其中N表示参与RAID 10构建的硬盘总数,G代表RAID 10下磁盘映射的分组数量(RAID 1分组数量)。例如,总共12块硬盘参与RAID 10构建,每个硬盘的大小为6TB,且分为三组RAID 1,那么这样组建的RAID 10阵列结构的存储容量为24TB;如果同样的情况下,这些硬盘被分为四组RAID 1,那么组建的RAID 10阵列结构的存储容量就为18TB。
可见RIAD 10通过集成更多硬盘的思路,将RAID 0阵列和RAID 1阵列的特点进行了融合,在保证数据存储可靠性的基础上提高了阵列的整体存储性能。RAID 10被广泛应用在各种计算场景中,市场上从几千到几百万的阵列设备都提供对RAID 10磁盘阵列结构的支持。RAID 10磁盘阵列的总读写速度会受到控制芯片的影响,所以几千和几百万的磁盘阵列设备实际读写性能是完全不一样的 。
另外还有一种和RAID 10阵列结构相似的阵列结构:RAID 01(或称为RAID 0 + 1),它们的构造区别是,后者首先将若干磁盘以RAID 0的方式进行组织,然后再分组成多个独立的RAID 1结构:
5-1-5、RAID 5
RAID 5阵列结构和RAID 10/01阵列结构在实际生产环境中都经常被使用,前者的应用更为广泛:虽然速度上RAID 5没有RAID 10/01阵列结构快,但是RAID 5阵列控制芯片的成本却低很多。RAID 5阵列基于奇偶校验原理,它的算法核心是异或运算(XOR)。异或运算是各位读者在大学离散数学课程中学习过的一种基本二进制运算,其运算关系如下表所述(以下表格的计算因子只有两个,目的是让读者回忆起来):
A值 | B值 | 运算结果 |
---|---|---|
1 | 1 | 0 |
1 | 0 | 1 |
0 | 1 | 1 |
0 | 0 | 0 |
接着我们可以再假设计算因子为N,根据异或运算的特点,我们可以在已知结果和N-1个原始计算因子的前提下,还原出未知的那个计算因子。请看下面示例的计算过程(N == 4):
......
1 ^ 1 ^ 1 ^ ? = 1 -----> ? = 0
1 ^ 1 ^ 1 ^ ? = 0 -----> ? = 1
1 ^ 0 ^ 1 ^ ? = 1 -----> ? = 1
1 ^ 0 ^ 1 ^ ? = 0 -----> ? = 0
0 ^ 0 ^ 1 ^ ? = 1 -----> ? = 0
0 ^ 0 ^ 1 ^ ? = 0 -----> ? = 1
0 ^ 0 ^ 0 ^ ? = 1 -----> ? = 1
0 ^ 0 ^ 0 ^ ? = 0 -----> ? = 0
......
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
有了以上的理论基础,我们就可以将它应用到实际的块存储工作中。试想一下如果将以上异或运算的每个计算因子扩展成磁盘上的一个数据扇区并针对多个扇区进行异或运算并将计算结果存储下来。那么是否可以在某一个数据扇区出现问题时恢复数据呢?答案是肯定的,请看如下扇区校验实例:
在以上四个扇区的校验示例中,它们分属四个不同的磁盘设备,其中三个扇区存储的是数据,最后一个扇区存储的是异或运算后的校验码。在上一篇文章中我们已经介绍过一个扇区存储的数据量为512字节。当某个数据扇区出现故障时,基于校验扇区的信息和正常状态的数据扇区的信息,RAID 5磁盘阵列可以将发生故障的扇区恢复出来;当某个校验扇区的信息出现故障时,RAID 5磁盘阵列还可以重新进行校验。也就是说RAID 5阵列结构同一时间内只允许有一块硬盘出现故障,出现故障的硬盘需要立即进行更换。
如果还未来得及更换故障硬盘,另一块硬盘又出现了故障,那么对整个RAID 5阵列就是毁灭性的——因为无法通过异或计算同时恢复两个计算因子。当更换故障硬盘后,RAID5阵列控制器将会自动对数据进行重新校验,恢复数据。为了在可靠性和读写性能上找到平衡,RAID 5阵列结构会将存储同一个文件的若干扇区分布在阵列下的若干磁盘上(设阵列中磁盘总数为N,则文件数据扇区分布于N-1个磁盘上),并将这些扇区的校验信息存储在最后剩余的一块磁盘上;RAID 5阵列结构中,校验信息也并不是全部存储在一块相同的磁盘上,而是均匀分布在每一块磁盘中,这样做的目的是为了尽可能快的完成数据恢复过程。
5-2、磁盘阵列设备
目前磁盘阵列设备被广泛应用在从民用级计算到工业级计算的各个基础领域,本小节的内容就带各位读者进行一些概要性了解。
- 集成在主板上的阵列控制器
最廉价的,也是在民用级市场上最普遍的做法,是将阵列控制器直接集成在主板上。RAID 0、RAID 1、RAID 5、RAID 10等阵列组织结构都可以在这种集成方式下提供支持。这些主板的价格可以做得很亲民,但是这样的阵列控制器集成方案也存在比较明显的问题:支持的磁盘数量有限制,总的I/O吞吐性能也存在瓶颈。
- 磁盘阵列盒
另一种做法是将阵列控制器至于计算机以外,民用级市场和企业级市场上也经常使用这种集成方式,并且效果要好得多,因为至少从可集成的磁盘数量上来说就比上一种做法要好得多。但是由于制造成本的原因,这些外挂的磁盘阵列盒一般还是采用USB3.0等接口进行数据传输,所以吞吐性能上基本不会有太大提升:
- 磁盘阵列柜
企业级和工业级项目中,进行磁盘阵列方案实施时更多使用磁盘阵列柜和光纤接口。这样可以缓解磁盘横向扩展的问题和数据传输吞吐性能的问题。例如EMC提供的大型磁盘阵列设备:
6、文件系统
通过以上1-5节的内容,我们向读者介绍了位于块存储方案最底层的磁盘设备和阵列控制设备的工作原理和具体应用。特别是阵列控制设备,目前大量应用在商业级系统和工业及系统中,这样的存储方案也是应用最成熟的方案。那么操作系统为了抽象化这些底层设备的控制,就需要在操作系统层面组织一个管理文件数据逻辑地址的统一结构,以便向上层开发者屏蔽硬件操作细节。这样的管理结构称为文件系统。
目前主流的文件系统包括windows下的NTFS文件系统、Linux下的EXT系列文件系统(管理规则已升级到EXT4)、XFS文件系统和Btrfs文件系统。由于本专题的知识体系全部基于Linux操作系统,所以我们不会对NTFS文件系统做过多介绍。在本文中我们主要介绍两种Linux下的文件系统:EXT系列文件系统和XFS文件系统。
6-1、EXT系列文件系统
EXT文件系统英文全称:Extended file system。最初的EXT文件系统于1992年发布,用于替换之前使用的MINIX文件系统。经过多年的发展,目前EXT文件系统已经发展出4个版本,Ext1、Ext2、Ext3和Ext4。这些不同版本的文件系统中,虽然一些结构细节发生了变化(例如采用更快的Data blocks查找方式、采用更宽的索引位等),但这几个版本中提现的文件系统设计思想却没有太大的改变。
本节将从一个统一的文件系统数据组织结构开始讲起,首先说明结构中重要区块的作用,然后说明区块间的关联关系并推导一些关键的数据计算公式,最后再阐述几个Ext文件系统版本的重要改进。
(Ext文件系统主要结构图)
上图描述了Ext文件系统主要结构。就如上文描述的那样,不同的Ext文件系统版本对以上结构都有一定的调整,但是文件系统管理结构中的重要区块的作用却没有发生什么变化,所以不会影响我们下文的讲解。首先需要说明,操作系统层面建立文件系统的单位都是磁盘分区,也就是说操作系统可以在一个物理磁盘上或者一个阵列管理设备上建立多种不同的文件系统。很多第三方分区工具都可以帮助开发人员/运维人员完成物理磁盘的分区操作。但是有一个问题需要特别说明一下:Linux系统下自带的fdisk工具只能支持2TB的最大分区容量,如果需要分割出大于2TB容量的分区,则需要使用Linux操作系统下的另一个工具parted,其根本原因是fdisk工具建立的是MBR分区表,而parted工具可以建立GPT分区表。
Ext系列文件系统的组织结构都是在进行格式化的时候就基于磁盘分区建立完毕的,以下示例脚本是针对一个名叫sdb1的磁盘分区成功建立Ext3文件系统后的显示信息。
[root@localhost ~]# mkfs.ext3 /dev/sdb1
mke2fs 1.41.12 (17-May-2010)
Filesystem label=
OS type: Linux
Block size=4096 (log=2)
Fragment size=4096 (log=2)
Stride=0 blocks, Stripe width=0 blocks
1310720 inodes, 5241198 blocks
262059 blocks (5.00%) reserved for the super user
First data block=0
Maximum filesystem blocks=4294967296
160 block groups
32768 blocks per group, 32768 fragments per group
8192 inodes per group
Superblock backups stored on blocks:
32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208,
4096000
Writing inode tables: done
Creating journal (32768 blocks): done
Writing superblocks and filesystem accounting information: done
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
以上脚本信息中有几个关键点需要读者注意,这几个关键点将和后文叙述的信息形成对照:
-
Block size=4096和First data block=0:前一个信息说明Ext3文件系统中采用的每一个block的单位大小为4KB;后一个信息说明第一个Data block(Data block用于“真正”存储文件数据)的起始号。
-
Maximum filesystem blocks=4294967296:这个信息并不是说文件系统所在磁盘分区的最大block数量,而是说Ext3文件系统所支持的最大block数量。如果按照Block size=4096进行计算,那么4294967296 * 4096 = 16TB。这个值就是当block size为4KB时,Ext3文件系统理论上支持的最大容量(但实际上是无法达到这个容量的)。
-
160 block groups:这个信息表示Ext3文件系统被分为160个block group。下文中将进行block group的详细介绍。这刚好和该Ext3文件系统的总容量是对应的:block groups * blocks per group * Block size = 160 * 32768 *4096 = 20,971,520 Byte = 20GB。
-
32768 blocks per group表示每一个block group组中所包含的block数量,这里的block并不单单指Data block,还包括block group中用来描述索引的inode table区域、可能预留的GDT区域、 block bitmap、inode bitmap等信息区域。
-
8192 inodes per group:这个信息表示了每一个block group中所包含的inode个数。inode个详细结构将在后文中进行介绍
-
Superblock backups stored on blocks:Superblock区域存储了Ext文件系统中非常重要的信息,这部分信息在一个Ext文件系统中有多个备份。这个信息向显示文件系统完成格式化后,有哪些block存储了Superblock区域的备份。
-
Creating journal (32768 blocks):journal日志区域和日志区域所占用的block数量,这个区域是Ext3、Ext4两种文件系统所独有的,Ext2文件系统中没有这个区域。关于journal日志区域的作用后文也会进行详细描述。
6-1-1、单个block group结构
上图(Ext文件系统主要结构图)中我们在sdb1磁盘分区上建立的Ext文件系统,它的最顶层由若干个block group结构组成。他们在文件系统完成格式化的时候就被预先分配好了,所以如果您使用dumpe2fs命令查看某一个ext分区的分区状态信息,就可以看到类似以下的信息(返回结果中段):
# 已省去大量状态描述信息
[root@lab-backup ~]# dumpe2fs /dev/sdb1
......
dumpe2fs 1.41.12 (17-May-2010)
......
Filesystem flags: signed_directory_hash
Default mount options: (none)
Filesystem state: clean
Errors behavior: Continue
Filesystem OS type: Linux
Inode count: 1310720
Block count: 5241198
Reserved block count: 262059
Free blocks: 5114876
Free inodes: 1310709
First block: 0
Block size: 4096
Fragment size: 4096
......
Group 157: (Blocks 5144576-5177343)
Block bitmap at 5144576 (+0), Inode bitmap at 5144577 (+1)
Inode table at 5144578-5145089 (+2)
32254 free blocks, 8192 free inodes, 0 directories
Free blocks: 5145090-5177343
Free inodes: 1286145-1294336
Group 158: (Blocks 5177344-5210111)
Block bitmap at 5177344 (+0), Inode bitmap at 5177345 (+1)
Inode table at 5177346-5177857 (+2)
32254 free blocks, 8192 free inodes, 0 directories
Free blocks: 5177858-5210111
Free inodes: 1294337-1302528
Group 159: (Blocks 5210112-5241197)
Block bitmap at 5210112 (+0), Inode bitmap at 5210113 (+1)
Inode table at 5210114-5210625 (+2)
30572 free blocks, 8192 free inodes, 0 directories
Free blocks: 5210626-5241197
Free inodes: 1302529-1310720
......
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
dumpe2fs的主要作用就是返回指定分区上建立的文件系统中super block的档案系统信息和每个block group组的信息。可以看到以上的命令显示了“/dev/sdb1”的分区中一共有160个block group,并且至少从第157个block group开始,其中的inode记录和block记录都是空闲的。
在以上命令中指定的目标分区“/dev/sdb1”采用Ext3文件系统进行格式化,所以读者看到的每一个分区的inode数量和block数据和我们后文的讲解在数值上可能会有一些出入,但是不会影响去理解Ext系列文件系统管理文件数据的组织思路。那么每一个block group中包括的数据结构主要还有:
6-1-1-1: Super block
Super block从字面上可以理解成“超级块”,其中存储了文件系统非常关键的信息用于向使用者描述当前文件系统的状态,例如block与inode的使用情况(总量和已使用量)、每个inode的大小、文件系统挂载点等等信息(后文会给出重要信息项的介绍)。并不是所有block group中都有Super block,实际上一个Ext文件系统中,只有一个唯一的Super block信息区域,但为了保证Super block不会丢失,所以会有一部分block group对完整的Super block信息进行备份,这就是为什么我们在上一小节中,格式化成功后的返回信息中会有一个“Superblock backups stored on blocks”信息。以下是dumpe2fs命令针对“/dev/sdb1”分区上的文件系统所打印的另一段返回信息,这些信息都记录在super block区域中:
[root@lab-backup ~]# dumpe2fs /dev/sdb1
dumpe2fs 1.41.12 (17-May-2010)
......
Filesystem flags: signed_directory_hash
Default mount options: (none)
Filesystem state: clean
Errors behavior: Continue
Filesystem OS type: Linux
Inode count: 1310720
Block count: 5241198
Reserved block count: 262059
Free blocks: 5114876
Free inodes: 1310709
First block: 0
Block size: 4096
Fragment size: 4096
Reserved GDT blocks: 1022
Blocks per group: 32768
Fragments per group: 32768
Inodes per group: 8192
Inode blocks per group: 512
......
Reserved blocks uid: 0 (user root)
Reserved blocks gid: 0 (group root)
First inode: 11
Inode size: 256
Required extra isize: 28
Desired extra isize: 28
Journal inode: 8
Default directory hash: half_md4
Directory Hash Seed: bd0a2065-d9ff-4abe-ad05-ded757287a0d
Journal backup: inode blocks
Journal features: (none)
Journal size: 128M
Journal length: 32768
Journal sequence: 0x00000001
Journal start: 0
......
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
为了保证文件系统在磁盘部分扇区出现物理问题的情况下还能正常工作,就必须保证文件系统的super block信息在这种情况下也能正常访问。所以一个文件系统的super block会在多个block group中进行备份,这些super block区域的数据保持一致。这里我们还需要对其中重要的参数项进行解释,以便帮助后文描述block group的其他区域:
-
Inode count:该参数表示在本文件系统中所有Inode的总数,这些Inode分布于不同的block group中,存在于这些block group的Inode Table区域。在后面的文章内容中,我们会详细讨论Inode区域的结构。
-
Free inodes:该参数说明当前文件系统中还有多少可用的Inode区域。
-
Inodes per group:该参数说明文件系统的每个block group中存在多少个Inode信息。
-
Inode size:每个Inode占用的存储空间大小,在以上显示的文件系统信息中每个Inode占用的大小都是256字节。注意,Inode的总数量、单位Inode所占用的存储空间、每个block group所拥有的Inode数量都是在文件系统格式化的时候就完成了初始化,所以无论当前的Inode是否已经有对应的文件,这个Inode都会占用256字节的空间。在以上显示的文件系统信息中,总共有1310720个Inode,每个Inode又占用256字节的空间,所以文件系统中所有Inode占用的空间为:1,310,720 * 256字节 = 335,544,320字节 = 320MB。
-
Block count:该参数表示在本文件系统中所有block的总数。
-
Free blocks:该参数说明当前文件系统中还有多少可用的block数量。一定要注意这些free block并不是全部用于记录文件数据的,例如,根据Super block中的信息一个block group要占据32768个block(128MB),其中就包含了后文我们要说明的各种数据区域,甚至包括inode所使用的block。所以我们常说的Ext3文件系统16TB的总容量实际上只是理论值,因为一个真实大小为100MB的文件在Ext文件系统上不仅仅只会使用100MB的空间进行记录。
-
Block size:该参数表示每个block所占用的磁盘空间。Ext2/Ext3文件系统中,该参数的默认值为4096(即4KB空间),并且这个参数可以在文件系统格式化的时候进行更改。那么从以上显示的文件系统中信息中,总共有5,241,198个block,所以整个文件系统可用的block总大小为5,241,198 * 4096字节 = 21,467,947,008字节 = 20TB
-
Blocks per group:该参数表示每个block group所占用的block数量。Ext文件系统中,该参数的默认值为32768。就像上文已叙述的那样,这里的32768个block包括了block group的所有结构区域:如果该block group拥有备份的Super block,那么Super block会占用1个block、Group description table占用1个block、Block bitmap占用1个block、Inode bitmap占用1个block、Inode table占用512个block(其中可能有8192个Inode也可能有16384个Inode),剩下的block的使用情况根据Ext文件系统的版本不一样而有所区别,但是大部分负责记录文件数据。
6-1-1-2:Group description table
这个区域的信息描述了当前这个block group中的其它数据区域的所在位置和使用情况,包括后面要介绍的block bitmap的起始位置、inode bitmap的起始位置、inode table的位置和使用情况以及blocks的使用情况。每一个block group的Group description table都在block group的状态发生变化时一同变化。实际上各位读者已经看过了Group description table的描述信息,就是上文6-1-1小节给出的命令返回示例中所显示的另一段信息:
......
Group 159: (Blocks 5210112-5241197)
Block bitmap at 5210112 (+0), Inode bitmap at 5210113 (+1)
Inode table at 5210114-5210625 (+2)
30572 free blocks, 8192 free inodes, 0 directories
Free blocks: 5210626-5241197
Free inodes: 1302529-1310720
......
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
6-1-1-3:Block bitmap 和 Inode bitmap
block group中的Block bitmap区域占用了4096字节(1个block),它用于帮助block group记录哪些block已经使用哪些没有使用。那么Block bitmap区域是如何完成这个工作任务的呢?Block bitmap区域的每一个bit都记录本block group中的一个block的使用情况,0表示对应的block还没有使用,如果为1则表示对应的block已被使用。既然Block bitmap区域占用了4096字节,就表示block bitmap一共有4096 * 8 = 32768 bit,也就意味着一个block group中最多有32768的block。这刚好和Super block中描述的“Blocks per group”一致。我们来具体看一个Block bitmap中的记录示例:
11110011 01010100 11101011 00000101 00000101 11100011
......
- 1
- 2
以上Block bitmap区域中的数据表示:在这个block group中,block区域第0个block已经被使用了,第1个block已经被使用了。。。第4个block还没有被使用。。。第12个block还没有被使用。。。第30个block还没有被使用。
Inode bitmap的作用是记录block group中Inode区域的使用情况,Ext文件系统中一个block group中可以有16384个Inode,代表着这个Ext文件系统中一个block group最多可以描述16384个文件。在本文6-1-1小节中所给出的Ext3文件系统中,每个block group中有8192个Inode(“Inodes per group”参数描述)。每个Inode bitmap区域本身需要占据4096字节,其描述block group中Inode区域使用情况时所使用的思路和Block bitmap区域使用的思路相似,不同的是Inode bitmap区域中所使用的4096字节中未对应Inode区域的部分将使用“1”进行填充。
6-1-1-4: Inode Table
在Ext2/Ext3文件系统中,每个文件在磁盘上的位置都由文件系统block group中的一个Inode指针进行索引,Inode将会把具体的位置指向一些真正记录文件数据的block块,需要注意的是这些block可能和Inode同属于一个block group也可能分属于不同的block group。我们把文件系统上这些真实记录文件数据的block称为Data blocks。我们将在下文中详细介绍Inode的组织结构。
6-1-2、Inode Table和单个Inode 结构
Ext2/Ext3文件系统中,采用Inode指针和Data block地址进行直接/间接关联的方式记录文件数据。一个Inode指针要么不对应任何文件的Data block,要么对应一个文件的一个或者多个Data block。在Ext3文件系统中一个Inode指针的长度为4字节,按照一个字节8个bit计算Ext3文件系统中的Inode指针支持32位Data block寻址(2 ^ 32个地址);到了Ext4文件系统,虽然它不再使用Inode指针,但同样有地址索引的概念,并且长度扩展到了6字节,那么按照一个字节8个bit计算,Ext4文件系统中的索引支持48位Data block寻址(2 ^ 48个地址)。所以如果按照单个Data block记录4KB数据大小来计算,Ext3文件系统支持的最大理论总容量就是:2 ^ 32 * 4KB / 1024 / 1024 / 1024 = 16TB,而Ext4文件系统支持的最大理论总容量就是 2 ^ 48 * 4KB / 1024 / 1024 / 1024 = 1,048,576TB = 1024 * 1024TB。
Ext2/Ext3文件系统的Inode信息存放在block group的Inode Table区域,这个区域的默认大小为512个block,那么再根据文件系统中单个Inode的大小就可以得到每个block group所管理的Inode总数。例如当单位Inode的大小为256字节,得出每个block group所管理的Inode总数为 512 * 4096字节 / 256字节 = 8192(Ext4文件系统);当Inode的大小为128字节,得出每个block group所管理的Inode总数为 512 * 4096字节 / 128字节 = 16384 (Ext3文件系统)
Ext2/Ext3文件系统中每个Inode结构中至少包括以下的信息:文件的权限定义、创建者、创建时间、最后一次修改事件、删除时间、文件唯一编号以及12个直接关联指针、1个间接关联指针、1个两级间接指针、1个三级间接指针、1个四级间接指针等。文件的权限定义等信息无需进行太多的说明,通过ls命令都可以查看到这些信息,这里重点讲解一下Inode中的直接关联指针和间接关联指针。
所谓直接关联是指Inode中4字节的信息直接指向一个Data block的位置,并可以从Data block中读取文件数据。间接关联是指Inode中4个字节的信息指向一个Data block,不过这个Data block中存储的信息并不是真实的文件数据而是将这个Data block控制的4KB空间模拟成1024个新的Inode索引(Inode大小为4字节,所以4KB / 4Byte = 1024),并分别指向若干新的Data blocks。
当从一个Inode开始,需要跳过一级这种模拟Inode的Data block才能到达真正用于记录数据的Data block,这种关联称为间接关联,原始的Inode指针称为间接关联指针;当从一个Inode开始,需要跳过两级这种模拟Inode的Data block才能到达真正用于记录数据的Data block,这种关联称为两级间接关联,原始的Inode指针称为两级间接关联指针。。。以此类推Ext2、Ext3文件系统中最多支持四级间接关联指针。这样我们就可以计算出一个Inode支持的最大Data block数量,由于一个Inode最多对应一个文件,所以这个数量就可以推导出Ext2/Ext3系统中支持的单个文件的最大容量:
以上推导过程是在Ext3文件系统下,Inode寻址位宽为32位并且在进行文件系统格式化时设置的block大小为4KB的情况下进行的。如果在文件系统格式化时设置的block大小为8KB则系统支持的单个文件最大容量就变成了8TB。
6-2、Ext2和Ext3的区别
在上文6-1小节中介绍的Ext文件系统结构主要适用于Ext2和Ext3文件系统,这两个版本的Ext文件系统在数据组织结构上基本上没有什么变化。Ext3文件系统相对于Ext2文件系统所做的主要优化在数据写入策略上。
6-2-1、Ext2写入过程概要
Linux操作系统为了加快I/O操作过程,当有文件需要写入文件系统时Linux操作系统并不会立刻将文件数据实际写入data block,而是存储到一个内存区块中。如果您通过top命令或者Free命令,就可以看到这个名叫cache memory的内存区块。
[root@lab-backup ~]# free
total used free shared buffers cached
Mem: 3805272 3662628 142644 508 1758376 1491128
-/+ buffers/cache: 413124 3392148
Swap: 3948540 0 3948540
- 1
- 2
- 3
- 4
- 5
无论是Ext2文件系统还是Ext3文件系统又或者是Ext4文件系统,写入cache memory区块的这个过程是不会改变的。以上示例效果中还有一个叫做buffers memory的内存区块,这个区块缓存了文件系统的部分Inode信息,这样保证了操作系统不会随时到文件系统上寻找Inode——优化文件系统的读性能。cache memory区块和buffers memory区块由操作系统自行管理,它们只会使用当前没有被应用程序占用的空闲内存。当应用程序请求新的内存区块且空闲内存不够时,操作系统会释放部分cache memory区块或者buffers memory区块。后文我们讲解Linux下的Page Cache技术时,还会对这部分只是进行详细讲解。当cache memory区块写满或者到达一个等待时间后,操作系统就会正式开始向文件系统写数据了。
在Ext2文件系统中,完成文件写入的过程可以简要的进行如下描述:首先文件系统会在收到文件写入请求后根据算法寻找一个还没有使用的Inode,这个算法过程根据不同文件系统小版本和Linux内核版本的不同而有所变化,但寻找原则都是一样的,即尽可能为文件寻找连续的inode和data block。上文已经提到Ext文件系统中,每一个block group都有Block bitmap区域和Inode bitmap区域分别用于记录block group中的block和Inode的使用情况。在寻找Inode的这个步骤中,block group的Block bitmap、Inode bitmap区域还不会被写入任何变化,block group的Group description table区域也不会被写入任何变化。
接下来Ext2文件系统就要向硬件层正式写入数据了,这个步骤还包括建立Inode和data block的关联信息。最后当文件系统相对空闲的情况下,文件系统才会更新block group中的Block bitmap、Inode bitmap和Group description table区域。正常情况下这种操作过程并没有太大的问题,因为文件系统随时都清楚哪些Block bitmap、Inode bitmap被真正使用了,哪些Block bitmap、Inode bitmap是被使用但还没有来得及更改状态。当文件系统正常关闭时Ext2文件系统会设置一个校验标记Valid bit=1,表示本次文件系统的操作正常结束。
但是如果当文件系统因为各种原因异常关闭时(例如断电)这个标记的值就为0,那么问题就来了。因为哪些已经被使用但还没有来得及被标示的Block bitmap、Inode bitmap区域,和真实的使用情况就存在误差了。虽然操作系统下次启动时会对整个文件系统进行扫描试图恢复操作(主要目的是恢复Block bitmap、Inode bitmap和真实事情情况的一致性),但是操作系统并不保证一致性被全部恢复。这样看来Ext2文件系统的写入过程至少存在一下几个问题:
-
由于data block块的写入操作和Block bitmap、Inode bitmap区域的更新过程是分开进行的,所以当系统异常关闭时Ext2文件系统并不能保证Block bitmap、Inode bitmap的状态和真实的block使用情况完全一致。所以才会有系统重启时试图恢复磁盘一致性的修复操作。
-
Ext2文件系统对于大文件的读写性能并不好,这是因为当文件特别大时,Ext2文件系统会启用三级间接指针和四级间接指针建立Inode和data block的联系。虽然当data block size为4KB时单个文件的大小可达4TB,但是因为存储大文件时会启用二级、三级甚至四级间接指针,这大大增加了文件系统上的索引查找时间。所以这种多级索引的思路除了可以增加单个文件容量外,对提升大文件的读写性能实际上并没有太多帮助。
-
另外Ext2文件系统在向data block写入文件数据时,将以data block size(默认为4KB)为单位依次向硬件层提交数据,这就是为什么在其它设置不变的情况下,如果将Ext2文件系统的data block size格式化为8KB,会增加一定的磁盘读写性能的原因。
6-2-2、Ext3日志模式
以上小节中提到的Ext2文件系统的明显问题中,最严重的应该还是文件系统异常情况下Block bitmap、Inode bitmap可能出现的一致性问题。所以Ext3文件系统在基本保持Ext2文件系统组织结构的前提下,使用索引日志的思路来解决Ext2文件系统上的数据一致性问题。
Ext3文件系统又被称为日志文件系统,它在Ext2文件系统的基础上做的主要改动就是,在正式写入data block数据并建立Inode索引前,通过在磁盘上的某块固定位置写入一段日志数据的方式建立这两个操作过程的关系,并根据后续操作进行日志数据的保留/删除,以达到保持操作一致性的目的。这样一来,即使文件系统上一次异常关闭,当文件系统再次启动时也不需要再进行整个文件系统Inode和data block扫描了,只需要重新加载日志数据文件系统就可以知道上一次异常关闭时还有哪些Block bitmap、Inode bitmap没有处理完。
Ext3文件系统支持三种日志模式,他们在数据一致性和I/O性能上有所区别:
-
journal日志模式
在这种模式下,当文件系统正式开始进行磁盘操作前会在磁盘的上专门的日志区域创建这个文件的副本,包括这个文件的Inode和data block完整信息,副本创建完成后才会按照正常的写入过程进行文件写入。当写入操作正常完成后,文件系统会删除日志区域的文件副本。这样一来当文件系统发生异常并重启后,e2fsck检测程序会扫描日志区域中的副本:如果发现文件副本本身就不完整,则会丢弃这部分副本。如果发现文件副本是完整的,则会继续完成正式文件的写入过程,最后删除文件副本。很显然journal日志模式会增加单个文件的磁盘操作次数,所以journal日志模式是三种日志模式中速度最慢的一种。但是journal日志模式却可以做到最完整的数据一致性要求。
-
ordered日志模式
ordered日志模式是Ext3/Ext4文件系统默认的日志模式。这种模式比 journal日志模式快了许多,因为ordered日志模式下Ext文件系统的日志区域并没有真正记录文件的真实数据,不会发生一个文件的多次读写过程。当文件系统为新文件确定了Inode和block区域后,这些Inode、Block bitmap、Inode bitmap等索引信息将会首先保存到文件系统的日志区域,并形成一个事务单位。当文件数据被完整写入磁盘对应的block后,文件系统才会将日志区域的Inode信息和block信息提交到对应的block group中,完成整个写入过程。当文件系统发生异常并重启后,e2fsck检测程序会扫描日志区域中的副本:如果发现还有事务单位没有被提交,那么磁盘上对应的block位置上的信息将被清除,以保持文件信息一致性。
-
writeback日志模式
这是一种异步日志模式,文件系统的日志区域虽然也记录将写入磁盘的新文件的Inode、Block bitmap、Inode bitmap等索引信息。但是和ordered日志模式不同的是,这些日志信息记录过程和文件数据的写入过程没有先后关联,并且也不存在“提交”的概念。当文件系统发生异常并重启后,e2fsck检测程序也不会按照日志区域的数据状态进行一致性修复。writeback日志模式在大多数情况下能够提供最佳的Ext3/Ext4文件系统性能,因为日志功能几乎是关闭的。
从以上三种Ext3文件系统日志模式的简述中,可以发现的Ext3日志并不保证数据不丢失。实际上保证数据不丢失并不是Ext3日志的目标,而保证数据一致性才是Ext3日志的主要目标。为了达到这个目标,日志数据甚至会主动丢弃无法复原的副本数据。
6-2-3、设置文件系统日志级别
本小节介绍如何查看和设置Ext3/Ext4文件系统的日志模式。首先为了确定当前文件系统是否开启了日志模式,可以使用dumpe2fs命令进行查看:
# dumpe2fs /dev/sdb2
dumpe2fs 1.41.12 (17-May-2010)
Filesystem volume name: <none>
Last mounted on: <not available>
Filesystem UUID: 0e94b563-8348-4056-8770-67fa34e2b903
Filesystem magic number: 0xEF53
Filesystem revision #: 1 (dynamic)
Filesystem features: has_journal ext_attr resize_inode dir_index filetype
......
Journal backup: inode blocks
Journal features: (none)
日志大小: 128M
Journal length: 32768
Journal sequence: 0x00000001
Journal start: 0
......
以下的block group信息可以忽略
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
可以看到Filesystem features选项中有一个has_journal关键信息标识,这表示当前文件系统已经开启了日志功能。如果没有看到这个标记,则说明文件系统本身还没有开启日志功能——这种情况下Ext3文件系统就和Ext2文件系统没有太大区别了。
以下命令可以开启Ext3/Ext4文件系统的日志功能:
# tune2fs -O has_journal /dev/sdb2
tune2fs 1.41.12 (17-May-2010)
Creating journal inode: done
- 1
- 2
- 3
以下命令可以关闭Ext3/Ext4文件系统的日志功能:
# tune2fs -O ^has_journal /dev/sdb2
tune2fs 1.41.12 (17-May-2010)
- 1
- 2
但必须注意这并不表示指定了具体的日志模式,在开启了文件系统日志功能的情况下读者可以在使用mount进行具体挂载时指定日志模式。如下所示:
mount -o data=journal /dev/sdb2 /mnt/hgfs/
- 1
以上命令的data参数部分可以换成ordered、writeback和journal,代表文件系统支持的三种日志模式。设置完成后文件一同也完成了挂载。但是怎么检查日志模式设置是否成功呢?读者可以通过dmesg命令查看Linux系统的内核日志,在日志的最后一行会有最近一次的挂载信息,类似如下:
# dmesg
......
EXT3-fs (sdb2): using internal journal
EXT3-fs (sdb2): mounted filesystem with journal data mode
SELinux: initialized (dev sdb2, type ext3), uses xattr
- 1
- 2
- 3
- 4
- 5
可以看到内核日志信息的最后一行信息说明最近完成的内核变动操作,是按照journal日志模式挂载sdb2分区。当然内核日志信息包含了很多无用的信息,读者还可以通过以下命令索引出需要查看的信息:
# dmesg | grep -B 1 "mounted filesystem"
......
sd 2:0:0:0: [sda] Attached SCSI disk
EXT4-fs (sda2): mounted filesystem with ordered data mode. Opts:
--
Microcode Update Driver: v2.00 <tigran@aivazian.fsnet.co.uk>, Peter Oruba
EXT4-fs (sda1): mounted filesystem with ordered data mode. Opts:
--
EXT3-fs (sdb2): using internal journal
EXT3-fs (sdb2): mounted filesystem with journal data mode
......
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
当然最终我们还是要实现磁盘的永久挂载,这是就需要更改Linux操作系统中的/etc/fstab文件了。如果读者使用的是文件系统默认的ordered日志模式,则不需要在/etc/fstab文件中进行额外的设置。但如果不是这样的话,读者在设置/etc/fstab文件时,就要在其中的options列说明文件系统使用的日志模式。类似如下所示:
# vim /etc/fstab
......
/dev/sdb2 /mnt/hgfs ext3 data=journal 0 0
......
- 1
- 2
- 3
- 4
7、下文介绍
上一节我们讨论了Ext3文件系统相对于Ext2文件系统最大的改进点——日志模式。但是Ext2文件系统中另外一个问题还没有做太多调整:因为存储大文件时Ext2/Ext3文件系统会启用二级、三级甚至四级间接指针建立Inode和data block的联系,这样的做法会降低文件系统上对大文件的读写能力。
Ext4文件系统的细节文章就不再过多介绍了,因为系统存储这个专题到到现在已经从最底层硬件设备开始到阵列结构再到操作系统上的文件系统进行了说明,相信读者已经理解了为什么这种传统的存储方案被称为块存储方案了。但是真正和软件架构有关的知识却还一点都没有介绍。这显然偏离了本专题最初规划。
对Ext4文件系统有兴趣的读者可以继续参考Ext官方文档(https://kernelnewbies.org/Ext4) ,从下文开始我们将转入普遍搭建在块存储方案之上的关系型数据库的相关知识介绍(主要为MySQL数据库),包括影响关系型数据库性能的重要因素、如何进行关系型数据库集群的搭建。