一篇學會Hadoop3數(shù)據(jù)容錯技術
本文轉載自微信公眾號「大數(shù)據(jù)技術派」,作者柯少。轉載本文請聯(lián)系大數(shù)據(jù)技術派公眾號。
背景
隨著大數(shù)據(jù)技術的發(fā)展,HDFS作為Hadoop的核心模塊之一得到了廣泛的應用。為了數(shù)據(jù)的可靠性,HDFS通過多副本機制來保證。在HDFS中的每一份數(shù)據(jù)都有兩個副本,1TB的原始數(shù)據(jù)需要占用3TB的磁盤空間,存儲利用率只有1/3。而且系統(tǒng)中大部分是使用頻率非常低的冷數(shù)據(jù),卻和熱數(shù)據(jù)一樣存儲3個副本,給存儲空間和網(wǎng)絡帶寬帶來了很大的壓力。因此,在保證可靠性的前提下如何提高存儲利用率已成為當前HDFS面對的主要問題之一。Hadoop 3.0 引入了糾刪碼技術(Erasure Coding),它可以提高50%以上的存儲利用率,并且保證數(shù)據(jù)的可靠性。糾刪碼技術(Erasure coding)簡稱EC,是一種編碼容錯技術。最早用于通信行業(yè),數(shù)據(jù)傳輸中的數(shù)據(jù)恢復。它通過對數(shù)據(jù)進行分塊,然后計算出校驗數(shù)據(jù),使得各個部分的數(shù)據(jù)產(chǎn)生關聯(lián)性。當一部分數(shù)據(jù)塊丟失時,可以通過剩余的數(shù)據(jù)塊和校驗塊計算出丟失的數(shù)據(jù)塊。
原理
Reed-Solomon(RS)碼是存儲系統(tǒng)較為常用的一種糾刪碼,它有兩個參數(shù)k和m,記為RS(k,m)。如下圖所示,k個數(shù)據(jù)塊組成一個向量被乘上一個生成矩陣(Generator Matrix)GT從而得到一個碼字(codeword)向量,該向量由k個數(shù)據(jù)塊和m個校驗塊構成。如果一個數(shù)據(jù)塊丟失,可以用(GT)-1乘以碼字向量來恢復出丟失的數(shù)據(jù)塊。RS(k,m)最多可容忍m個塊(包括數(shù)據(jù)塊和校驗塊)丟失。圖片比如:我們有 7、8、9 三個原始數(shù)據(jù),通過矩陣乘法,計算出來兩個校驗數(shù)據(jù) 50、122。這時原始數(shù)據(jù)加上校驗數(shù)據(jù),一共五個數(shù)據(jù):7、8、9、50、122,可以任意丟兩個,然后通過算法進行恢復。
比如:我們有 7、8、9 三個原始數(shù)據(jù),通過矩陣乘法,計算出來兩個校驗數(shù)據(jù) 50、122。這時原始數(shù)據(jù)加上校驗數(shù)據(jù),一共五個數(shù)據(jù):7、8、9、50、122,可以任意丟兩個,然后通過算法進行恢復。
- 7 x
- 50 y
- x + 2*8 + 3 * 9 = y
- 4x + 5*8 + 6 * 9 = 122
HDFS EC 方案
傳統(tǒng)模式下HDFS中文件的基本構成單位是block,而EC模式下文件的基本構成單位是block group。以RS(3,2)為例,每個block group包含3個數(shù)據(jù)塊,2個校驗塊。
連續(xù)布局(Contiguous Layout)
文件數(shù)據(jù)被依次寫入塊中,一個塊寫滿之后再寫入下一個塊,這種分布方式稱為連續(xù)布局。優(yōu)點:
- 容易實現(xiàn)
- 方便和多副本存儲策略進行轉換
缺點:
- 需要客戶端緩存足夠的數(shù)據(jù)塊
- 不適合存儲小文件
條形布局(Striping Layout)
條(stripe)是由若干個相同大小的單元(cell)構成的序列。文件數(shù)據(jù)被依次寫入條的各個單元中,當一個條寫滿之后再寫入下一個條,一個條的不同單元位于不同的數(shù)據(jù)塊中。這種分布方式稱為條形布局。優(yōu)點:
- 客戶端緩存數(shù)據(jù)較少
- 無論文件大小都適用
缺點:
- 會影響一些位置敏感任務的性能,因為原先在一個節(jié)點上的塊被分散到了多個不同的節(jié)點上
- 和多副本存儲策略轉換比較麻煩
HDFS EC 開發(fā)計劃
整個HDFS EC項目主要分為兩個階段:1、用戶可以讀和寫一個條形布局(Striping Layout)的文件;如果該文件的一個塊丟失,后臺能夠檢查出并恢復;如果在讀的過程中發(fā)現(xiàn)數(shù)據(jù)丟失,能夠立即解碼出丟失的數(shù)據(jù)從而不影響讀操作。2、支持將一個多副本模式(HDFS原有模式)的文件轉換成連續(xù)布局(Contiguous Layout),以及從連續(xù)布局轉換成多副本模式。第一階段 HDFS-7285 已經(jīng)實現(xiàn),第二階段 HDFS-8030 正在進行中。
糾刪碼策略
RS-10-4-1024k:使用RS編碼,每10個數(shù)據(jù)單元(cell),生成4個校驗單元,共14個單元,也就是說:這14個單元中,只要有任意的10個單元存在(不管是數(shù)據(jù)單元還是校驗單元,只要總數(shù)=10),就可以得到原始數(shù)據(jù)。每個單元的大小是1024k=10241024=1048576。RS-3-2-1024k:使用RS編碼,每3個數(shù)據(jù)單元,生成2個校驗單元,共5個單元,也就是說:這5個單元中,只要有任意的3個單元存在(不管是數(shù)據(jù)單元還是校驗單元,只要總數(shù)=3),就可以得到原始數(shù)據(jù)。每個單元的大小是1024k=10241024=1048576。RS-6-3-1024k:使用RS編碼,每6個數(shù)據(jù)單元,生成3個校驗單元,共9個單元,也就是說:這9個單元中,只要有任意的6個單元存在(不管是數(shù)據(jù)單元還是校驗單元,只要總數(shù)=6),就可以得到原始數(shù)據(jù)。每個單元的大小是1024k=10241024=1048576。RS-LEGACY-6-3-1024k:策略和上面的RS-6-3-1024k一樣,只是編碼的算法用的是rs-legacy,應該是之前遺留的rs算法。XOR-2-1-1024k:使用XOR編碼(速度比RS編碼快),每2個數(shù)據(jù)單元,生成1個校驗單元,共3個單元,也就是說:這3個單元中,只要有任意的2個單元存在(不管是數(shù)據(jù)單元還是校驗單元,只要總數(shù)=2),就可以得到原始數(shù)據(jù)。每個單元的大小是1024k=10241024=1048576。
以RS-6-3-1024k為例,6個數(shù)據(jù)單元+3個校驗單元,可以容忍任意的3個單元丟失,冗余的數(shù)據(jù)是50%。而采用副本方式,3個副本,冗余200%,卻還不能容忍任意的3個單元丟失。因此,RS編碼在相同冗余度的情況下,會大大提升數(shù)據(jù)的可用性,而在相同可用性的情況下,會大大節(jié)省冗余空間。
糾刪碼基本操作
1.查看當前支持的糾刪碼策略,命令如下:
- [user@nn1 ~]$ hdfs ec -listPolicies
- Erasure Coding Policies:
- ErasureCodingPolicy=[Name=RS-10-4-1024k, Schema=[ECSchema=[Codec=rs, numDataUnits=10, numParityUnits=4]], CellSize=1048576, Id=5, State=DISABLED]
- ErasureCodingPolicy=[Name=RS-3-2-1024k, Schema=[ECSchema=[Codec=rs, numDataUnits=3, numParityUnits=2]], CellSize=1048576, Id=2, State=DISABLED]
- ErasureCodingPolicy=[Name=RS-6-3-1024k, Schema=[ECSchema=[Codec=rs, numDataUnits=6, numParityUnits=3]], CellSize=1048576, Id=1, State=DISABLED]
- ErasureCodingPolicy=[Name=RS-LEGACY-6-3-1024k, Schema=[ECSchema=[Codec=rs-legacy, numDataUnits=6, numParityUnits=3]], CellSize=1048576, Id=3, State=DISABLED]
- ErasureCodingPolicy=[Name=XOR-2-1-1024k, Schema=[ECSchema=[Codec=xor, numDataUnits=2, numParityUnits=1]], CellSize=1048576, Id=4, State=DISABLED]
2.設置糾刪碼策略
糾刪碼策略是與具體的路徑(path)相關聯(lián)的。也就是說,如果我們要使用糾刪碼,則要給一個具體的路徑設置糾刪碼策略,后續(xù),所有往此目錄下存儲的文件,都會執(zhí)行此策略。例子如下 首先在/下創(chuàng)建目錄rs-6-3,然后查看其是否設置了糾刪碼策略,結果顯示沒有指定策略(新建的目錄不會指定策略)。
- hdfs://bigdata/dn1/path
- hdfs://bigdata/dn1/path2
- [user@nn1 ~]$ hdfs dfs -mkdir /rs-6-3
- [user@nn1 ~]$ hdfs ec -getPolicy -path /rs-6-3
- The erasure coding policy of /rs-6-3 is unspecified
接下來,給此目錄設置糾刪碼策略RS-6-3-1024k,此策略名是從前面list策略中查到的??梢钥吹揭呀?jīng)設置成功。
- [user[@nn1 ](/nn1 ) ~]$ hdfs ec -setPolicy -path /rs-6-3 -policy RS-6-3-1024k
- Set erasure coding policy RS-6-3-1024k on /rs-6-3
注意:RS-6-3-1024k可以直接設置成功,其它的策略需要enable后,才能設置:設置RS-3-2-1024k,這個需要先enablePolicy
- [user[@nn1 ](/nn1 ) hadoop-3.0.0-beta1]$ hdfs ec -enablePolicy -policy RS-3-2-1024k
- Erasure coding policy RS-3-2-1024k is enabled
- [user[@nn1 ](/nn1 ) hadoop-3.0.0-beta1]$ hdfs ec -setPolicy -path /rs-3-2 -policy RS-3-2-1024k
- Set erasure coding policy RS-3-2-1024k on /rs-3-2
驗證:
- [user[@nn1 ](/nn1 ) hadoop-3.0.0-beta1]$ hdfs ec -getPolicy -path /rs-3-2
- RS-3-2-1024k
設置RS-10-4-1024k,如果不enablePolicy,會報錯
- [user[@nn1 ](/nn1 ) hadoop-3.0.0-beta1]$ hdfs dfs -mkdir /rs-10-4
- [user[@nn1 ](/nn1 ) hadoop-3.0.0-beta1]$ hdfs ec -setPolicy -path /rs-10-4 -policy RS-10-4-1024k
報錯了 RemoteException: Policy 'RS-10-4-1024k' does not match any enabled erasure coding policies: [RS-3-2-1024k, RS-6-3-1024k]. An erasure coding policy can be enabled by enableErasureCodingPolicy API.
3.上傳文件,查看文件編碼情況
下面我們上傳一個文件看一下,這里提示我們沒有使用ISA-L支持的編碼器(這個編碼器和CPU優(yōu)化相結合,效率更高,需要重新編譯和配置,我們后續(xù)再講)
- [user@nn1 ~]$ hdfs dfs -cp /profile /rs-6-3/
- 2017-11-30 10:24:29,620 WARN erasurecode.ErasureCodeNative: ISA-L support is not available in your platform... using builtin-java codec where applicable
查看profile編碼后的分布
- [user@nn1 ~]$ hdfs fsck /rs-6-3/profile -files -blocks -locations
輸出:
- Connecting to namenode via http://nn1:9870/fsck?ugi=user&files=1&blocks=1&locations=1&path=%2Frs-6-3%2Fprofile
- FSCK started by user (auth:SIMPLE) from /192.168.182.11 for path /rs-6-3/profile at Thu Nov 30 10:57:12 EST 2017
- /rs-6-3/profile 1872 bytes, erasure-coded: policy=RS-6-3-1024k, 1 block(s): OK
- 0. BP-529485104-192.168.182.11-1511810134643:blk_-9223372036854775792_1065 len=1872 Live_repl=4 [blk_-9223372036854775792:DatanodeInfoWithStorage[192.168.182.11:9866,DS-da58ee3e-adcc-4f6c-8488-c2a0b742d8b9,DISK], blk_-9223372036854775786:DatanodeInfoWithStorage[192.168.182.20:9866,DS-c36de658-0f5a-42de-8898-eab3b04c7016,DISK], blk_-9223372036854775785:DatanodeInfoWithStorage[192.168.182.14:9866,DS-a3569982-de52-42b5-8543-94578f8b452a,DISK], blk_-9223372036854775784:DatanodeInfoWithStorage[192.168.182.19:9866,DS-71be9468-c0c7-437c-8b59-ece27593b4c2,DISK]]
查看block文件的信息,可以看到nn1上block的大小正好是1872。這是因為1872<1024k,因此無法分割,直接整體編碼。
- [user@nn1 ~]$ ls dfs/share/datanode/current/BP-529485104-192.168.182.11-1511810134643/current/finalized/subdir0/subdir0/blk_-9223372036854775792 -l
- -rw-rw-r--. 1 user user 1872 Nov 30 10:24 dfs/share/datanode/current/BP-529485104-192.168.182.11-1511810134643/current/finalized/subdir0/subdir0/blk_-9223372036854775792
Live_repl=4的解釋,表示此文件共有4個副本,其中1個是原始數(shù)據(jù),3個是校驗數(shù)據(jù),因此,這里的策略是rs_6_3,要保證冗余3個校驗單元,原始數(shù)據(jù)1872<1024k,只能構成1個數(shù)據(jù)單元,再加上3個校驗單元,就是4個副本了。1 block(s)的解釋:blocks是指數(shù)據(jù)單元在datanode的存儲而言,1872<1024k,只有1個數(shù)據(jù)單元,因此只能分配到1個datanode,對于每個datanode,其block默認大小是256MB(hdfs3.0是256MB,hdfs2.x是128MB),1872遠小于256MB,當然只有1個block了,如果單個datanode上多個數(shù)據(jù)單元之和>256MB,這時才會生成新的block。再看一個
- hdfs dfs -cp file:///home/user/jdk1.8.0_152/lib/ant-javafx.jar /rs-6-3/
此文件的大小是1224175>1024k,但是<2*1024k,也就是可以構成2個數(shù)據(jù)單元,加上3個校驗單元,推測最終編碼出來一共是5個副本。查看下
- [user@nn1 ~]$ hdfs fsck /rs-6-3/ant-javafx.jar -files -blocks -locations
果然是
- /rs-6-3/ant-javafx.jar 1224175 bytes, erasure-coded: policy=RS-6-3-1024k, 1 block(s): OK
- 0. BP-529485104-192.168.182.11-1511810134643:blk_-9223372036854775776_1066 len=1224175 Live_repl=5 [blk_-9223372036854775776:DatanodeInfoWithStorage[192.168.182.11:9866,DS-da58ee3e-adcc-4f6c-8488-c2a0b742d8b9,DISK], blk_-9223372036854775775:DatanodeInfoWithStorage[192.168.182.18:9866,DS-2dc5d603-ad42-4558-bfda-c9a597f88f06,DISK], blk_-9223372036854775770:DatanodeInfoWithStorage[192.168.182.14:9866,DS-a3569982-de52-42b5-8543-94578f8b452a,DISK], blk_-9223372036854775769:DatanodeInfoWithStorage[192.168.182.20:9866,DS-c36de658-0f5a-42de-8898-eab3b04c7016,DISK], blk_-9223372036854775768:DatanodeInfoWithStorage[192.168.182.13:9866,DS-118ae8da-f820-447c-9d97-dbe4f33bff39,DISK]]
查看第一個block的大小(nn1),可以看到正好是按照1024k來切分的
- [user@nn1 ~]$ ls dfs/share/datanode/current/BP-529485104-192.168.182.11-1511810134643/current/finalized/subdir0/subdir0/blk_-9223372036854775776 -l
- -rw-rw-r--. 1 user user 1048576 Nov 30 10:30 dfs/share/datanode/current/BP-529485104-192.168.182.11-1511810134643/current/finalized/subdir0/subdir0/blk_-9223372036854775776
查看第二個block的大小(nn7),其大小是175599
- [user@dn7 ~]$ ls dfs/share/datanode/current/BP-529485104-192.168.182.11-1511810134643/current/finalized/subdir0/subdir0/blk_-9223372036854775775 -l
- -rw-rw-r--. 1 user user 175599 Nov 30 11:54 dfs/share/datanode/current/BP-529485104-192.168.182.11-1511810134643/current/finalized/subdir0/subdir0/blk_-9223372036854775775
第一個block 1048576 + 第二個block 175599 = 1224175,正好是ant-javafx.jar的大小。為什么第二個block沒有補齊1024k呢?因為補齊的話,也是填0,沒有必要。第三個block~第五個block是校驗數(shù)據(jù)。
數(shù)據(jù)恢復驗證(datanode dead的時間間隔是10m)
我們以ant-javafx.jar為例,它有5個副本,分布在:
- 192.168.182.11
- 192.168.182.18
- 192.168.182.14
- 192.168.182.20
- 192.168.182.13
其中2個原始數(shù)據(jù)單元、3個校驗數(shù)據(jù)單元,意味著可以容忍任意3個數(shù)據(jù)單元的丟失。下面,我們關閉后3個節(jié)點上的datanode
- 192.168.182.14
- 192.168.182.20
- 192.168.182.13
然后從/rs-6-3目錄中復制ant-javafx.jar到本地/tmp目錄,并和本地的ant-javafx.jar比較,正確,說明數(shù)據(jù)沒有問題。
- [user@nn1 ~]$ hdfs dfs -cp /rs-6-3/ant-javafx.jar file:///tmp/
- 2017-11-30 13:12:36,493 WARN erasurecode.ErasureCodeNative: ISA-L support is not available in your platform... using builtin-java codec where applicable
- [user@nn1 ~]$ diff jdk1.8.0_152/lib/ant-javafx.jar /tmp/ant-javafx.jar
再關掉一個節(jié)點,在下面的節(jié)點
- 192.168.182.18
運行
- [user@dn7 ~]$ hdfs --daemon stop datanode
在nn1上再次復制 報錯,因為丟失的數(shù)據(jù)單元個數(shù)>3了
- cp: 4 missing blocks, the stripe is: Offset=0, length=175599, fetchedChunksNum=0, missingChunksNum=4
在dn3上啟動datanode,再次復制 發(fā)現(xiàn)還是報錯,說192.168.182.18上數(shù)據(jù)丟失,這是為什么呢?查看HDFS狀態(tài),發(fā)現(xiàn)剛才關閉的dn3 dn9 dn2 dn7仍然是live的,這是因為datanode的狀態(tài)有一個刷新的間隔,這個間隔默認是10m(600s),只有10m沒有收到datanode的消息,namenode才認為此datanode是dead的。因此,等待10m后,可以看到HDFS的live nodes變成了7。
這個時候,再次復制,DFSClient就知道dn7是dead,就不會再選擇dn7了,轉而選擇其它的live節(jié)點,因此復制成功。
- [user@nn1 ~]$ hdfs dfs -cp /rs-6-3/ant-javafx.jar file:///tmp/
- 2017-11-30 13:26:35,241 WARN erasurecode.ErasureCodeNative: ISA-L support is not available in your platform... using builtin-java codec where applicable
- cp: `file:///tmp/ant-javafx.jar': File exists
將dn2、dn7、dn9恢復,啟動datanode,再次查看。
- [user@nn1 ~]$ hdfs fsck /rs-6-3/ant-javafx.jar -files -blocks -locations
- Connecting to namenode via http://nn1:9870/fsck?ugi=user&files=1&blocks=1&locations=1&path=%2Frs-6-3%2Fant-javafx.jar
- FSCK started by user (auth:SIMPLE) from /192.168.182.11 for path /rs-6-3/ant-javafx.jar at Thu Nov 30 13:29:30 EST 2017
- /rs-6-3/ant-javafx.jar 1224175 bytes, erasure-coded: policy=RS-6-3-1024k, 1 block(s): OK
- 0. BP-529485104-192.168.182.11-1511810134643:blk_-9223372036854775776_1066 len=1224175 Live_repl=5 [blk_-9223372036854775776:DatanodeInfoWithStorage[192.168.182.11:9866,DS-da58ee3e-adcc-4f6c-8488-c2a0b742d8b9,DISK], blk_-9223372036854775770:DatanodeInfoWithStorage[192.168.182.14:9866,DS-a3569982-de52-42b5-8543-94578f8b452a,DISK], blk_-9223372036854775769:DatanodeInfoWithStorage[192.168.182.19:9866,DS-71be9468-c0c7-437c-8b59-ece27593b4c2,DISK], blk_-9223372036854775768:DatanodeInfoWithStorage[192.168.182.16:9866,DS-c32fdd4e-aa34-4b65-b192-643ade06d71b,DISK], blk_-9223372036854775775:DatanodeInfoWithStorage[192.168.182.18:9866,DS-2dc5d603-ad42-4558-bfda-c9a597f88f06,DISK]]
發(fā)現(xiàn)數(shù)據(jù)單元的分布發(fā)生了變化。
- 192.168.182.11
- 192.168.182.14
- 192.168.182.19
- 192.168.182.16
- 192.168.182.18
其中綠色部分,應該是在這些節(jié)點關閉后,hdfs重新啟動譯碼和編碼,將原來丟失的數(shù)據(jù)補到了dn8和dn5上。而dn8沒有去掉,可能是還沒來得及??傊?,如果編碼后的stripe中,有數(shù)據(jù)丟失,hdfs會自動啟動恢復工作。