Hadoop 3.0 EC技術
EC的設計目標
- Hadoop默認的3副本方案需要額外的200%的存儲空間、和網絡IO開銷
- 而一些較低I/O的warn和cold數據,副本數據的訪問是比較少的(hot數據副本會被用於計算)
- EC可以提供同級別的容錯能力,存儲空間要少得多(官方宣傳不到50%),使用了EC,副本始終為1
EC背景
EC在RAID應用
- EC在RAID也有應用,RAID通過EC將文件划分為更小的單位,例如:可以按照bit、byte或者block來划分。
- 然后將這些條紋單元存儲在不同的磁盤中
條紋單元:官方稱之為Stripe Unit,我把它隱喻為斑馬身上的黑白條紋,就稱每個文件經過EC處理后的就是一個個的條紋單元。

EC編碼奇偶校驗單元

根據剩余條紋單元和奇偶校驗單元恢復數據。

EC與HDFS
一個具有6個塊,3副本會消耗6 x 3 = 18個塊存儲空間。而EC只需要 6個Block,再加上3個奇偶校驗,僅需要6 + 3 = 9個塊。節省了一半的存儲空間。
EC在Hadoop架構的調整
使用EC有幾個重要優勢:
- Online-EC,在寫入數據的時候就是以EC方式寫入的,而不是先存完數據再開始進行EC編碼處理(offline-EC)。
- Online-EC將一個小文件分發到多個DataNode,而不是將多個文件放到一個編碼組中。這樣,刪除數據、Qutoa、數據遷移是更容易的。
NameNode元數據存儲
基於EC的文件存儲與Hadoop經典分塊存儲方式做了調整。

基於條紋的HDFS存儲邏輯上是由Block Group(塊組組成),每個Block Group包含了一定數量的Internal Block(后續我們稱為EC Block)。如果一個文件有很多的EC Block,會占用NameNode較大的內存空間。HDFS引入了新的分層Block命名協議,通過Block的ID可以推斷出Block Group的ID,NameNode是基於Block Group而不是EC Block級別管理。

Client
客戶端讀取、寫入HDFS也做了調整,當以Online-EC寫入一個文件時,是以並行方式來處理Block Group中的Internal Block。
- 當寫入文件時,數據流通過DFSStripedOutputStream實現,會管理一組數據流,每個DataNode節點都對應一個數據流,並將Block Group的一個Internal Block存儲。這些流操作都是異步進行的。還有一個Coordinator,負責一個文件的所有Block Group操作,包括:結束當前的Block Group、分配新的Block Group等。
- 當讀取文件時,數據流通過DFSStripedInputStream實現,它將請求的文件轉換為存儲在DataNode的Internal Block,然后進行並行讀取,出現故障時,發出奇偶校驗數據請求來進行解碼恢復Internal Block。
DataNode
DataNode上運行一個ErasureCodingWorker(ECWorker)任務,專門用於失敗的EC Block進行后台數據恢復。一旦NameNode檢測到失敗的EC Block,NameNode會選擇一個DataNode進行數據恢復。
- 先從錯誤數據所在的Block Group數據節點中讀取正常的EC Block作為輸入,基於EC策略,通過最小的EC Block進行數據恢復。
- 從輸入的EC Block以及奇偶校驗碼塊進行解碼數據恢復,生成正常的EC Block。
- EC解碼完成后,將恢復的EC Block傳輸到對應的DataNode中。
EC存儲方案
EC編碼和解碼
EC編解碼器是對EC Block上的條紋單元進行處理。編碼器將EC Block中的多個條紋單元作為輸入,並輸出許多奇偶校驗單元。這個過程稱為編碼。條紋單元和奇偶校驗單元稱為EC編碼組。

解碼的過程就是恢復數據的過程,可以通過剩余的條紋單元和奇偶校驗單元來恢復數據。

