HDFS-RAID原理和實現
HDFS-RAID 是非死book基于hadoop-20-append分支(第一代Hadoop)開發的raid方案,對HDFS的修改極少,主要包括為NameNode增加了根據block信息找到block所屬文件的接口。RAID的基本知識和目前社區的方案對比見這,本文重點分析HDFS-RAID的原理和實現。
業界常用的編碼方式包括Reed-Solomon(RS),XOR,他們本質上都是對N個數據塊就行運算,產生K個校驗塊。這N+K個塊可以同時最 多容忍K個塊的丟失,丟失的塊可以從其余的塊中的任意N個恢復出來。在HDFS-RAID里面,N叫做stripeLength,K叫做 parityLength。在對數據塊和校驗塊的組織上,HDFS-RAID提供了兩種方式:
-
每個數據文件對應一個校驗文件,對數據文件的連續stripeLength個block進行編碼,產生parityLength個parity
block,多個parity
block組成parity文件存儲在HDFS上。例子:stripeLength=3,parityLength=2,數據文件有6個block,那么
對這個數據文件做raid會產生4個parity block,這四個parity
block被連接起來組成parity文件。數據文件和parity文件的存放路徑存在一定的規則,根據配置決定.
- 數據block可以從不同的數據文件中抽取進行組合,這種方式下,就不存在數據文件和parity文件的一一對應關系,這樣的組合用 StripeInfo表示,里面包括:數據塊列表,校驗塊列表,編碼方式。并且為了容錯,這個信息需要持久化,實現中,這個信息可以以多個文件的形式存在 本地硬盤(LocalStripeStore)上,也可以存在RDBMS(DBStripeStore)中。值得注意的是,在對某個block進行恢復的 時候,需要根據corrupt block信息從StripeStore中取出用于恢復的stripeLength個block,而讀取這些block的數據需要拿到block所在的文 件名等信息才能讀取,而NameNode不提供block到file的接口,所以HDFS-RAID為了NameNode增加了這樣的接口。
HDFS-RAID主要由三個模塊組成,一個包裝了DistributedFileSystem的DistributedRaidFileSystem,一個是RaidNode進程,另外一個RaidShell命令行工具。
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>or g.apache.hadoop.dfs.DistributedRaidFileSystem</value> </property>
其他配置見wiki
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線程用來做這個。
修復
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.xml和raid-default.xml
raid.xml:
<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></pre>
raid-default.xml中主要配置RaidNode支持的編碼類型
<configuration> <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>
<p> 實際上,少于2個block的文件不會被RAID。 </p> <h2> 參考資料 </h2> <p> <a href="/misc/goto?guid=4959625906349823655">HDFS-RAID Wiki</a> </p> <p> <a href="/misc/goto?guid=4959628502970569861">HDFS-RAID</a> </p> <p> <a href="/misc/goto?guid=4959628503186984124">Hadoop 2.5.0</a> </p>