1.前言
Swift升级到2.0大版本后宣称开始支持纠删码,这其实是一个很有意义的特性,主要是能够在一定程度上解决3副本空间浪费太多的问题。因为3副本这一点是swift推广的最大障碍之一,成本的增加吓退了不少潜在客户。这次的改进有望消除客户顾虑,拓展更多用户
而回到存储领域来看,数据冗余机制其实这几十年来没有太多进展,RAID,副本一直是当仁不让的最终选择。而近几年,尤其是规模较大的应用场景下,纠删码越来越多的出现在选择的视野范围,成为RAID,副本之外的第三种选择,因此也获得了越来越多的关注。
纠删码(Erasure Code)本身是一种编码容错技术,最早是在通信行业解决部分数据在传输中损耗的问题,它的基本原理是把传输的信号分段,加入一定的校验再让各段间发生一定的联系,即使在传输过程中丢失掉部分信号,接收端仍然能通过算法把完整的信息计算出来。
如果严格的区分,实际上按照误码控制的不同功能,可分为检错、纠错和纠删三种类型。检错码仅具备识别错码功能 而无纠正错码功能;纠错码不仅具备识别错码功能,同时具备纠正错码功能;纠删码则不仅具备识别错码和纠正错码的功能,而且当错码超过纠正范围时,还可把无法纠错的信息删除。
前面曾提到过适用场景通常是大规模部署,这是有其缘由的。从传统情况来看,RAID通常用在企业级环境里较多,在几台和十几台存储设备规模的IT系统中,一向是使用稳定可靠历经数十年磨砺的RAID技术。而在数据中心级的大规模部署中,RAID不再受欢迎,大部分的分布式系统都偏好副本模式,看重的是其高可靠性和读性能优化的特点(关于副本的讨论我之前写过一篇:为啥大家都喜欢3副本,是拍脑袋的吗?http://blog.sina.com.cn/s/blog_57f61b490101a8ca.html)。然而副本带来的成本压力实在是有些吃不消,这时候Erasure Code适时出现,以更低成本和更高技术含量提供近似可靠性这几点,就吸引到了众多分布式存储/云存储的厂商和用户。
2. 纠删码理论分析
纠删码常见的有三类,Reed-Solomen类,级联低密度纠删码和数字喷泉码,后面两种的实现原理细节和优劣这里就不深入了,这里只简单介绍下目前在存储行业应用的Reed-Solomen类纠删码。
从纠删码基本的形态来看,它是N个数据+M个校验的结构,其中数据和校验的N和M值都能够按照一定的规则设定。在1~M个数据块(数据或校验都行)损坏的情况下,整体数据仍然可以通过计算剩余数据块上面的数据得出,整体数据不会丢失,存储仍然是可用。
下面是纠删码的结构示意图。
从图中其实可以看出,纠删码和大家熟悉的RAID技术看起来是有些类似的,一个条带(Stripe)是由多个数据块(strip)构成,分为数据块和校验块。但与RAID5/RAID6不同的是,纠删码功能上来看最大的区分特点是校验和数据的比例按N+M可调整,并且校验块数量不再受限于两个,典型的如12+4,6+3等等。
校验块和数据之间是如何建立关系的呢?通信理论(鄙人专业)告诉我们纠删码是属于分组线性编码,编码过程用到的数学理论并不高深,数学关系其实是是矩阵乘法。具体来说是用编码矩阵和分块数据做乘法从而得到校验块,如下:
在建立完关联后,由于矩阵运算是可逆,系统就具备了容忍最大M个失效的能力。
(微软曾在一次演示中用两数之和等于第三个数来解释,但仅是为了方便理解,在真正实践的多校验情况下,还是使用编码矩阵的。)
https://www.usenix.org/conference/atc12/technical-sessions/presentation/huang
3. 纠删码的演化,RS->LRC
纠删码通过技术含量较高的算法,提供和副本近似的可靠性,同时减小了额外所需冗余设备的数量,从而提高了存储设备的利用率。但纠删码所带来的额外负担主要是计算量和数倍的网络负载,优缺点都相当明显。尤其是在出现硬盘故障后,重建数据非常耗CPU,而且计算一个数据块需要通过网络读出N倍的数据并传输,所以网络负载也有数倍甚至10数倍的增加。
整体来看,若采用纠删码技术,你能够得到了希望的容错能力和存储资源利用率,但是需要接受一定的数据重建代价,两者间做一个平衡。
难道事情就这个样子了吗?有没有优化改善的空间呢?答案是“有”。
如果仔细分析故障出现的情况,你将很容易发现两个特征:
特征一:所有的故障都将导致同样的重建代价,无论是一个盘,还是M个盘
特征二:单个磁盘故障的几率远远大于多个磁盘同时故障的几率,通常在90%以上
因此,优化的思路自然聚集到更容易出现的单个磁盘故障上来,如何更有效的处理这种概率较大的事件呢,直接出现的对策就是分组,把单个磁盘故障影响范围缩小到各个组内部,出坏盘故障时,该组内部解决,在恢复过程中读组内更少的盘,跑更少的网络流量,从而减小对全局的影响。
LRC(Locally Repairable Codes),我理解为局部校验编码,其核心思想为:将校验块(parity block)分为全局校验块(global parity)、局部校验块(local reconstruction parity),故障恢复时分组计算。
以微软Azure的云存储(Windows Azure Storage)实现为例,它采用LRC(12,2,2)编码,将12个数据块为一组编码,并进一步将这12个数据块平均分为2个本地组, 每个本地组包括6个数据块,并分别计算出一个local parity,之后把所有12个数据块计算出2个global parities。
当发生任何一个数据块错误时,只需用本地组内的数据和校验块用于计算,即可恢复出原始数据。而恢复代价(通过网络传输的数据块数量)就由传统RS(12,4)编码的12,变为6,恢复过程的网络I/O开销减半,同时空间冗余率保持不变,仍为(12+2+2)/12 = 1.33
微软在介绍中有过一张图来对RS和LRC进行对比,我觉得描述的非常清楚,这里借用一下
图中,蓝色是LRC,红色是RS,可以很清楚的看到,使用LRC的编码方式后,虽然空间利用率(横轴)并没有提高,但是重建代价(纵轴)却有明显的改善。考虑到分布式系统里故障是一个常态,重建代价的降低和局部化就是非常有价值的一个技术改进了。
另外,有一点要特别注意,LRC并不是100%不丢数据的,4个块坏掉的情况下,只有86%的几率能找回数据,从可靠性排序来说,RS12+4 》 LRC12+2+2 》RS6+3。
但综合来说,LRC还是很有竞争力的技术,目前,Microsoft、Google、Facebook、Amazon、淘宝(TFS)都已经在自己的产品中采用了Erasure Code,并且大多都从经典RS转向LRC。虽然具体实现,但基本原理都是LRC的组内校验+全局校验。
4. 纠删码的实际案例
我们可以具体来看看在业界,各家优化版纠删码的实现是怎么样的。
Google RS(6,3) in GFS II (Colossus),
Google在04年发布GFS的经典论文后,09年开始开发第二代GFS(Colossus),它采用了最基本的RS(6,3)编码,将一个待编码数据单元(Data Unit)分为6个数据块, 再添加3个parity block,最多可容包括parity blocks在内的任意3个数据块错误。
数据恢复的网络I/O开销为:恢复任何一个数据块需要6次I/O,通过网络传输6个数据block,存储的空间冗余率 为(6+3)/6 = 1.5
由于Google的信息能查阅到的有限,我没找到近两年的情况介绍,但相信Google这样一家技术型的公司,应当也有类似的演进后的纠删码技术,不会止步于5年前的RS标准码。
Facebook:从RS(10,4)到LRC(10,6,5)
Facebook早期在HDFS RAID中采用的编码方式RS(10,4)
如上图所示。将每个待编码的数据均分为10个数据块, 后面添加4个校验的parity校验块。这种RS编码方式的空间冗余率为(10+4)/10 = 1.4x,发生任何一个数据块错误的恢复代价为10,即发生任意一个块错误需要10次I/O操作,从网络传输的数据量为10个数据块。
同样为减少数据恢复的网络I/O代价, FaceBook在13年和加州大学共同发表了论文, XORing Elephants: Novel Erasure Code for Big Data,并把这一成果用在“进阶版HDFS”上
其LRC编码方法如下:
除了在原先的10个数据块之后添加4个校验块外,还将10个数据块均分为2组,每组单独计算出一个局部校验块(Parity),将数据恢复代价由原来的10降低为5.即恢复任何一个数据块错误只需要进行5次网络I/O,从网络传输5个数据块。此种编码方式的空间冗余率 为(10+4+2)/10 = 1.6。
4. 结论
相对副本而言,纠删码(erasure code)的编码技术无疑对存储空间利用率带来很大提升,但由于引入额外的编码、解码运算,对分布式系统的计算能力和网络都有一定额外的要求,简单的理解就是硬件性能要升级,网络环境也得要升级,升级的代价在目前阶段还是一笔不小的预算。
而由于性能损失的原因,用在本身压力已经很大,很“热”的在线存储系统明显不是很合适,所以目前大多数系统还是把erasure code用于冷数据的离线处理阶段。
LRC编码由于减少了网络I/O传输的数据量,参与数据恢复运算的数据量和重建时间都基本上能够缩短一倍,但却是以可靠性和空间利用率的一定牺牲为代价的。如何更加有效的实现还是要结合实际项目需求,综合考量,有更多的实际工作要做。