容錯性和存儲效率
在比較不同的存儲方案時,需要考慮兩個重要因素:
- 數據的容錯性(最多可允許同時出現多少故障來衡量,容錯性越大越好)
- 存儲效率(邏輯大小除以物理數據存儲大小,存儲效率也是越大越好)
HDFS中副本方案的容錯性為:有N個副本,就可以容忍N-1同時發生故障。存儲效率為:1/N。
下面這張表格,是針對不同的存儲方案的數據容錯性和存儲效率。

可以看出來,XOR只能容忍一個數據塊出現故障,而RS-6-3、RS-10-4允許3個、4個數據塊同時出現故障。但XOR的存儲效率是最高的、其次是RS-10-4、再下來是RS-6-3。而3副本的存儲效率是33%。
連續存儲還是條紋單元存儲

這張圖對比了連續存儲和條紋存儲方案在HDFS的示意圖。可以明顯的看到,條紋存儲方案,將一個Block繼續分解為一個個的條紋單元。並在一組DataNode的block中,循環寫入條紋單元。基於連續存儲或者條紋存儲,都是支持EC的。EC的方式存儲效率比較高,但增加了復雜度、以及較高消耗的故障恢復。條紋存儲方案比連續存儲更好的I/O吞吐量。但與傳統的MapReduce本地數據讀取相悖,因為數據都是跨網絡存儲的。讀取數據需要更多的網絡I/O開銷。
連續存儲
連續存儲容易實現,讀寫的方式與副本方式非常類似。但只有文件很大的場景適用。例如:使用RS-10-4,一個128M的文件仍然要寫入4個128M的就校驗塊。存儲開銷為400%。這種方式,客戶端需要有GB級別的緩存來計算奇偶校驗單元。
條紋存儲
條紋存儲對小文件是友好的,可以節省很多空間。條紋單元大小通常是(64KB或者1MB)。客戶端只需要有幾MB的緩存就可以用於計算奇偶校驗單元。但這種方案,需要跨網絡I/O,性能會因此下降。要提升處理效率,需要將數據轉換為連續存儲,但這就需要重寫整個文件了。
所以文件大小決定了使用哪種方式更合適。Cloudera做了一些調研,發現其實HDFS中小文件(少於一個EC Block Group)的使用率占整個集群的36%-97%。小文件的處理更重要。所以HDFS EC使用的是條紋存儲的EC存儲方案。

