facebook hdfs-raid原理详解

前言

最近集群存储资源压力比较大,很多业务长期运行下来产生了大量的历史数据,占用了大量的存储空间,目前采用的hadoop版本为hadoop2.7,为了数据安全性,hdfs默认是使用的多副本策略进行容错,,但是这样会占用过多的存储空间,导致不断地增加硬件的成本。为了节省存储成本,近期研究了下facebook 针对hdfs做的EC方案:hdfs-raid。HDFS-RAID是Facebook基于hadoop-20-append分支(第一代Hadoop)开发的raid方案,对HDFS的修改极少,主要包括为NameNode增加了根据block信息找到block所属文件的接口。

由于hdfs-raid方案是在hadoop老版本下进行集成的,所以需要自行将hdfs-raid代码迁移集成到hadoop2版本下进行使用,涉及到代码一些兼容问题。本文主要介绍hdfs-raid的一些实现原理。

HDFS-RAID实现原理

HDFS Raid目前⽀持三种⽅式进⾏冗余编码,分别是XOR, Reed-Solomon以及Jerasure Code三种。其中前两种采⽤的是java代码进⾏冗余码的编解码,⽽Jerasure Code采⽤的是C实现的native代码。下面主要介绍经常使用RS编码,本质上都是对N个数据块就行运算,产生K个校验块。这N+K个块可以同时最多容忍K个块的丢失,丢失的块可以从其余的块中的任意N个恢复出来。在HDFS-RAID里面,N叫做stripeLength,K叫做parityLength。在对数据块和校验块的组织上,HDFS-RAID提供了两种方式:

1. 每个数据文件对应一个校验文件,对数据文件的连续stripeLength个block进行编码,产生parityLength个parity block,多个parity block组成parity文件存储在HDFS上。例子:stripeLength=3,parityLength=2,数据文件有6个block,那么对这个数据文件做raid会产生4个parity block,这四个parity block被连接起来组成parity文件。数据文件和parity文件的存放路径存在一定的规则,根据配置决定.
2. 数据block可以从不同的数据文件中抽取进行组合,这种方式下,就不存在数据文件和parity文件的一一对应关系,这样的组合用StripeInfo表示,里面包括:数据块列表,校验块列表,编码方式。并且为了容错,这个信息需要持久化,实现中,这个信息可以以多个文件的形式存在本地硬盘(LocalStripeStore)上,也可以存在RDBMS(DBStripeStore)中。值得注意的是,在对某个block进行恢复的时候,需要根据corrupt block信息从StripeStore中取出用于恢复的stripeLength个block,而读取这些block的数据需要拿到block所在的文件名等信息才能读取,而NameNode不提供block到file的接口,所以HDFS-RAID为了NameNode增加了这样的接口。

如果要迁移hdfs-raid到hadoop2版本中,为了尽量少的调整hadoop原有的代码,建议采用第一种方式。

HDFS-RAID主要由三个模块组成,一个包装了DistributedFileSystem的DistributedRaidFileSystem,一个是RaidNode进程,另外一个RaidShell命令行工具。

Hadoop2版本以后raidnode向yarn resourcemanager提交job;

新加⼊了⼀个Server节点,即RaidNode,其作⽤如下:
1、与Raid Shell进⾏RPC交互,强制恢复⽂件!
2、与NameNode通信,获得corrupt的⽂件,并向JobTracker提交任务,通过mapreduce将 corrupt的⽂件进⾏恢复
Client底层使⽤的是DistributedRaidFileSystem,如果在读取⽂件过程中出现错误,会透明的将数据进⾏修复,返回给⽤户使⽤。

DistributedRaidFileSystem

DistributedRaidFileSystem基于DistributedFileSystem,是一种FilterFileSystem,在DistributedFileSystem读数据抛出BlockMissingException或者ChecksumException异常时,会构造DecoderInputStream,构造的过程中,会做block fix过程,找到stripeLength个数据块,启动几个线程同时读取这几个数据块,decode完成将修复的block数据放入buf中,上层即可以进行读取。
应用使用DistributedRaidFileSystem需要在hdfs-site.xml中设置:

<property>
  <name>fs.hdfs.impl</name>
  <value>org.apache.hadoop.dfs.DistributedRaidFileSystem</value>
</property>

RaidNode

编码

RaidNode的TriggerMonitor线程根据配置的策略(PolicyInfo)不断的选择符合RAID条件的文件,然后做RAID。做RAID有两种方式,一种是单机(LocalRaidNode),另外一种是分布式(DistRaidNode),利用MapReduce Job。HDFS-RAID中有一个encodingUnit的概念,它是做RAID的单位,默认是1。以分布式做RAID为例,假设stripeLength=3, parityLength=1,encodingUnit=2, TriggerMonitor选出了两个文件a和b,文件a有6个block, b有12个block,可以得出,a有6/3=2个stripe,b有12/3=4个stripe, encodeingUnit=2代表2个stripe作为一个unit,unit用EncodingCandidate表示,这个例子会产生三个EncodingCandidate。每个EncodingCandidate作为mapper的key,相应的PolicyInfo作为value写入Job的Input文件_distRaid.op.list(Job目录下)中作为一行。输入文件由DistRaidInputFormat来解析。Mapper类是DistRaidMapper,map函数就是对输入的EncodingCandidate范围内的stripe做raid。做raid,需要读stripeLength个块数据,生成parityLength个校验块,默认会有4个线程来做读操作,每个线程就是打开数据block所在的文件,并且seek到block的开始offset,然后将数据读入readbuf中,每个block对应一个readbuf,同时有parityLength个writebuf,用于存编码完成的parity块。最后将生成的parity块连成一个parity file。为了更安全,HDFS-RAID有一个ChecksumStore的概念,开启后,会将数据block和校验block的crc都存入ChecksumStore中,后续如果发现有block损坏,进行修复完成后,从ChecksumStore中取出以前block的crc进行比对,如果相等,说明恢复无误,然后选择一个DataNode将恢复的block发送过去。

