轉載自:https://blog.csdn.net/u011026968/article/details/52295666
近幾個月主要參與一個分布式存儲系統的糾刪碼部分(用於數據容錯),糾刪碼在學術界出現比較早,現在ceph,微軟的存儲系統,Hadoop 3.0等都用了EC。文章會分為多篇,主要將Erasure Code,LRC, 以及相關的數學基礎,作為學習總結。
一、糾刪碼簡介
分布式系統需要在硬件失效等故障發生后仍然能繼續提供服務。就數據而言,HDFS采用每份數據3副本的方式,保證某些數據損失之后仍能繼續使用。
數據的容錯除了副本還有另一種做法,就是把丟失的數據計算出來。這就是糾刪碼的思想了。(PS: Spark的數據也可以通過計算恢復,詳見spark論文)。
與副本相比,糾刪碼的優點在於節省存儲空間(見下文解釋),缺點在於有計算開銷而且修復需要一定時間,而副本損失只要復制出來損失的數據,未損失的數據可以繼續提供服務。
二、Erasure Codes(EC)原理
1、朴素的解釋
有下列6個方程組成的方程組
(1)x1 = 1
(2)x2 = 2
(3)x3 = 3
(4)x1 + x2 + x3= 6
(5)x1 + 2*x2 +4*x3 = 17
(6)x1 + 3*x2 +9*x3 = 34
要知道x1,x2,x3三個數的值,只需要上面任意三個方程即可解出來。假設有上面4個方程,有趣的地方出現了,如果丟了一個方程,那么仍然可以用其他三個方程求出x1,x2, x3的值。相當於只多了一個方程就能解決x1,x2,x3任何一個數的值丟失的問題。
把上面的方程(1)(2)(3)看做是分布式系統的數據,(4)(5)(6)看做是code,那么只要一個code,即使丟了(1)(2)(3)中的任何一個數據都是可以恢復的, 達到這樣的效果只需要存儲4個方程。 如果采取副本策略,要達到(1)(2)(3)丟失任何一個數據都能恢復的話,只要把(1)(2)(3)三個方程都存儲兩份,也就是存儲了6個方程。於是糾刪碼比副本策略在存儲效率上的優勢就體現出來,4/6的比值,節省1/3的空間。實際根據code的多少,存儲效率會不一樣。
2、存儲系統中的符號約定
k:數據塊的個數
m:校驗塊的個數(就是code)
n:k+m,也就是數據塊和校驗塊的個數總和。
編碼效率:r = k/m
上面的解釋是參照Jerasure庫的代碼解釋的,IntelEC庫符號表示不同,但是意義一樣,不再贅述。
3、現有的EC庫
(1)Jerasure庫
http://jerasure.org/
(2)Intel EC庫
http://www.intel.com/content/www/us/en/storage/erasure-code-isa-l-solution-video.html
實際實驗發現,(1)線程不安全,(2)線程安全(本人簡單看過一部分代碼+1000線程並發測試)
尚未面世的Hadoop 3.0據說要使用EC編碼。查資料發現用的應該也是英特爾庫。本人近日工作是基於英特爾的EC庫封裝LRC庫, 也就是線程安全的LRC(見后文)。
三、模擬EC編碼、解碼(恢復)的例子
假設4個data塊,2個Code塊。
1、編碼
通用的編碼矩陣:
4個data塊,2個Code塊情況下,編碼過程如下:
(a)
Code塊是:
C0=D0+D1+D2+D3
C1=D0+2*D1+4*D2+8*D3
圖1 EC編碼過程
編碼矩陣如上圖,Di表示數據塊,Ci表示校驗塊。編碼矩陣(encodematrix)組成有兩部分,上面是k*k的單位矩陣,下面是m*k的編碼矩陣,如圖是范德蒙矩陣,Jerasure庫用的是范德蒙矩陣,Intel EC提供了范德蒙矩陣和柯西矩陣的實現,奇怪的是Intel EC說范德蒙不一定可逆,柯西一定可逆,所以本人在用Intel EC的時候一直用柯西矩陣。(為什么需要可逆見下文:解碼)。
2、解碼
解碼粗淺理解就是未損失的數據塊和校驗塊乘以編碼矩陣的逆矩陣可以得到原來的數據。大部分博客感覺也就是能讓人有這種粗淺的感覺,所以本文寫得更詳細一點。
以4個data塊,2個Code塊的情況的解碼來解釋,當code的塊數為2時,最多壞掉兩塊數據塊(按照解方程就是四元一次方程,至少4個才能解出來四個元的值)。此處假設一個數據塊D1和一個code塊C0丟失
解碼過程分為兩步:
(1)根據已有的數據求解出所有的Data塊, D0 ~ D3
更具體而言,順序遍歷編碼矩陣的前n行,順序選取沒有損壞的前k行(意思是該行對應的數據塊或者校驗塊沒有損壞)。生成k*k的矩陣 M。本例中M矩陣如下:
編碼encode的時候這幾行發生了下面的事情:
(b)
所以解碼的時候,有D0 D1 D2 C1 以及M,很顯然可以通過求M的逆矩陣來求出D0D1 D2D3 :
(c)
(2)求出損失的數據
(1)中已經求出來了所有的數據塊的內容,而且編碼矩陣是知道的,因此可以求出所有的數據,對於本例子,其實是在(c)式子兩邊同時乘以一個矩陣來求出C0,矩陣很簡單,就是相應的編碼矩陣的部分:
於是就求出來了丟失的數據D1和Code C0
結合Intel EC的源碼簡單再講下decode生成解碼矩陣decodematrix的過程:
(1)順序遍歷編碼矩陣的前n行,順序選取沒有損壞的前k行(意思是該行對應的數據塊或者校驗塊沒有損壞)。生成k*k的矩陣 M。 (2)求M的逆矩陣 M_inv (后續文章講怎么求) (3)求解碼矩陣decode matrix,解碼矩陣構成: a) 損失的數據塊:損失的數據所在的行對應的M_inv的行復制到decode matric b)直接上代碼…
|
Decode matrix的構成的代碼:
nsrcerrs就是數據塊的損失的個數;
nerrs是總的(數據塊加上校驗塊)損失的個數;
invert_matrix就是上面說的M_inv ;
src_err_list是失效的數據塊對應的行的下標(idx);
Gf_mul以及下面的異或符號,簡單說下就是EC的矩陣運算都是在有限域進行的。 直接把異或理解成加法, 把gf_mul理解成乘法,然后下面的循環看成矩陣運算很容易明白了…
for (i = 0; i < nsrcerrs; i++) { for (j = 0; j < k; j++) { decode_matrix[k * i + j] = invert_matrix[k * src_err_list[i] + j]; } } /* src_err_list from encode_matrix * invert of b for parity decoding */ for (p = nsrcerrs; p < nerrs; p++) { for (i = 0; i < k; i++) { s = 0; for (j = 0; j < k; j++) s ^= gf_mul(invert_matrix[j * k + i], encode_matrix[k * src_err_list[p] + j]);
decode_matrix[k * p + i] = s; } } |
3、編碼矩陣需要滿足的性質
從上面的過程可以看出,編碼矩陣必須可逆,否則無法解碼,也就無法恢復數據。
k*k的范德蒙矩陣可逆的簡單證明(保研狗,就大一學過線代,如果出錯的話求指教):
(1) 范德蒙的性質之一是有求行列式的式子:
式子來源: (https://zh.wikipedia.org/wiki/%E8%8C%83%E5%BE%B7%E8%92%99%E7%9F%A9%E9%99%A3)
(2) 對於我們用的范德蒙矩陣,ai=i (i = 1,2,3…)
所以任意兩行ai和aj一定不一樣,也就是det(V) 不為0 (3)行列式的值不為0等價於矩陣可逆。 證畢。(略羞恥,因為(1)直接用的人家的結論。也可以從滿秩的角度很容易證明)
|
所以當數據損失之后,選取k*k的矩陣,一定可逆,也就可以繼續解碼。
四、編碼類型
1、編碼方式
(1)橫式編碼(horizontalcodes)
這種編碼方式下,code數據塊單獨uoweie數據塊,而不是和data塊放在一起。例如EVENODD編碼,RDP編碼都是橫式編碼。
(2)縱式編碼(verticalcodes)
code存儲在數據所在的磁盤,某些塊既有數據又有code。如X-code編碼。
2、RS編碼
RS編碼是唯一可以滿足任意的數據磁盤數目(n)和冗余磁盤數目(m)的MDS(maximum distance separable)的編碼方法。解碼重構的原理推到中,有一個重要的條件,就是未出錯的信息所對應的殘余生成矩陣在GF(2w)上滿足可逆。
(1) 范德蒙RS編碼
范德蒙矩陣滿足上述的“可逆”的條件。
(2) 柯西RS編碼
柯西矩陣滿足上述的“可逆”的條件。
與范德蒙RS編碼區別就在於用柯西矩陣代替范德蒙行列式,並且有位運算的方法可以對柯西RS編碼中的乘法進行改進,轉化為二進制乘法,整個RS編碼的運算可以轉化為只包含異或的簡單運算。(此部分待補充)
五、分布式系統的工程實現
1、簡單實現方案
(1)編碼
在創建數據塊以及數據塊遠遠未寫滿的情況下,使用副本策略做數據容錯。當若干數據塊(比如k個數據塊)都基本寫滿,則禁止對這些數據塊做寫(包括修改、刪除)等操作。此時進行編碼,當編碼成功時,刪除冗余的數據塊副本。此時就從副本策略變成糾刪碼策略。
(2)解碼
a)一個線程定期掃描數據,比如對數據塊和校驗塊做crc校驗,如果發現有數據塊或者校驗塊失效,則啟動恢復線程。
b)恢復線程先根據EC組現有的數據情況,從遠程或本地獲取必要的數據進行解碼,恢復失效數據。
2、EC的一些優化策略
本人參與的項目遇到了這種情況。最初在解碼前未損失的數據都拉到本地,然后恢復數據。后來發現這樣是多余的。於是只順序拉去必要的數據塊。實際如果肯修改相應的EC庫的代碼,還可以有其他選擇策略,比如選擇同一機房,同一機器不同磁盤的數據用於恢復(這個時候構造的解碼矩陣也有一定變化,需要做相應修改,詳見本文的解碼矩陣的生成過程)。
參考及相關資料:
Hadoop EC的一個實現:
https://sourceforge.net/projects/hadoop-ec/
http://blog.cloudera.com/blog/2016/02/progress-report-bringing-erasure-coding-to-apache-hadoop/