Erasure Coding(糾刪碼)深入分析
1.前言
Swift升級到2.0大版本後宣稱開始支援糾刪碼,這其實是一個很有意義的特性,主要是能夠在一定程度上解決3副本空間浪費太多的問題。因為3副本這一點是swift推廣的最大障礙之一,成本的增加嚇退了不少潛在客戶。這次的改進有望消除客戶顧慮,拓展更多使用者
而回到儲存領域來看,資料冗餘機制其實這幾十年來沒有太多進展,RAID,副本一直是當仁不讓的最終選擇。而近幾年,尤其是規模較大的應用場景下,糾刪碼越來越多的出現在選擇的視野範圍,成為RAID,副本之外的第三種選擇,因此也獲得了越來越多的關注。
糾刪碼(Erasure Code)本身是一種編碼容錯技術,最早是在通訊行業解決部分資料在傳輸中損耗的問題,它的基本原理是把傳輸的訊號分段,加入一定的校驗再讓各段間發生一定的聯絡,即使在傳輸過程中丟失掉部分訊號,接收端仍然能通過演算法把完整的資訊計算出來。
如果嚴格的區分,實際上按照誤碼控制的不同功能,可分為檢錯、糾錯和糾刪三種類型。檢錯碼僅具備識別錯碼功能 而無糾正錯碼功能;糾錯碼不僅具備識別錯碼功能,同時具備糾正錯碼功能;糾刪碼則不僅具備識別錯碼和糾正錯碼的功能,而且當錯碼超過糾正範圍時,還可把無法糾錯的資訊刪除。
前面曾提到過適用場景通常是大規模部署,這是有其緣由的。從傳統情況來看,RAID通常用在企業級環境裡較多,在幾臺和十幾臺儲存裝置規模的IT系統中,一向是使用穩定可靠歷經數十年磨礪的RAID技術。而在資料中心級的大規模部署中,RAID不再受歡迎,大部分的分散式系統都偏好副本模式,看重的是其高可靠性和讀效能優化的特點(關於副本的討論我之前寫過一篇:為啥大家都喜歡
2. 糾刪碼理論分析
糾刪碼常見的有三類,Reed-Solomen類,級聯低密度糾刪碼和數字噴泉碼,後面兩種的實現原理細節和優劣這裡就不深入了,這裡只簡單介紹下目前在儲存行業應用的Reed-Solomen類糾刪碼。
從糾刪碼基本的形態來看,它是N個數據+M個校驗的結構,其中資料和校驗的N和M值都能夠按照一定的規則設定。在1~M個數據塊(資料或校驗都行)損壞的情況下,整體資料仍然可以通過計算剩餘資料塊上面的資料得出,整體資料不會丟失,儲存仍然是可用。
下面是糾刪碼的結構示意圖。
從圖中其實可以看出,糾刪碼和大家熟悉的RAID技術看起來是有些類似的,一個條帶(Stripe)是由多個數據塊(strip)構成,分為資料塊和校驗塊。但與RAID5/RAID6不同的是,糾刪碼功能上來看最大的區分特點是校驗和資料的比例按N+M可調整,並且校驗塊數量不再受限於兩個,典型的如12+4,6+3等等。
校驗塊和資料之間是如何建立關係的呢?通訊理論(鄙人專業)告訴我們糾刪碼是屬於分組線性編碼,編碼過程用到的數學理論並不高深,數學關係其實是是矩陣乘法。具體來說是用編碼矩陣和分塊資料做乘法從而得到校驗塊,如下:
在建立完關聯後,由於矩陣運算是可逆,系統就具備了容忍最大M個失效的能力。
(微軟曾在一次演示中用兩數之和等於第三個數來解釋,但僅是為了方便理解,在真正實踐的多校驗情況下,還是使用編碼矩陣的。)
3. 糾刪碼的演化,RS->LRC
糾刪碼通過技術含量較高的演算法,提供和副本近似的可靠性,同時減小了額外所需冗餘裝置的數量,從而提高了儲存裝置的利用率。但糾刪碼所帶來的額外負擔主要是計算量和數倍的網路負載,優缺點都相當明顯。尤其是在出現硬碟故障後,重建資料非常耗CPU,而且計算一個數據塊需要通過網路讀出N倍的資料並傳輸,所以網路負載也有數倍甚至10數倍的增加。
整體來看,若採用糾刪碼技術,你能夠得到了希望的容錯能力和儲存資源利用率,但是需要接受一定的資料重建代價,兩者間做一個平衡。
難道事情就這個樣子了嗎?有沒有優化改善的空間呢?答案是“有”。
如果仔細分析故障出現的情況,你將很容易發現兩個特徵:
特徵一:所有的故障都將導致同樣的重建代價,無論是一個盤,還是M個盤
特徵二:單個磁碟故障的機率遠遠大於多個磁碟同時故障的機率,通常在90%以上
因此,優化的思路自然聚集到更容易出現的單個磁碟故障上來,如何更有效的處理這種概率較大的事件呢,直接出現的對策就是分組,把單個磁碟故障影響範圍縮小到各個組內部,出壞盤故障時,該組內部解決,在恢復過程中讀組內更少的盤,跑更少的網路流量,從而減小對全域性的影響。
LRC(Locally Repairable Codes),我理解為區域性校驗編碼,其核心思想為:將校驗塊(parity block)分為全域性校驗塊(global parity)、區域性校驗塊(local reconstruction parity),故障恢復時分組計算。
以微軟Azure的雲端儲存(Windows Azure Storage)實現為例,它採用LRC(12,2,2)編碼,將12個數據塊為一組編碼,並進一步將這12個數據塊平均分為2個本地組, 每個本地組包括6個數據塊,並分別計算出一個local parity,之後把所有12個數據塊計算出2個global parities。
當發生任何一個數據塊錯誤時,只需用本地組內的資料和校驗塊用於計算,即可恢復出原始資料。而恢復代價(通過網路傳輸的資料塊數量)就由傳統RS(12,4)編碼的12,變為6,恢復過程的網路I/O開銷減半,同時空間冗餘率保持不變,仍為(12+2+2)/12 = 1.33
微軟在介紹中有過一張圖來對RS和LRC進行對比,我覺得描述的非常清楚,這裡借用一下
圖中,藍色是LRC,紅色是RS,可以很清楚的看到,使用LRC的編碼方式後,雖然空間利用率(橫軸)並沒有提高,但是重建代價(縱軸)卻有明顯的改善。考慮到分散式系統裡故障是一個常態,重建代價的降低和區域性化就是非常有價值的一個技術改進了。
另外,有一點要特別注意,LRC並不是100%不丟資料的,4個塊壞掉的情況下,只有86%的機率能找回資料,從可靠性排序來說,RS12+4 》 LRC12+2+2 》RS6+3。
但綜合來說,LRC還是很有競爭力的技術,目前,Microsoft、Google、Facebook、Amazon、淘寶(TFS)都已經在自己的產品中採用了Erasure Code,並且大多都從經典RS轉向LRC。雖然具體實現,但基本原理都是LRC的組內校驗+全域性校驗。
4. 糾刪碼的實際案例
我們可以具體來看看在業界,各家優化版糾刪碼的實現是怎麼樣的。
Google RS(6,3) in GFS II (Colossus),
Google在04年釋出GFS的經典論文後,09年開始開發第二代GFS(Colossus),它採用了最基本的RS(6,3)編碼,將一個待編碼資料單元(Data Unit)分為6個數據塊, 再新增3個parity block,最多可容包括parity blocks在內的任意3個數據塊錯誤。
資料恢復的網路I/O開銷為:恢復任何一個數據塊需要6次I/O,通過網路傳輸6個數據block,儲存的空間冗餘率 為(6+3)/6 = 1.5
由於Google的資訊能查閱到的有限,我沒找到近兩年的情況介紹,但相信Google這樣一家技術型的公司,應當也有類似的演進後的糾刪碼技術,不會止步於5年前的RS標準碼。
Facebook:從RS(10,4)到LRC(10,6,5)
Facebook早期在HDFS RAID中採用的編碼方式RS(10,4)
如上圖所示。將每個待編碼的資料均分為10個數據塊, 後面新增4個校驗的parity校驗塊。這種RS編碼方式的空間冗餘率為(10+4)/10 = 1.4x,發生任何一個數據塊錯誤的恢復代價為10,即發生任意一個塊錯誤需要10次I/O操作,從網路傳輸的資料量為10個數據塊。
其LRC編碼方法如下:
除了在原先的10個數據塊之後新增4個校驗塊外,還將10個數據塊均分為2組,每組單獨計算出一個區域性校驗塊(Parity),將資料恢復代價由原來的10降低為5.即恢復任何一個數據塊錯誤只需要進行5次網路I/O,從網路傳輸5個數據塊。此種編碼方式的空間冗餘率 為(10+4+2)/10 = 1.6。
4.