RAID
前面我们介绍了磁盘的基本原理,我们知道一块磁盘的容量和速度是有限的,对于一些应用来说,可能需要几个TB的大小的来存放数据,我们必须要制造更大单盘容量的磁盘吗?实际上,可以使用多块磁盘并行起来解决这个问题,这就是RAID技术。
RAID:独立的磁盘组成具有冗余特性的阵列。Redundant Array of Independent Disks
七种RAID
RAID 0
实现RAID 0 有两种方式,一种是非条带化的模式,一种是条带化的模式。
对于非条带化的模式:
RAID 0 : 一块磁盘写满了,就往另一块上写, 一次IO只用到一块磁盘,对整个系统来说容量增大了。
因为写满一块物理盘以后再写另一块盘。对写IO没有任何优化,但是对读IO能提高一定的并发IO读几率。
我们希望可以这样,同时向两块磁盘进行写。我们可以把要写的IO块进行编号,1、2、3……100等,然后在$t_1$时刻,磁盘A和磁盘B同时写入1、3两块,然后$t_2$时刻,同时写入2、4块,依次类推。
这样就可以实现并发写IO呢。接下来就是如何来进行块的划分的问题了。
其实磁盘已经经过低级格式化了,也就是划分为若干的扇区,但是扇区只有512B大小,这么小的粒度太消耗性能。所以我们要重新划分磁盘,而且又不能抛弃原有的扇区。
最直接的想法就是若干个扇区组成一个Data block,比如由4个扇区组成一个块(block)
- data Block:N倍个扇区大小的容量,Block只有在一个Segment中是物理连续的,逻辑连续就需要跨物理磁盘。
下图是引入了分割思想以后的硬盘
- 不同磁盘的相同偏移处的块组成成了Stripe,也就是条带,Stripee
- Segment:一个Stripee所占用的单块磁盘上的区域。
那么条带深度和条带长度指的就是
总结
这就是所谓的条带化,它是对磁盘逻辑上的划分,相当于在磁盘上加了一个中间层而已
这样对于一个大块的数据,可以以条带为单位进行写入,也就是数据被分为了多块写入了4块硬盘。而不是向之前一样顺序的写入一个硬盘里面
RAID 0要提升性能,条带要做得越小越好。因为若是条带深度容量大于写入的数据的长度,这段数据将会落到同一个segment中,相当于本次IO只能从一块硬盘中读取。
但是条带太小,并发IO几率降低。因为如果条带太小,每次IO一定会占用大部分物理盘,队列中的IO只能等待IO结束后才使用物理盘。
总之,参与RAID0 的物理盘会组成一个逻辑上连续,物理上也连续的虚拟磁盘。控制器对虚拟磁盘发出的指令,被RAID控制器转换为真实磁盘IO,再返回主机磁盘控制器,经过控制器在cache中的组合,再提交给主机控制器。
RAID 0有非常明显的缺点,没有任何的备份,所以任何一块硬盘损坏均会造成数据丢失。
RAID 1
RAID 0 最大的缺点是没有备份盘。RAID 1 进行了改正。他采用了一块用于正常使用,另一块作为影子盘存在。
也就是写数据的时候,会写两份。所以写的时候的速度并不快,而且可用容量实际上就只有一块盘,空间浪费很严重。
RAID 2
RAID 0 速度快,但是没有备份,RAID 1 有备份,但是可用容量太少。
RAID 2 的改进在于引入了校验盘的概念。当数据损坏的时候,可以根据校验盘的数字,恢复原来磁盘上的数字。
RAID 2采用“汉明码”来进行校验,这种纠错技术算法比较复杂,而且需要加入大量的校验位,比如4位数据编码,会加入3位校验位。
同时数据存储的时候,会把每个IO下的数据以位为单位强行打散在每个磁盘。
磁盘最小的IO单位是512B,如何写入1bit?上层IO可以先经过文件系统,然后通过磁盘控制器驱动向磁盘发出IO。最终IO大小都是N倍的扇区。即使只要几个字节,也需要读出整个扇区
所以每次必须所有联动起来一次进行存储,如果各磁盘的主轴没有同步,则先读出数据的硬盘需要等待。所以开销也比较大。
正因为此,目前RAID2已经不怎么使用了。
RAID 3
RAID 3引入了一种新的校验算法,可以将数据盘中的每个位做XOR运算,然后将结果写入到校验盘的对应位置。任何一个扇区损坏,可以通过剩余的位和校验位一起进行XOR运算来获得丢失的位。
同时RAID 3 把条带长度设置为4K字节,因为一般文件系统刚好是4KB一个块,所以如果用4块数据盘,条带深度为1KB,也就是2个扇区。这样,可以保证连续写的时候,以条带为单位写入,提高并行度。
所以RAID 2和RAID 3的每次IO都会牵动所有磁盘并行读写,每次只能做一个IO,不适合多IO并发的情况。
也说RAID 2和RAID 3适合IO块大的情况
一般来说,RAID 3 的条带长度= 文件系统的大小,就不会产生条带不对齐的现象。减少碎片。
关于RAID 3的校验盘有没有瓶颈的问题
若一个逻辑块是4KB,4+1块盘,文件系统下发一个IO至少是以一个逻辑块为单位的。所以文件系统下发一次IO,不管多大都是跨越了所有数据盘的。
连续
连续读:寻道时间忽略,IOPS受限于传输时间,因为RAID 3 是把一个IO分散到N个数据盘上,即传输时间是单盘的1/N,即持续读的性能是单盘的N倍。
持续写:分担到N个盘,也是单盘的1/N。因为每次IO写,物理磁盘上的所有分块都需要更新,包括校验块,就没有瓶颈和热点的区别。
随机
随机读写:多个盘同时换道,所以性能相对于单盘没有提升。而且有的磁盘不是严格主轴同步的,会拖累。
并发IO:一次IO必定会占用所有的盘,其他的盘必须等待,所以根本不能并发IO。
总结起来就是RAID 3适合于连续大块的读和写,不适合于随机IO和并发IO。
RAID 4
- RAID 0属于激进派,为了速度,根本不要备份。
- RAID 1属于保守派,需要浪费一个镜像的容量。
- RAID 2和RAID 3 属于中庸派。
RAID 2和RAID 3已经解决了校验盘的问题,避免了一块盘损坏数据全丢失的问题。但是对于无法得到并发IO的问题还没解决。
RAID 2和RAID 3的思想是让所有数据盘都参与起来。对于随机小块读写,每秒产生的IO数目很大,但是每个IO的请求数据长度却很短,如果所有磁盘同一时刻都在处理一个IO,得不偿失。不如让这个IO直接写入一块磁盘,其他的做其他的IO。
方法有:
- 可以增加条带深度,一个IO比条带深度小,所以可以完全被一个磁盘所处理。直接写入了一块磁盘的Segment中。
- 增大数据的随机分布性,不要连续在一块磁盘分布,要和其他IO所用的磁盘不一样。
所以 RAID 4 的改进是增加了条带深度,RAID 4相对于RAID 3 性能几乎没有提升。但是至少让它可以进行并发IO
RAID 5
RAID 4相对于RAID 3 性能几乎没有提升。而且因为每个IO必定会占用校验盘, 所以校验盘成为了瓶颈,而且是热点盘,容易坏。
这样看来RAID 4其实有些不伦不类。
RAID 4的关键错误在于忽略了校验盘,每个IO不管怎么样都会读写校验盘的。
RAID 5的改进在于将校验盘把校验盘分割开,依附于数据盘。把条带做得很大,保证每次IO不会占满整个条带。
2块盘的RAID 5系统,对于写操作来说不能并发IO,因为访问一块盘的时候,校验信息一定在另一块盘中。同理,3块盘也不能
所以最低可以并发IO的RAID 5 需要4块盘,此时最多可以并发两个IO,并发的几率是0.0322。
RAID 5 磁盘数量越多,并发的几率越大。
RAID 5与RAID 0相比
RAID 5 是继RAID 0 , RAID 1以后又一个可以实现并发IO的阵式,但是比RAID 1更划算,比RAID 0更安全。
- 容量方面:随着磁盘数增加,RAID5浪费的是N分之一,而RAID永远是二分之一。
- 性能方便:RAID 5和RAID 0都是使用条带来提升性能,但是RAID 6又克服了RAID 0的无保护。
RAID 5与RAID 3相比
RAID 5的连续读写不如RAID 3,因为RAID 3 的条带深度很小,每次IO可以牵动所有的磁盘为之服务。
RAID 5 每次IO一般只使用一块数据盘,先放满一个Segment,再去下一个磁盘的Segment存放,块编号是横向进行。
所以RAID 3在IO SIZE大的时候高性能,RAID 5在随机IOPS大时有高性能。
RAID 5的缺点
RAID 5的缺点是写惩罚:写性能差,因为每写一扇区就要产生其校验扇区,一并写入校验盘。
新数据过来,控制器立即读待更新扇区的数据,然后读此条带的校验数据根据公式新数据的校验数据 = (老数据 EOR 新数据) EOR 老校验数据 得到新校验数据,然后写到磁盘中。
所以每次写入都需要更新校验盘。浪费3个其他动作,也就是读老数据,读老校验数据,然后写新数据和校验数据,浪费了除了“写新数据”之外的操作。
总结:随机并发IO和写性能二者只能取其一。
RAID 5EE
RAID 5的缺点是不能坏两块盘,因为如果一块盘坏了,可以用其他数据校验出。
可在阵式中增加热备盘,不参与组阵,只有在损坏的时候立刻顶替,其他盘恢复数据。如果此时有请求此盘的IO,则其他盘暂代。
RAID 6
同样RAID 6也是为了解决RAID 5只能坏一块盘的问题。
如果坏了两块盘,相当于存在两个未知数,要求解两个未知数据,一定需要另外一个不相关的等式来求解。
所以使用两套算法各自算出一个等式,分别放在两块校验盘中。
优点是更安全,缺点是写惩罚更大。
RAID 卡
软件RAID运行于操作系统底层,将SCSI 或者IDE控制器提交上来的物理磁盘,虚拟成虚拟磁盘,再提交给卷管理程序。但是
软件RAID :
- 占用内存空间
- 占用CPU
- 无法把操作系统盘做成RAID :如果操作系统损坏了,就无法运行。
既然软件这么多缺点,所以人们谋求用硬件来实现RAID的方法。
RAID卡就是利用独立硬件来实现RAID功能的方法。
一般在SCSI卡上增加了额外的芯片用于RAID功能。
SCSI RAID卡上一定要包含SCSI控制器,因为其后端连接依然是SCSI的物理磁盘。
操作系统如何看待逻辑磁盘
RAID控制器向OS提交的是虚拟逻辑盘,而非所有的物理磁盘。每个逻辑盘对OS来说都是一块单独的磁盘。
比如安装了2块IDE磁盘和4块SCSI磁盘,IDE直接连接到IDE接口,SCSI连接到PCI接口的SCSI卡上。
若无RAID,可以看到6块硬盘。
可以通过RAID程序把两块IDE做成RAID 0 , 变成了2*80G = 160G的磁盘。
4块SCSI磁盘做RAID 5,相当于3块盘的容量,即216GB。
所以磁盘管理器其实可以看到两块硬盘,可以格式化为NTFS等文件格式。
与分区对比
与分区不同,分区是OS在物理磁盘上做再次划分。而RAID 卡提供给OS的是任何时候是一块或者几块的逻辑盘,也就是OS认为的物理磁盘。
OS在磁盘上还可以进行分区,格式化。
LUN:条带化以后,RAID程序操控SCSI控制器向OS层的驱动程序代码提交虚拟化之后的虚拟盘。
RAID 控制器如何管理逻辑磁盘
RAID 卡可以对逻辑磁盘进行再次的划分,既然要划分,必须对某块磁盘的某个区域划分给那块逻辑盘用心里有数,所以有必要在每块磁盘保留一个区域,记录划分信息、RAID类型以及组内的磁盘信息。这种统一的RAID信息格式:DDF 。
RAID卡可针对总线上某几块磁盘做一种RAID类型,然后针对另几块磁盘做另一种RAID类型。
一种RAID类型包含的磁盘共同组成了一个RAID GROUP ,简称RG。
逻辑盘就是从RG划分出来的,原则上不能跨RG来划分,因为RG的RAID类型不一样,性能就不一样。
RAID 卡上的内存
RAID卡上的内存,有数据缓存和代码执行内存的作用
RAID 控制器和磁盘通道控制器之间需要一个缓存来适配。适配不同速率的通信
缓存数据IO:缓存队列,执行或者优化合并。
RAID卡的缓存
对于上层的写IO,有两种手段来处理;
- Write Back模式:将上层发来的数据保存在缓存中之后,立即通知主机IO完成,执行下一个IO。实际上此时数据还在缓存中,没有写入磁盘。RAID卡在空闲的时候,一条一条或者批量写入磁盘,其实是欺骗了主机。如果意外,数据丢失,上下数据不一致。需要使用电池来保护缓存。
- Write Through模式:只有在写入到磁盘之后才会通知主机,但是缓存的提速作用没有优势。缓冲作用依旧生效。
对于读缓存:
- 有一种算法叫PreFetch:预取,读缓存。其实就是认为主机下一次IO,有很大的几率是读取到所在磁盘位置的相邻数据。所以在主机还没发出读请求的时候,就先把相邻的数据读到缓存中。对大文件应用很适用。
- 还有一种缓存算法:假设主机的下一次IO可能还会读取上一次读过的数据。读了一段数据到缓存之后,如果数据被主机的写IO更改了,不会立即写入磁盘,而是留在缓存中。等到主机有一段时间不用了,则写入磁盘中。
中高端的RAID卡一般有256M的RAM作为缓存。
卷管理层
到目前为止,我们已经可以通过RAID卡对外呈现一个一个的逻辑盘了,但是逻辑盘存在一个非常大的问题就是不够灵活。
如果一开始就划分一个100G的逻辑盘,如果数据盛不下了,此时把其他磁盘上未使用的空间挪一部分到逻辑盘上。
但是从RAID卡里面增加逻辑盘容量很费功夫。即使实现了,上层文件系统也无法立刻感知到。所以对要求不间断服务的服务器不适用。
归根结底,因为RAID控制器是由硬件来实现RAID的,所以操作起来不灵活,如果OS把RAID控制器提交上来的逻辑盘,加以组织和再分配。就非常灵活,其实就是加一层灵活的管理层。
卷管理层:Volume Manager,LDM(逻辑磁盘管理)
LVM开始是Linux系统上的一种实现,后来移植到AIX和HPUX等系统
- PV:OS识别的物理磁盘(或者RAID提交的),类似一块面团
- VG:多个PV放到一个VG里面,VG(volume group)卷组。VG会将所有的PV首尾相连,组成逻辑上连续编址的存储池。
- PP:物理区块,Physical Partition,在逻辑上将一个VG分割为连续的小块。(把一大盆面掰成大小相等的无数块小面块)。LVM会记录PP的大小和序号的偏移。如果PV本身是经过RAID控制器虚拟化而成的LUN,扇区可能是位于若干条带中,物理上不一定连续。
- LP:逻辑区块,可以对应一个PP,也可以对应多个PP,前者对应前后没有什么区别。后者又分为两种情况。
- 多个PP组成一个大LP,像RAID 0
- 一个LP对应几份PP,这几份PP每一份内容一样,类似RAID1。然后用一个LP来代表他们,往这个LP写数据,也就是写到了这个LP对应的几份PP中。
- LV:若干LP组成LV (逻辑卷),也就是LVM所提供最终可以用来存储数据的单位。生成的逻辑卷,在主机看来还是普通的磁盘,可以进行分区和格式化。
大小可以随时变更,也不需要重启OS。前提是还有备用的PP。
操作很简单:创建PV,加入VG ,创建LV,格式化,随便扩展。
最大的好处:生成的LV可以跨越RAID卡提交给OS的物理或者逻辑盘。
卷管理软件的实现
那么卷管理软件到底怎么实现的呢?
LVM会记录某块物理盘的名称、容量,谁是谁,从哪里到哪里是属于这块盘的,地址是多少等。这些信息记录在磁盘某个区域,LVM中这个区域叫VGDA
LVM可以通过读取每块物理磁盘上的这个区域来获得LVM的配置信息,比如PP大小,初始偏移,PV的数量,排列顺序和映射关系等。
LVM初始化的时候读取信息,然后在缓存中生成映射公式,从而完成LV的挂载。如果此时上层来一个IO,LVM就需要通过缓存中的映射关系判断地址对应到实际物理磁盘的哪个地址。然后通过磁盘控制器驱动直接给这个地址发数据。这个地址被RAID控制器接收到了,还需要做一次转换。
总之:卷管理软件就是运行在OS磁盘控制器驱动程序之上的软件,作用是实现RAID卡硬件管理磁盘空间所实现不了的灵活功能,比如随时扩容。
磁盘在VM这一层处理之后,称为卷更为恰当。因为磁盘控制器看待磁盘,就是盘片+磁头,而卷管理软件看待磁盘,会认为它是一个线性的大仓库,而不管仓库用什么方式存储。
仓库的每个房间都有一个地址(LBA),VM只需要知道一共有多少,让库管员(磁盘控制器驱动)从某段地址(LBA地址段)存取货物(数据),那么库管员立即操控机器(磁盘控制器)来各个房间取货物(数据),这就是VM的作用。
** 即从底到上依次是:物理磁盘、磁盘控制器、IO总线、总线驱动、磁盘控制器驱动、卷管理程序**
在底层磁盘扩容之后,磁盘控制器驱动程序会通知VM已经增大了多少容量
扩大、收缩卷需要其上的文件系统来配合。
MBR 和VGDA
分区管理是最简单的卷管理方式,分区就是将一个磁盘抽象为一个仓库,然后将仓库划分为一库区、二库区等。
分区管理和卷管理最大的不同在于,分区管理只能针对单个磁盘进行划分,而不能将磁盘进行合并再划分。
分区信息保存在磁盘上,位于LBA1这个扇区,又称为MBR也就是主引导记录。
BIOS代码都是固定的,所以必定要执行MBR上的代码,新出来的规范EFI可以灵活定制从那个磁盘的哪个扇区启动,
MBR除了包含启动指令代码,还包含了分区表。启动的时候,程序会跳转到活动分区去读取代码做OS启动。所以必须有一个活动分区。
卷管理软件在划分了逻辑卷以后同样需要记录卷怎么划分的,使用一种叫VGDA的数据结构。
高级VM没有抛弃MBR,而是在此基础上,增加了类似VGDA的这样的数据结构来动态管理磁盘。
文件系统
文件系统相当于理货员,与库管员一起完成管理粮库的工作。
应用程序只需要告诉文件系统需要放多少的数据或者读多少数据,而文件系统不需要关心仓库到底在那里,放到那个房间。
文件系统需要将数据存放在那里记录下来。
如果存取的数据量大,每次记录的数很大。将8个房间划分为一个逻辑房间,称作“簇”
数据如果找连续的簇进行存放,则还必须花时间把整理仓库,整理出连续的空间来,所以不如在描述数据存放的方式,比如数据 10000 簇2,6,18,这样就可以解决空间浪费的问题,而且不需要经常整理库。
但是这样又存在一个问题,数据描述的方式变得长短不一,可以为了简化处理,需要给一个定长的描述。比如数据 100000 首簇1,然后在在首簇做一个标记,写明下一个簇是多少号,然后找到下一个簇,根据簇的路标,到下下个簇进行取货,依此类推。
如果要寻找空的簇,只需要找格子上没有写字的簇即可。
总结一下,
描述货物的三字段:名称、数量、存放的第一个簇。
进一步优化:
货物虽然有可能存放在不连续的簇中,但是这些簇往往也是局部连续的。所以如果是一段一段的找而不是一簇一簇的找,会节约时间。比如簇段1~3,簇段5~7等等。
所以记录数据存放信息的本子就是元数据,也就是用来描述数据怎么组织的数据。
如果记录本丢失,纵然货物无损,也无法取出,因为无法判断货物的组织结构了。
文件系统的IO方式
IO Manager是OS内核中专门来管理IO的模块,可以协调文件系统、磁盘驱动的运作,流程如下
- 应用调用文件系统接口
- IO Manager把请求发送给文件系统模块
- 文件系统映射为卷的LBA
- 文件系统向IO Manager调用卷管理软件模块的接口
- 卷管理软件将卷对应的LBA反映为实际的LBA,并请求调用磁盘控制器驱动程序。
- IO Manager向磁盘控制器驱动程序请求将对应的LBA段从内存写入到物理磁盘。
文件系统IO:
- 同步IO:进程调用了IO以后,必须等到下位程序返回信号,否则一直等待,被挂起。
- 如果下位程序没有得到数据
- 阻塞IO:下位程序等待自己的下位程序返回数据。
- 非阻塞IO:通知上位程序数据没收到。
- 异步IO:请求发出以后,执行本线程的后续代码,直到时间片到或者被挂起。这样应用程序的响应速度不会受IO瓶颈的影响,即使这个IO很长时间没有完成。
异步IO和非阻塞IO另一个好处:文件系统不需要等待及时返回数据,可以对上次的IO进行优化排队。
Direct IO:文件系统有自己的缓存机制,数据库也有缓存,IO发出之前已经经过自己的优化了,如果又在文件系统层面再优化一次,多次一举。使用Direct IO以后IO请求、数据请求以及回送数据不被文件系统缓存,直接进入应用程序的缓存中,可以提升性能。此外,在系统路径上任何一处引入了缓存,若采用write back模式,都存在数据一致性的问题,因为Direct IO绕过了文件系统的缓存,降低了数据不一致性。