采用RAID方式后,为了提高可用性,尽量不在同一个机器上存储属于同一个stripe group的两个block,PlacementMonitor线程用来做这个。

RAID--PurgeMonitor
PurgeMonitor主要对过期失效的parity⽂件、目录进⾏删除。 
PurgeMonitor最主要的是确定purge的元素,它是通过DirectoryTraversal来对进⾏遍历,
需要提供check⽅法,来确定返回符合purge条件的元素。 
⾸先是对directory进⾏purge。 
然后是对parity进⾏purge,同时检查是否需要将parity移动(原因是parity尽可能分散,防⽌
某个节点挂起导致的数据丢失)。 

RAID--PlacementMonitor线程
内部主要靠BlockMover线程⼯作,与PurgeMonitor配合,通过拉取datanode topology以及 socket通知DataNode将block到其它DataNode上。  

修复

BlockIntegrityMonitor用来定期检测corrupt的file,并进行修复。同样,修复block有分布式和本地修复两种方式。同样,以DistBlockIntegrityMonitor为例,获取corrupt file通过DFSck向NameNode获得,拿到corrupt文件名以及对应的corrupt的块个数后,调用FileCheckRunnable来检查文件是否已经corrupt,这里的corrupt是对DistributedRaidFileSystem而言的,只要corrupt block所在的stripe group(包括stripeLength个数据块和parityLength个parity块)中有至少stripeLength个数据块是好的,那么这个corrupt block就可以恢复,说明这个文件对于DistributeRaidFileSystem来说就是好的,没有corrupt,在这种情况下,会提交一个Job对这些corrupt block进行修复。Mapper是ReconstructionMapper,输入文件的内容是corrupt file。Mapper的map函数拿到corrupt file name,然后进行reconstruct,这块的流程原理和编码差不多,不再赘述。恢复成功block后,选择一个DataNode,给它发送WRITE_BLOCK指令,并把数据发送给它。

具体应用

主要提供两个配置文件raid-default.xml和raid.xml:

raid-default.xml: 主要存储raidnode相关配置信息以及编码信息

<configuration>

具体的raidnode相关的配置省略。。。。
主要看编码的配置
<property>
<name>raid.codecs.json</name>
<value>
[{
"id" : "rs", //编码id,在raid.xml中用到,用来饮用具体的编码
"parity_dir" : "/raidrs",//校验文件存放的位置
"stripe_length" : 10, 
"parity_length" : 4, //10个data block编码生成4个parity block
"priority" : 200,
"erasure_code" : "org.apache.hadoop.raid.ReedSolomonCode", //具体编码类
"description" : "ReedSolomonCode code",
"simulate_block_fix": true
},
{
"id" : "xor",
"parity_dir" : "/raid",
"stripe_length" : 10, 
"parity_length" : 1,
"priority" : 100,
"erasure_code" : "org.apache.hadoop.raid.XORCode",
"description" : "XORCode code",
"simulate_block_fix": true
},
{
"id" : "dir-rs",
"parity_dir" : "/dir-raidrs",
"stripe_length" : 10, 
"parity_length" : 4,
"priority" : 400,
"erasure_code" : "org.apache.hadoop.raid.ReedSolomonCode",
"description" : "Directory ReedSolomonCode code",
"simulate_block_fix": false,
"dir_raid" : true //目录级别的RAID
}
]
</value>
<description>JSon string that contains all Raid codecs</description>
</property>
</configuration>

raid.xml  : 配置需要做EC的不同目录或者文件的policy信息


<configuration> 
    <policy name = "RaidTest1"> 
        // prefix指定的路径下的文件(递归)被扫描检查是否满足RAID条件
        <srcPath prefix="/user/foxmailed/raidtest"/>
        // 引用raid-default.xml中定义的id
        <codecId>xor</codecId>
        <property> 
            <name>targetReplication</name> 
            <value>1</value> 
            <description>after RAIDing, decrease the replication factor of a file to this value.</description> 
        </property> 
        <property> 
            <name>metaReplication</name> 
            <value>1</value> 
            <description> replication factor of parity file</description> 
        </property> 
        <property> 
            // 一个文件只有2秒没有修改过才有可能被RAID
            <name>modTimePeriod</name> 
            <value>2000</value> 
            <description> time (milliseconds) after a file is modified to make it a candidate for RAIDing 
            </description> 
        </property> 
    </policy>
    // fileList指定的文件每一行的文件是RAID的候选,编码方式引用名为RaidTest1的policy
    <policy name = "RaidTest2">
        <fileList>/user/foxmailed/fileList.txt</fileList>
        <parentPolicy>RaidTest1</parentPolicy>
    </policy>
    
</configuration>

总结

Hdfs-raid的根本目的是希望通过⼀定的冗余性来较少存储空间,属于⽤计算来换存储,会占用很高的cpu和网络资源, 且在周期线程未来及做修复的情况下,客户端读取过程中会触发恢复操作延时很高,只适合特别冷的数据,线上业务直接应用的数据不建议做EC.

发布了81 篇原创文章 · 获赞 29 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/breakout_alex/article/details/102805631