EC策略關鍵屬性
為了適應不同的業務需求,在HDFS中可以針對文件、目錄配置不同的副本和EC策略。EC策略實現了如何對文件進行編碼/解碼方式。每個策略包含以下屬性:
- EC schema:EC schema包含了EC Group中的EC Block數量以及奇偶校驗Block的數量,例如:6+3,以及編解碼器算法,例如:Reed-Solomon(索羅蒙算法)、XOR(異或算法)。
- 條紋單元大小(EC Block):條紋單元的大小決定了條紋單元的讀取、寫入速度、Buffer的大小、以及編碼的效率。
EC策略命名
EC策略命名策略:EC編碼器-EC Block數量-奇偶校驗Block數量-條紋單元大小。Hadoop中內置了5種策略:
- RS-3-2-1024K
- RS-6-3-1024K
- RS-10-4-1024K
- RS-LEGACY-6-3-1024K
- XOR-2-1-1024K
同時,默認的副本策略也是支持的。副本策略設置在目錄上,這樣可以前置目錄使用3副本方案,指定該目錄不繼承EC編碼策略。這樣,目錄中是可以切換副本存儲方式的。
online-EC
Replication存儲方式是始終啟用的,默認啟用的EC策略是:RS-6-3-1024K。與Replication存儲方式一樣,如果父目錄設置了EC策略,子文件/目錄會繼承父目錄的EC策略。
目錄級別的EC策略僅會影響在目錄中創建的新文件,這也意味着就的文件不會重新進行EC編碼,HDFS是使用online-EC,文件一旦創建,可以查詢它的EC策略,但不能再更改
如果將已經進行EC編碼的文件移動到其他EC策略的目錄,文件的EC編碼也不會改變。如果想要將文件轉換為其他的EC策略,需要重寫數據。可以通過distcp來移動數據,而不是mv。
自定義EC策略
HDFS允許用戶基於XML自己來定義EC策略。例如:
<?xml version="1.0"?>
<configuration>
<!-- The version of EC policy XML file format, it must be an integer -->
<layoutversion>1</layoutversion>
<schemas>
<!-- schema id is only used to reference internally in this document -->
<schema id="XORk2m1">
<!-- The combination of codec, k, m and options as the schema ID, defines
a unique schema, for example 'xor-2-1'. schema ID is case insensitive -->
<!-- codec with this specific name should exist already in this system -->
<codec>xor</codec>
<k>2</k>
<m>1</m>
<options> </options>
</schema>
<schema id="RSk12m4">
<codec>RS</codec>
<k>12</k>
<m>4</m>
<options> </options>
</schema>
<schema id="RS-legacyk12m4">
<codec>RS-legacy</codec>
<k>12</k>
<m>4</m>
<options> </options>
</schema>
</schemas>
<policies>
<policy>
<!-- the combination of schema ID and cellsize(in unit k) defines a unique
policy, for example 'xor-2-1-256k', case insensitive -->
<!-- schema is referred by its id -->
<schema>XORk2m1</schema>
<!-- cellsize must be an positive integer multiple of 1024(1k) -->
<!-- maximum cellsize is defined by 'dfs.namenode.ec.policies.max.cellsize' property -->
<cellsize>131072</cellsize>
</policy>
<policy>
<schema>RS-legacyk12m4</schema>
<cellsize>262144</cellsize>
</policy>
</policies>
</configuration>
配置文件很容易理解,主要包含兩個部分組成:
- EC Schema:編碼器、k(EC Block數量)、m(奇偶校驗Block數量)
- Policy:綁定schema、以及指定條紋單元大小
RS-legacy:遺留的,基於純Java語言實現的EC編解碼器
而HDFS默認的RS和XOR編解碼器是基於Native實現的。
XOR算法與RS算法
XOR算法
XOR(異或)算法是最簡單的EC實現,可以從任意數量的數據生成1個奇偶校驗位。例如:1 ⊕ 0 ⊕ 1 ⊕ 1 = 1。但針對任意數量的條紋單元僅生成一個奇偶校驗位。HDFS中如果出現多個故障,這種恢復方式是不夠的。XOR的容錯能力為1,存儲效率為75%。

如果某一個X、Y對丟失,可以通過奇偶檢驗位進行異或來恢復。
Reed-Solomon算法
RS算法克服了XOR算法的限制,基於線性代數運算來生成多個奇偶校驗位,可以容忍多個失敗。RS 算法使用生成矩陣(GT,Generator Matrix)與 m 個數據單元相乘,以獲得具有 m 個數據單元(data cells)和 n 個奇偶校驗單元(parity cells)的 extended codewords。RS算法的容錯能力最高為n。存儲效率為 m / m + n。例如:RS-6-3為67%的存儲效率,而:RS-3-2為60%的存儲效率。

