上次介绍了磁盘,这篇来介绍一下RAID
要介绍RAID技术的原因,其实是因为目前大部分分布式存储在做的事情其实RAID在很多年前就已经做到了,所以如果你希望做存储相关的事情,那么RAID是必须要理解,但不一定要用到的概念:)
计算机要存储和读取数据,主要依托这么两个部件:
1.通信管道和通信协议,心灵感应还需要靠电波通信呢不是?~一般来说这通信管道在计算机内就是总线,使用电信号,在计算机外则很多选择,可以选择光,可以选择无线电波,也可以选择电信号,甚至可以动用卡车和油轮嘛J,在磁盘存储领域,有非常多种通信管道和通信协议,粗略的按照发展时间序列来排列的话:ide/ata/sata/scsi/sata3mb/sata6mb。大概是这个顺序,通道的吞吐量也是逐渐的提升。
2.数据存储介质,一般来说也就那么几种,光蚀刻,磁带,固态电子等。每一种都有自己的一些特性。
而raid的主要目标就是合理的使用上述资源,通过数据分区(partition)和数据复制(replication),来提升数据存取的整体吞吐量,iops以及安全性和可以性。
(RAID,RedundantArrayofIndependentDisks)独立磁盘冗余阵列,其实产生的核心原因很简单,单个硬盘的iops和数据安全性都不够高,那么自然而然就会想到,如果有很多块磁盘存储介质,把这些磁盘进行合理的组织,让他们并行的提供读写,不是就能实现更高的吞吐量和更高的数据安全级别了么?这就是raid的基本想法,其实RAID/软RAID/GoogleFilesystem/HDFS要做到的事情都是一样的:
通过切分和复制组装大量存储,实现更高的吞吐量和更高的数据安全级别。
因为RAID的各类介绍广见于各类网站,因此这里就用几句句话来快速的介绍一下几种常见的RAID模式吧:
RAID0,RAID1,RAID5,RAID10,RAID01,当年看这些概念的时候总以为这些数字之间有什么继承或者进化关系,但其实这些数字之间是没关系的,只是个唯一标识而已。。。
RAID0把所有磁盘按照条件进行全局分区,保证数据可以尽可能并行的写入到这些磁盘中,从而提升读写性能,HDFS/GFS也借鉴了这个机制。一般来说就是给每一个固定大小的文件块一个唯一的标识,写的时候按照这个标识往某块磁盘中写入,读的时候根据这个标识从某块磁盘中读取这批数据。
RAID1数据复制镜像,简单来说就是依托冗余的方式把数据复制多分从而能够保证数据安全。HDFS/GFS里面也借鉴了这个机制,做法就是把数据文件顺序的写在同一个机架的三个不同的机器内,从而保证数据安全。代价是N倍的数据空间消耗,同时在一块硬盘坏掉后,因为需要在新磁盘做数据冗余,因此某些磁盘会有一段时间的“较高延迟不应期”
RAID5是对RAID1在空间浪费上的改进,主要思路就是三块磁盘组成一个组,假设有A,B,C.每个磁盘上都会被全局分区,然后C上面存AB的XOR后数据。这样当A坏掉的时候,可以根据C,B恢复A,B坏掉可以根据A,C恢复B,C坏掉可以根据A,B恢复C。与RAID1相比的好处是:因为RAID1因为是全镜像,所以假设数据量是Nmb,那么实际磁盘存储了2Nmb,而RAID5则只需要存储1.5Nmb。代价是额外的计算量,并且,raid5的写入速度也一般比较慢。
RAID1+0与RAID0+1,就是把raid1和raid0结合到一起,1+0和0+1的区别在于:
先做数据冗余然后再做数据分区(raid10),(图片的下层是“先”上层是“后”:)
(图片来自wiki)
(图片来自wiki)
RAID0+1和raid1+0在四块磁盘组成的阵列里面读写吞吐量在正常情况下其实没有本质差别,但想一想,假设我们使用了raid0+1,写了一组顺序的数据,0,1,2,3,4,5,6,7,8,9,10.他以mod2的方式被分配到两块磁盘上,当这两块磁盘中的一块,比如是mod2=0的那块磁盘损坏了,那么为了保证数据的写入顺序,这组raid0的两块磁盘势必会停留在0,2,4,6,8,10这几个写入中的某一个上,还不能跳过有问题的数据,否则会出现写入乱序,数据会错乱的。对四块磁盘组成的raid0+1,坏一块盘就等于两块盘不能用了,明显不划算,所以raid0+1只存在于思想实验中。。。默认一般都是raid1+0方式。再稍微扩展一点,其实目前所有的文件系统,如GFS,HDFS,FASTFS等,采用的方式都类似raid1+0。再扩展一点,其实所有的有状态kv存储,都是一个raid1+0..也就是先数据复制,再数据切分。
虽然原理完全相同,但是Raid的实现方式有非常多种形式,下面让我来做个概要性的介绍
一种模式是在单台计算机内部实现raid。为了能够对raid的实现有更深入的理解,我们还需要来回顾一下计算机的基本体系结构。
(图片来自网络)
从上面的图中能够看到两个关键的设备,南桥和北桥,他们的主要都用来处理cpu与其他设备之间的io操作,差别在于北桥因为距离cpu比较近,所以主要面向一些高速设备,比如内存和pci-e,能够提供更低延迟和更高的吞吐量。而南桥则主要负责与一些慢速设备的链接工作。磁盘么就像老牛拉破车,于是就必然被分配到了慢速设备的后面。
在开始的时候,我们主要使用的磁盘接口就是上图中的IDE总线,但随着技术的逐渐发展,在一台机器上仅仅只支持两块硬盘的IDE已经明显的不能够满足日常要求了,于是就出现了后续的几套新的协议,SCSI,ATA,SATAetc,这些协议的位置都是磁盘读写控制协议的一种,主要差别在于能够达到的理论速度值不一样,支持的协议也因为不同厂商的小农意识有意无意的设计的有那么点儿不兼容(其实这就是市场经济的体现~哈哈)。
Raid的实现方法可以很多样,不过大部分情况下就是一层decorator,接在ide/ata/sata/scsi等协议层的后面。
然后,这里会有一个很重要的问题需要解决了,就是传说中的单机数据复制CAP问题,所以借此机会就来详细的做个阐述。
当然,用后面产生的CAP这个概念来阐述单机数据复制的问题,这个方式肯定会有点别扭,不过其实他们都描述了同一个问题,而只有一个条件发生了变化。理解清楚这个变化的条件,对于理解整个问题会有很大的帮助。我们在将来会在无数的地方不断地从各种不同的角度来阐述这个问题的:
先来描述一下问题:如果想要数据更安全,并尽可能提高数据的可用性,一个比较可行的方法就是把一份数据多存几份。那么问题就来了,如何能够保证一组有序数据(Data0,Data1,Data2…DataN)能够以最低的延迟在N个磁盘上同时成功呢?没错!就是高性能原子性操作问题,我们又不得不面对原子性这头怪兽了!
我来尝试做一个分析:首先,以N个磁盘为前提,串行的按照磁盘1,磁盘2,磁盘3..磁盘N这个顺序写入数据是肯定不行了,否则得慢死,一定要能够并行。
一种合理的并行策略一般来说是这样进行的:取第一个数据Data0,并行的发送给所有磁盘进行写入并等待,当所有磁盘反馈写入成功后,标记Data0已经被写成功了,然后取Data1,并行的发送给所有磁盘进行写入并等待,当所有磁盘反馈写入成功后,标记Data1已经被写成功了。…然后取DataN重复操作。
这里有一个非常重要的操作要提醒大家注意:”标记Data已经被写成功了”,这步操作是不能够省略的,而且也不能丢失!让我们来看看如果省略或丢失了会有什么。
因为从可见性要求来说,没有被标记为全部写成功的数据是不能够被读取到的,因为可能有些磁盘写成功了,而有些则没有。因此必须尽可能快的对已经全部写成功的记录进行标记。
而如果丢失,则意味着数据丢失,那么RAID就失效了。
这就是多块磁盘同时并行写时候面临的问题。是不是跟gfs/hdfs面临的问题一样?哈哈,本来gfs/hdfs就是在“借鉴”磁盘的raid策略嘛。
既然这个写成功的log这么重要,还丢不得,并且每次读还都需要用到,那可是个宝,得多来几份存着。等一下,这个log应该被存在哪里?raid在做的事情本身不就是为了让数据能够写到多个地方么?如果这个log也要被尽可能快的写到多个地方,那么保证写log到多份的那个log’应该怎么保证安全呢?保证log’写到多份的那个log’’应该怎么保证安全呢?于是我们就进入到了无穷倒退的悖论里面去了。这其实也是CAP中必然面临的问题~
必须打破循环!于是,单机raid利用了一个取巧的方式来解决问题:单点的raid卡,内存,ssd和电池。
因为raid卡是个单点的存储,所以他只需要把数据的log保存在本地的内存中就会返回成功,那么下一个问题就来了,内存是易失性存储啊,外一断电数据丢了怎么办?这时候就要靠ssd和电池来拯救世界了~电池会给raid卡供电,让raid卡能够把还在内存中的数据誊写到ssd里面。
没错,不太酷,但能工作。而且也很巧妙的就解决了CAP的问题。你问:这不是解决吧,raid卡是单点啊。
没错,确实是单点,不过这个单点因为只使用内存和简单的逻辑控制器,出现异常的概率要远远小于磁盘出现问题的概率。因此,大部分人在机器的运维生涯中应该是见不到raid卡坏掉的。
第二种实现,我也简单说说,其实也很好理解,就是放在外面独立成为一台新的机器。
单个刀片就算是再能装,6块到8块盘就已经会让空间捉襟见肘了,我的cpu运算能力超强,但磁盘太慢,6块盘远远赶不上cpu的运算速度增长。那么有没有其他方式能够用更多磁盘组织到一起的方式呢?
有需求自然就有市场,盘柜就进入了大家的视线:
(图片来自网络,特意找了一个看不见厂家标识的XD)其实盘柜可以扩展到很大,像个小房子hoho..
他的核心原理跟我们上面提到的单机raid其实完全一样,也是由一个单点的raid路由策略模块和一大堆磁盘组成的阵列一致,同时,因为磁盘被拿到了原来机器的外面,原来用于内部数据传输的传输协议因为缺少远程机器地址类的信息,比较难以解决在跨机器的网络中进行数据传输的问题,因此就产生了一些新的网络传输协议。这就是基于光纤的FibreChannel,将scsi/sata嫁接上ip协议的协议嫁接派和后来产生的infiniband。
上面的这些协议,都有一些存储厂商在背后进行支持,但本质没变,与tcp/ip地址要做的事情一样,需要在传输的包内加上对端地址信息(类似ip),然后再加一层封装来标记和分割数据包(tcp),从而让数据可以被传递到遵循相同封装协议的其他机器上。但这里不扩展这些协议的设计理念和市场占有率的不同,因为目前还没人能取得绝对盛世,感兴趣的读者可以自行查阅相关资料。
一般来说,复制和切分只需要做一次,没必要在所有逻辑层都做这么个事情,减少层次就可以直接减少软件的复杂度。目前,在gfs/hdfs里面都存在着这样的冗余浪费,因为上层逻辑层做了partitionreplication,下层如果再使用raid,等于多次partitionreplication,逻辑层次过多,会降低整体性能和磁盘利用率。因此目前在一些互联网企业中,工程师们也可能会选择的方式就是彻底放弃传统raid,使用裸设备,而全部使用上层逻辑复制和切分。