分布式文件系统用于解决海量数据存储的问题,腾讯大数据采用HDFS(Hadoop分布式文件系统)作为数据存储的基础设施,并在其上构建如Hive、HBase、Spark等计算服务。
HDFS块存储采用三副本策略来保证数据可靠性,随着数据量的不断增长,三副本策略为可靠性牺牲的存储空间也越来越大。如何在不降低数据可靠性的基础上,进一步降低存储空间成本,成为腾讯大数据迫切需要解决的问题。
我们对facebook版本的hadoop raid分析发现,还有很多细节需要优化改进,本文就hadoop raid存在的问题进行探讨,并对一些可以改进的地方给出思路。
首先介绍一下hadoop raid的原理和架构:
原理分析
HDFS Raid以文件为单位计算校验,并将计算出来的校验block存储为一个HDFS文件。HDFS Raid支持XOR和RS两种编码方式,其中XOR以位异或生成校验信息;而RS又称里所码,即Reed-solomon codes,是一种纠错能力很强的信道编码,被广泛应用在CD、DVD和蓝光光盘的数据纠错当中。
HDFS为每个block创建3个副本,可以容忍2个block丢失,因此存储空间为数据量的3倍。而采用RS编码,如按条带(Stripe length)和校验块(Parity block)个数比例为10,4计算,则只需要1.4倍的存储开销,就可以容忍同一条带内任意4个block丢失,即存储量可以节省16/30。
Hadoop Raid架构
DRFS
l DRFS:应用Raid方案后的HDFS
l RaidNode:根据配置路径,对需要Raid的文件(source file),从HDFS DataNode中读取对应的数据块,计算出校验块文件(parity file,所有校验块组成一个HDFS文件),并将parity file存储在HDFS中;RaidNode周期性的检查源文件及校验块文件对应的block数据是否丢失,如有丢失,则重新计算以恢复丢失的block
l Raid File System:提供访问DRFS的HDFS客户端,其在HDFS Client接口上进行封装,当读取已丢失或损坏的block时,通过对应的校验块计算恢复的block数据返回给应用,恢复过程对应用是透明的
l RaidShell:DRFS管理工具,可手工触发生成parity file、恢复丢失block等
问题与优化
l 问题1 集群压力增加
集群压力增加表现为NameNode元数据增多、访问量增加、Raid和数据恢复时集群网络及IO负载增加几个方面,具体如下:
其一,raid过程中会生成校验文件以及目录结构,导致元数据增加。如下图所示,对于每一个原始文件,都会在目标目录生成一个对应的检验文件,造成元数据量double。由于校验文件读操作远大于删除等更新操作,解决方案为对校验文件做har打包,将目录打包成一个har文件,以节省元数据量。
其二,RaidNode周期性的访问NameNode,查询哪些文件需要做raid、是否存在废弃的parity file(源文件被删除,则对应的parity file已经无效了,需要清理掉)、是否存在Missing Block等,这些操作都对NameNode产生一定压力。解决方案为调整RaidNode访问NameNode的频率,控制在可接受的范围。
其三,做Raid生成校验文件及恢复丢失的block时,需要读取相同stripe的多个block数据,导致集群内网络及IO负载增加。解决方案为选择空闲时段进行操作,减少对现网生产环境的影响。
其四,Raid完成后,源文件block副本数减少,job本地化概率减小,同时增加了网络流量和job的执行时间。为减少影响,只对访问频率较低的冷数据做Raid,而冷数据的判定,则需要从数据生成时间、访问时间、访问次数综合考虑。
l 问题2 集群性能下降
性能下降则包括块删除速度变慢、读取频繁移动的块速度变慢,具体如下:
其一,NameNode应用Raid块放置策略,删除block需要考虑相同stripe的其他block的位置情况,以保证同一DataNode上不会存储该stripe的多个block,避免由于该DataNode故障缺失过多的块,造成数据无法恢复的风险。另外,在集群启动时,NameNode要重建元数据信息,同时对比block的实际副本数和配置值,用以删除和增加block;由于Raid块放置策略的引入,每个block的增加和删除都需要考虑相同stripe的其他block位置信息,这一过程非常耗时,导致NameNode启动变慢很多。
解决方案是,在启动时使用默认的块放置策略,保持启动过程同原有流程相同,待启动完成,再修改为Raid块放置策略,动态刷新到NameNode生效。
其二,RaidNode周期性的扫描原始文件和检验文件,如发现同一DataNode上存储该stripe内的过多block,则将超出来的block迁移到其他DataNode上。RaidNode的检查周期默认值为10分钟,然而块移动过程NameNode并不会及时清掉block同移出DataNode的映射关系,而要等到下次DataNode块上报,块上报的周期比较长,一般2个小时。这样在下次块上报之前,NameNode中block映射的DataNode会不断累积,直至遍布整个集群。客户端读取这个block数据就会因很多DataNode上并不存在块文件而重试,导致性能下降。解决方案为调整RaidNode扫描周期,要大于DataNode的块上报周期,期间NameNode来修正block和DataNode的映射关系。
l 问题3 数据安全性问题
表现在rebalance不理解raid概念:
Rebalance不理解raid的条带的概念,将block在集群中重新移动后,可能会导致相同stripe的多个block保存在相同的DataNode上,存在丢块的风险。解决方案为NameNode增加RPC接口,查询block所属文件,进而结合raid块放置策略,将stripe的多个block分散得更散。
l 问题4 Raid过程Job数据倾斜
RaidNode提交job对多个源文件做raid,理想效果如图(a),多个文件平均分配到每个map中raid操作,但执行过程中发现大部分map迅速完成,统计读取记录为0,而另外少部分map执行时间较长。
分析流程发现,RaidNode采用同distcp相同的方式,先将需要raid的文件列表,以SequenceFile格式写入HDFS,且每10个文件写入一次SYNC标识,分片时再将每个文件构造成FileSplit作为分片单元;map读取输入使用SequenceFileRecordReader,以SYNC标识为起止位置。以(b)图为例,map1的起止位置跨越了SYNC1,因读取的数据为SYNC1和SYNC2之间的10个文件列表,而其它map的起止位置在同一SYNC区间内,则读取数据为0,这就是job倾斜的原因。
解决方案为每个文件后面都写入一次SYNC标识,多个文件就会平均分配到map中执行。而SYNC标识占用20个字节,且只在job执行结束SequenceFile就会清理掉,存储代价微乎其微。