上圖可以看到,RS是使用復雜的線性代碼運算來生成多個奇偶校驗單元,可以容忍每個組出現多個故障。一般生產環境都是使用RS算法。RS-k-m是將k個條紋單元與生成矩陣Gt相乘,生成具有k個條紋單元和m個奇偶校驗單元。只要k + m個單元的k個可用,就可以通過剩余的條紋單元乘以Gt的倒數恢復存儲失敗。可以容忍m個數據單元的故障。
部署HDFS EC
集群配置要求
- EC對Hadoop集群的CPU、網絡有額外的要求。EC編碼、解碼會消耗HDFS客戶端、DataNode更多的CPU資源
- EC要求集群中的DataNode最起碼和EC條紋寬度(條紋寬度 = EC Block數量 + 奇偶校驗Block數量)是一樣的。也就是,如果我們用使用RS-6-3策略,至少需要9台DataNode。
- EC Block文件也是分布在整個機架上,以實現機架級別的容錯。在讀寫EC Block文件時,也需要保證機架的帶寬。如果要實現機架級別的容錯,需要擁有一定數量的機架容錯。每個機架所存放的EC Block不能超過就校驗塊的數量。機架數量計算公式為:(EC Block數量 + 奇偶校驗塊數量)/ 奇偶校驗塊數量,然后四舍五入。例如:針對RS-6-3如果要實現機架級別的容錯,至少需要(6 + 3)/ 3 = 3個機架。如果機架數小於這個數,將無法保證機架級別的容錯。如果有進行機架級別停機維護需求,官方建議提供6 + 3以上個機架。
EC配置
默認,除了dfs.namenode.ec.system.default.policy指定的默認策略,其他的內置的EC策略都是禁用的。我們可以根據Hadoop集群的大小、以及所需的容錯屬性,通過hdfs ec -enablePolicy -policy 策略名稱來啟用EC策略。例如:如果有5個節點的集群,比較適合的就是RS-3-2-1024k,而RS-10-4-1024k策略就不合適了。
默認dfs.namenode.ec.system.default.policy為RS-6-3-1024k。
# 讀取EC Block(條紋文件)的超時時間,默認5000毫秒
dfs.datanode.ec.reconstruction.stripedread.timeout.millis
# 讀取EC Block的緩存大小,默認為64KB
dfs.datanode.ec.reconstruction.stripedread.buffer.size
# 用於DataNode重建EC Block的線程數量,默認為8個線程
dfs.datanode.ec.reconstruction.threads
# EC后台恢復任務數與復制Block的Xmits權重。NameNode基於這個Xmits值來調度任務到DataNode。默認為0.5,設置為0表示禁用EC恢復任務計算權重,始終都是1 Xmits。它的值為:讀取正常EC Block的數量、以及暑輸出新的EC Block的最大值。例如:如果讀取6個正常的EC Block,輸出到兩個EC Block,則Xmits值為:Max(6, 2) * 0.5 = 3。
dfs.datanode.ec.reconstruction.xmits.weight
EC命令
EC相關的操作,使用hdfs ec命令。
hdfs ec [generic options]
[-setPolicy -path <path> [-policy <policyName>] [-replicate]]
[-getPolicy -path <path>]
[-unsetPolicy -path <path>]
[-listPolicies]
[-addPolicies -policyFile <file>]
[-listCodecs]
[-enablePolicy -policy <policyName>]
[-disablePolicy -policy <policyName>]
[-verifyClusterSetup -policy <policyName>...<policyName>]
[-help [cmd ...]]
1、查看當前HDFS支持的ec策略
[root@node1 hadoop]# 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=ENABLED
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=ENABLED
我們看到目前我的HDFS集群上面啟用了兩個策略:一個是RS-6-3-1024k、一個是XOR-2-1-1024k。
2、查看當前HDFS支持的編解碼器
[root@node1 hadoop]# hdfs ec -listCodecs
Erasure Coding Codecs: Codec [Coder List]
RS [RS_NATIVE, RS_JAVA]
RS-LEGACY [RS-LEGACY_JAVA]
XOR [XOR_NATIVE, XOR_JAVA]
3、設置EC編碼策略。因為我的測試集群只有3個節點,所以只能使用XOR-2-1-1024k。先要將XOR-2-1-1024k啟用。
-- 創建用於存放冷數據的目錄
[root@node1 hadoop]# hdfs dfs -mkdir -p /workspace/feng/cold_data
-- 啟用XOR-2-1-1024 EC策略
[root@node1 hadoop]# hdfs ec -enablePolicy -policy XOR-2-1-1024k
Erasure coding policy XOR-2-1-1024k is enabled
-- 驗證當前集群是否支持所有啟用的或者指定的EC策略(這個命令應該是3.2.x添加的,我當前是3.1.4,還不支持這個命令)
-- hdfs ec -verifyClusterSetup -policy XOR-2-1-1024k
-- 設置冷數據EC存儲策略
[root@node1 hadoop]# hdfs ec -setPolicy -path /workspace/feng/cold_data -policy XOR-2-1-1024k
Set XOR-2-1-1024k erasure coding policy on /workspace/feng/cold_data
-- 查看冷數據目錄的存儲策略
[root@node1 hadoop]# hdfs ec -getPolicy -path /workspace/feng/cold_data
XOR-2-1-1024k
驗證測試
新上傳一個293M的文件到冷數據目錄
[root@node1 software]# hdfs dfs -put hadoop-3.1.4.tar.gz /workspace/feng/cold_data
2021-01-16 14:23:28,681 WARN erasurecode.ErasureCodeNative: ISA-L support is not available in your platform... using builtin-java codec where applicable
此處,Hadoop警告提示,當前我的操作系統平台,不支持ISA-L,默認RS、XOR使用的是Native方式進行編解碼,會基於Intel的ISA-L加速編解碼。
我們來查看下HDFS文件的Block的信息:
[root@node3 subdir2]# hdfs fsck /workspace/feng/cold_data/hadoop-3.1.4.tar.gz -files -blocks
我們看到文件是以XOR-2-1-1024k進行EC編碼,並且有兩個Block。總共有兩個EC Block Group。
0. BP-538037512-192.168.88.100-1600884040401:blk_-9223372036854775232_2020 len=268435456 Live_repl=3
1. BP-538037512-192.168.88.100-1600884040401:blk_-9223372036854775216_2021 len=38145321 Live_repl=3
總共的EC Block Group = 306580777字節,與原始的數據文件相等。
Erasure Coded Block Groups:
Total size: 306580777 B
Total files: 1
Total block groups (validated): 2 (avg. block group size 153290388 B)
Minimally erasure-coded block groups: 2 (100.0 %)
Over-erasure-coded block groups: 0 (0.0 %)
Under-erasure-coded block groups: 0 (0.0 %)
Unsatisfactory placement block groups: 0 (0.0 %)
Average block group size: 3.0
Missing block groups: 0
Corrupt block groups: 0
Missing internal blocks: 0 (0.0 %)
FSCK ended at Sat Jan 16 16:59:46 CST 2021 in 1 milliseconds
原始文件大小:
[root@node1 software]# ll hadoop-3.1.4.tar.gz
-rw-r--r-- 1 root root 306580777 Sep 25 09:29 hadoop-3.1.4.tar.gz
我們可以觀察看到當前的Block Group大小為:256MB。而我的HDFS集群配置的dfs block size是:128MB。
<property>
<name>dfs.blocksize</name>
<value>134217728</value>
<final>false</final>
<source>hdfs-default.xml</source>
</property>
因為當前的Block size是128MB,而EC的策略是:XOR-2-1,也就是一個Block Group 2個Block,所以Block Group的大小就是256MB了。
使用distcp遷移數據
假設現在需要將一個3副本存儲方式的文件,遷移到配置了EC策略的目錄中。
-- 創建一個用於測試的數據目錄
hdfs dfs -mkdir /workspace/feng/test_data
-- 上傳一個測試文件
hdfs dfs -put hbase-logs.zip /workspace/feng/test_data
-- 啟動YARN
start-yarn.sh
-- 使用distcp移動到EC策略的目錄中(此處要跳過檢驗和,因為使用EC編碼肯定校驗失敗)
hadoop distcp -update -skipcrccheck /workspace/feng/test_data/hbase-logs.zip /workspace/feng/cold_data
可以對比下該文件的block數據:
3副本方式文件
[root@node1 hadoop]# hdfs fsck /workspace/feng/test_data/hbase-logs.zip -files -blocks
Connecting to namenode via http://node1:9870/fsck?ugi=root&files=1&blocks=1&path=%2Fworkspace%2Ffeng%2Ftest_data%2Fhbase-logs.zip
FSCK started by root (auth:SIMPLE) from /192.168.88.100 for path /workspace/feng/test_data/hbase-logs.zip at Sat Jan 16 19:43:39 CST 2021
/workspace/feng/test_data/hbase-logs.zip 6970734 bytes, replicated: replication=3, 1 block(s): OK
0. BP-538037512-192.168.88.100-1600884040401:blk_1073742800_2023 len=6970734 Live_repl=3
Status: HEALTHY
Number of data-nodes: 3
Number of racks: 1
Total dirs: 0
Total symlinks: 0
Replicated Blocks:
Total size: 6970734 B
Total files: 1
Total blocks (validated): 1 (avg. block size 6970734 B)
Minimally replicated blocks: 1 (100.0 %)
Over-replicated blocks: 0 (0.0 %)
Under-replicated blocks: 0 (0.0 %)
Mis-replicated blocks: 0 (0.0 %)
Default replication factor: 3
Average block replication: 3.0
Missing blocks: 0
Corrupt blocks: 0
Missing replicas: 0 (0.0 %)
Erasure Coded Block Groups:
Total size: 0 B
Total files: 0
Total block groups (validated): 0
Minimally erasure-coded block groups: 0
Over-erasure-coded block groups: 0
Under-erasure-coded block groups: 0
Unsatisfactory placement block groups: 0
Average block group size: 0.0
Missing block groups: 0
Corrupt block groups: 0
Missing internal blocks: 0
FSCK ended at Sat Jan 16 19:43:39 CST 2021 in 1 milliseconds
The filesystem under path '/workspace/feng/test_data/hbase-logs.zip' is HEALTHY
EC編碼后的文件
[root@node1 hadoop]# hdfs fsck /workspace/feng/cold_data/hbase-logs.zip -files -blocks
Connecting to namenode via http://node1:9870/fsck?ugi=root&files=1&blocks=1&path=%2Fworkspace%2Ffeng%2Fcold_data%2Fhbase-logs.zip
FSCK started by root (auth:SIMPLE) from /192.168.88.100 for path /workspace/feng/cold_data/hbase-logs.zip at Sat Jan 16 19:42:51 CST 2021
/workspace/feng/cold_data/hbase-logs.zip 6970734 bytes, erasure-coded: policy=XOR-2-1-1024k, 1 block(s): OK
0. BP-538037512-192.168.88.100-1600884040401:blk_-9223372036854774560_2128 len=6970734 Live_repl=3
Status: HEALTHY
Number of data-nodes: 3
Number of racks: 1
Total dirs: 0
Total symlinks: 0
Replicated Blocks:
Total size: 0 B
Total files: 0
Total blocks (validated): 0
Minimally replicated blocks: 0
Over-replicated blocks: 0
Under-replicated blocks: 0
Mis-replicated blocks: 0
Default replication factor: 3
Average block replication: 0.0
Missing blocks: 0
Corrupt blocks: 0
Missing replicas: 0
Erasure Coded Block Groups:
Total size: 6970734 B
Total files: 1
Total block groups (validated): 1 (avg. block group size 6970734 B)
Minimally erasure-coded block groups: 1 (100.0 %)
Over-erasure-coded block groups: 0 (0.0 %)
Under-erasure-coded block groups: 0 (0.0 %)
Unsatisfactory placement block groups: 0 (0.0 %)
Average block group size: 3.0
Missing block groups: 0
Corrupt block groups: 0
Missing internal blocks: 0 (0.0 %)
FSCK ended at Sat Jan 16 19:42:51 CST 2021 in 1 milliseconds
基於Hive使用EC
基於副本冗余方式和EC方式共存。
按時間分區設置EC
- 對時間較早的分區(例如:半年前的數據),設置EC策略
- 默認分區還使用副本冗余方式,這樣可以保證多個作業讀取數據時,可以獲取比較好的性能
按數據使用頻率設置EC
- 一些只運行一兩次就不再使用的數倉低層數據,可以使用EC存儲。
- 一些非共享的、ETL系統的數據可以設置EC,獲得更高的EC存儲。
參考文獻:
