目前的HDFS中数据是靠三备份triplication来保证冗余的。显然这只是一个简单有效的方法而不是一个非常elegant的方法。三备份浪费了大量存储空间,在集群规模较小的时候可能还不是那么明显,但是对于大规模集群就比较明显了。如果按照1GB存储空间的成本是1$来算,如果数据规模是5TB,那么两备份(10TB)和三备份(15TB)的成本差距只有5000$;而如果数据规模到了5PB的话,两备份和三备份的成本差距就有5,000,000$。目前在GFS/HDFS普遍采用的三副本策略设计的初衷是由于commodity hardware在三副本的情况下可以保证不丢失数据。
在高端存储上普遍采用的RAID机制是一种数据冗余策略。比较常见的RAID-5通过奇偶校验的方法,能容忍磁盘阵列中任意1块存储设备失效而不丢失数据,但是需要一定的额外存储空间来存放校验数据。
那么在HDFS中能否使用类似的策略在不降低存储可靠性的前提下降低存储副本数目呢?Google在GFS2(即Colossus)系统中使用了Reed Solomon Erasure Code实现了成本更低的可靠存储。Microsoft的Azure平台也使用了类似的Erasure Code技术来降低存储成本。Facebook在开源Hadoop的基础上也实现了一套基于Erasure Code的RAID方案(http://wiki.apache.org/hadoop/HDFS-RAID)。
Hadoop-hdfs-raid现在是对现有Hadoop的一个包装,而不是把这部分代码嵌入到现有的Hadoop代码里,因为那样会增加代码的复杂度和不稳定性。从hadoop-2.0开始,它将要作为一个单独的project存在,社区也加大了这一部分的开发力度。目前这一部分功能主要是Facebook在维护。Dhruba在今年的Hadoop Summit 2012中的一个关于HDFS的workshop(http://www.meetup.com/Hadoop-Contributors/events/60841502/)中谈到了hdfs-raid在Facebook内部运行的情况。
存放在HDFS上的数据分为热数据和冷数据两种。热数据一般是存放三备份,因为这些数据经常会被用到,所以多备份除了高效冗余外还能起到负载均衡的作用。对于冷数据,并一定要在HDFS里面保存3个副本。Dhruba介绍了两种不同的RAID方案,对于不太冷的数据块A/B/C,通过XOR方式产生parity数据块,原来的数据块A/B/C各保留2个replica,parity数据块也有两个replica,这样,replica系数就从3减小到了2.6(理论值)。 对于很冷的数据,方案更加激进,由10个数据块通过Reed Solomon算法生成4个parity file,对于原来的数据块,只保留一个replica,parity数据块有2份replica,这样,replica系数就降到了1.2。那么这个过程是怎么做的呢?
整个hdfs-raid分为两部分:RaidNode和DRFS(Distributed Raid File System)。
RaidNode:
(1)启动一个RPC Server,接受RPC请求执行相应的命令。RPC对应的协议是RaidProtocol。集群管理员可以通过RaidShell给RaidNode发送命令。
(2)生成一个ConfigManager,通过读取raid.xml配置文件,获取用户指定的Policy。一般一个Policy对应一个文件或者目录,包括这个目录下什么样的文件会触发RAIDing操作(例如srcReplication是3的文件,modTimePeriod为3600000表示最后一次修改时间在1个小时之前的文件才会被作为RAIDing的备选);这些文件采用何种编码方式(XOR或者Reed Solomon);编码之后parity file和meta file将要存放几个备份(targetReplication和metaReplication)等。
(3)然后就是会启动多个线程:BlockFixer线程用于定期扫描看是否有数据块corruption然后执行修复;TriggerMonitor线程按照raid.xml配置的各种策略定期检查相应的文件,然后执行encoding操作生成parity file;PurgeMonitor线程用于删除已经废弃的parity file;HarMonitor线程定期把parity file小文件按照Har的形式合并,减少NameNode元数据存储的压力。
DRFS(Distributed Raid File System):
DRFS是盖在HDFS client之上的一层,截获应用对HDFS client的调用。例如用户访问某块数据,DRFS会调用HDFS client去NameNode/DataNode获取数据,如果返回数据正常那么OK;如果读取的过程中遇到corrupted data,那么DRFS client截获HDFS client返回的 BlockMissingException,然后接管这个文件的读取流,通过parity file恢复丢失的数据块,然后返回给应用程序。所以整个数据块恢复的过程对应用程序是透明的。而且DRFS client在读取到corrupted data block然后恢复之后只是传给了应用程序,并不会把数据恢复到HDFS中,而BlockFixer和通过RaidShell触发的recoverFile操作会把修复好的块复原到HDFS中。
整个流程讲清楚之后,就要看看具体的实现。目前在RaidNode计算encoding和decoding的过程主要有两种方法:
(1)LocalRaidNode:在RaidNode本地计算parity块,因为计算parity块是一个计算密集型任务,所以这种方法的可扩展性受到限制。
(2)DistributedRaidNode:通过MapReduce任务的形式把Job分布到不同的节点上来计算parity块。
同样对于BlockFixed也有两种对应的方法:
(1)LocalBlockFixer: 在RaidNode本地decoding计算修复corrupted data块。
(2)DistBlockFixer: 通过分布式MR任务的形式decoding
Erasure Code
EC是用来encoding和decoding的算法。在目前的Hadoop中有两种实现:XOR和Reed Solomon。XOR只能允许丢失一块数据,而RS可以容忍丢失多块。目前的RS是采用(10,4)策略实现的,即10个数据块生成4个parity file,那么可以容忍丢失4块数据。
目前Erasure Code也是分布式存储领域的研究重点,<<Rethinking Erasure Codes for Cloud File Systems:Minimizing I/O for Recovery and Degraded Reads>>-FAST2012和<<Erasure Coding in Windows Azure Storage>>-ATC2012是关于如何把Erasure Code用到现在的云存储中最新的两篇顶级会议的文章,可能会给我们如何改进hdfs-raid有所启发。
https://issues.apache.org/jira/browse/HDFS-3544 也在考虑一种新的数据块恢复方法,因为在现有方法中,以(10,4)的RS编码为例,如果发现某一块数据丢失,那么需要另外10块数据来恢复这个数据块,那么在数据中心内部就会产生大量的网络流量,可能会影响正常的Job运行。所以这个jira提出了使用http://arxiv.org/pdf/1109.0264.pdf 所提出的Simple Regenerating Codes来通过少量的存储空间浪费来换取大量的带宽资源的节省。
最后我联想到一个问题,就是现在的云计算数据中心是基于commodity hardware搭建的,数据中心各个server之间的通信是通过以太网来连接的,那么在各个server之间的控制流和数据流就是通过同一条物理网络连接的,那么这样必然会导致网络争用。而控制流要求低延迟,数据流要求高吞吐。GFS的paper里提到Google数据中心是控制流和数据流分别使用不同的物理网络,我到没听说现在实际部署的Hadoop集群中是两种流分开的,不知道为什么大家没这么做?成本?复杂性?还是现阶段没有必要呢?
Ref: http://yanbohappy.sinaapp.com/?p=106
推荐阅读:
http://www.slideshare.net/ydn/hdfs-raid-facebook
http://wiki.apache.org/hadoop/HDFS-RAID
http://en.wikipedia.org/wiki/Erasure_code