Ceph學習記錄7-糾刪碼
參考:Ceph原始碼分析 常濤
1. EC的基本原理
雲端儲存領域比較流行的資料冗餘儲存方法,原理和傳統的RAID類似,但是比RAID靈活。
它將寫入的資料分成N份原始資料,通過這N份原始資料計算出M份校驗資料。把N+M份資料分別儲存在不同的裝置或者節點中,並通過N+M份中的任意N份資料塊還原出所有的資料塊。
EC包含了編碼和解碼的兩個過程:
編碼:將原始的N份資料計算出M校驗資料。
解碼:通過這N+M份資料中的任意N份資料來還原出原始資料的過程稱為解碼。
EC可以容忍M份資料失效,任意小於等於M份的資料失效能通過剩下的資料還原出原始資料。
目前,一些主流的雲端儲存商都採用EC編碼方式:
Google GFS II---RS(6,3)編碼
Facebook HDFS RAID---RS(10,4)編碼
Mircosoft Azure---LRC(12,2,2)編碼
2. EC的不同外掛
ceph支援以外掛形式來指定不同的EC編碼方式。各種編碼的不通電,實質上就是在ErasureCode的三個指標之間折中的結果,這三個指標是:空間利用率、資料可靠性和恢復效率。
2.1 RS編碼
目前最廣泛的糾刪碼是ReedSolomon編碼,簡稱RS碼。
RS編碼實現之一:Jerasure,是一個ErasureCode開源實現庫,實現了EC的RS編碼,目前ceph中預設的就是Jerasure方式。
RS編碼實現之二:ISA,是Intel提供的一個EC庫,只能執行在Intel CPU上,它利用了Intel處理器本地指令來加速EC的計算。
RS編碼不足之處:在N+K個數據塊中有任意一塊資料失效,都需要讀取N塊資料來恢復丟失的資料。在資料恢復的過程中引起的網路開銷比較大。因此,LRC編碼和SHEC編碼分別從不同角度做了相關優化。
2.2 LRC
LRC編碼的核心思想為:將校驗塊(parity block)分為全域性校驗塊(global parity)和區域性校驗塊(local reconstruction parity),從而減少恢復資料的網路開銷。其目標在於解決當單個磁碟失效後恢復過程的網路開銷。
LRC(M,G,L)的三個引數分別為:
M:原始資料塊的數量
G:全域性校驗塊的數量
L:為區域性校驗塊的數量
編碼過程為:把資料分為M個同等大小的資料塊,通過該M個數據塊計算出G份全域性校驗資料塊。然後把M個數據塊平均分為L組,每組計算出一個本地資料校驗塊,這樣共有L個區域性資料校驗塊。
下面以Azure的LRC(12,2,2)和Facebook的HDFS RAID的早起編碼方式RS(10,4)為例來比較LRC和RC編碼在恢復過程的開銷:
LRC(12,2,2) | RS(12+4) |
D1 D2 D3 D4 D5 D6 D7 D8 D9 D10 D11 D12 L1 L2 G1 G2 |
D1~D12 P1~P4 |
表中LRC編碼:總共有12 個數據塊,分別為D1~D12.有兩個本地資料校驗塊L1和L2,L1為通過第一組資料塊D1~D6計算而得到的本地校驗資料塊;L2為第二組資料塊D7~D12計算而得到的本地校驗資料塊。有兩個全域性資料校驗塊G1和G2,它是通過所有資料塊D1~D12計算而來。對應RS編碼,資料塊D1~D12,計算出的校驗塊為P1~P4.
不同情況下的資料恢復開銷:
***如果資料塊D1~D12只有一個數據塊損壞,LRC只需要讀取6個額外的資料塊來恢復。而RS需要讀取12個其他的資料塊來修復。
***如果L1或者L2其中一個數據塊損壞,LRC需要讀取6個數據塊。如果G1,G2其中一個數據損壞,LRC扔需要讀取12個數據塊來修復。
最大允許失效的資料塊:
***RS允許資料塊和校驗塊中任意的小於等於4個數據的失效。
***LRC:
資料塊中,只允許任意的小於等於2個數據塊失效。
允許所有的校驗塊(G1,G2,L1,L2)同時失效。
允許至多兩個資料塊和兩個本地校驗塊同時失效。
綜上分析:對於只有一個數據塊失效,或者一個本地資料校驗塊失效的情況下,再恢復該資料塊時,LRC比RS可以減少一般的磁碟IO和網路頻寬。所以LRC重點在單個磁碟失效後恢復的優化。但是對於資料可靠性來說,通過最大允許失效的資料塊個數的討論可知,LRC會有一定的損失。
2.3 SHEC編碼
SHEC的編碼方式為SHEC(K,M,L),其中K代表data chunk的數量,M代表parity chunk的數量,L代表計算parity chunk時所需要的data chunk數量。其最大允許失敗的資料塊為ML/K. 這樣恢復失效的單個數據塊只需要額外讀取L個數據塊。
以SHEC(10,6,5)為例,其最大允許失效的資料塊為:
M(6) * L(5) / K(10) = 3
D1~D10為資料塊
P1:D1~D5計算出的校驗塊
P2:D3~D7計算出的校驗塊
P3:D5~D9
P4:D6~D10
P5:D1~D2 D8~D10
P6:D1~D4 D10
2.4 EC和副本的比較
三副本 | RS(10,4) | LRC(10,6,5) | SHEC(10,6,5) | |
資料容量開銷 | 3X | 1.4X | 1.8X | 1.6X |
資料恢復開銷 (單個數據塊失效) |
1X | 10X | 5X | 5X |
可靠性 | 高 | 中 | 中 | 中下 |
說明:
***在三副本的情況下,恢復效率和可靠性都比較高,缺點就是資料容量開銷比較大
***對於EC的RS編碼,和三副本比較,資料開銷顯著降低,以恢復效率和可靠性為代價。
***LRC編碼以資料容量開銷率高的代價,換取了資料恢復開銷的顯著降低。
***SHEC編碼用可靠性換代價,在LRC的基礎上進一步降低了容量開銷。
3. Ceph中EC的實現方式
3.1 基本概念
首先介紹一些EC的基本概念。注意,這裡提到的stripe是RADOS系統定義的,可能與其他系統的定義不同。
***chunk:一個數據塊就叫data chunk,簡稱chunk,其大小為chunk_size設定的位元組數。
***stripe:用來計算同一個校驗塊的一組資料塊,稱為data_stripe,簡稱stripe,其大小為stripe_width,參與的資料塊的數目為stripe_size.
stripe_width = chunk_size * stripe_size
如圖:一個EC(4+2):stripe_size = 4, chunk_size = 1K, 那麼stripe_width = 4K.
在ceph中,預設stripe_width就是4K.
3.2 EC支援的寫操作
當前ceph的EC寫入還有一定的限制,目前支援的操作如下:
***create object:建立物件
***remove object:刪除物件
***write full:寫整個物件
***append write(stripe width aligned):追加寫入(限定追加操作的起始偏移以stripe_width對齊)
目前ceph只支援上述操作,而不支援overwrite操作,其主要有如下兩個條件的限制:
***由於編碼和解碼的過程都以stripe width整塊資料計算
***EC在特殊場景需要回滾場景
所以,目前EC只支援append寫操作中,寫操作的起始偏移offset以stripe_width對齊的情況,如果ennd不是以stripe_width對齊,就補0對齊即可。
目前不支援以下情況:
***append寫操作,寫操作的起始偏移offset沒有以stripe_width對齊。
***overwrite寫操作,offset和end都不以stripe_width對齊。
(由於計算資料校驗塊需要讀取整個stripe的資料塊,所以前兩種情況都需要讀取該stripe確實的資料塊,來計算校驗塊,由於效能原因,目前不支援)
***overwrite寫操作,寫操作的起始偏移offset和結束為止end都以stripe_width對齊。
(overwrite寫操作不支援是由EC的回滾機制導致的)
3.3 EC的回滾機制
依據EC原理可知,EC(N+M)的寫操作如果小於等於M個OSD失效,不會導致資料丟失沒資料可恢復。EC在理論上就最多隻能容忍M個OSD失效。如果OSD失效的數量大於M,這種情況就超出了理論設計的範疇,系統無法處理這種情況。可以說這是合理的。
但是對於所有的儲存系統,必須應對一種特殊情況:整個機房或者整個資料中心全部斷電,系統重啟後可以恢復,並且資料不丟失。
當儲存烯烴全域性斷電時,其資料的寫入狀態就有可能出現:小於N個磁碟的資料成功寫入,而其他磁碟沒有寫成功的情況。
以之前EC(4+2)為例,假設寫操作只有三個OSD寫成功了,其他的3個OSD沒有來得及把資料寫入磁碟。這種情況下,不但導致新資料寫入失敗,而且導致舊資料也無法讀取成功。這就需要EC的回滾機制,回滾到最後一次成功寫入的舊資料版本。
Ceph目前支援的EC操作都是回滾比較容易實現的,實現機制如下:
***create object操作的回滾實現比較簡單,刪除該物件即可
***對於remove object操作,在執行時並不刪除該物件,而是暫時保留該物件;如果需要回滾,就可以直接恢復。
***writeFull操作,暫時保留舊的物件,建立一個新的物件完成寫操作。當需要回滾時,恢復舊的資料物件。
***append操作,記錄append時的size到PG日誌中,當需要回滾時,對該物件做truncate操作即可。
4. EC原始碼分析
對應EC的上述三種變更操作,其本地回滾的資訊都記錄在對應的PG日誌記錄的mod_desc中:
struct pg_log_entry_t{
.......
objectModDesc mod_desc;
......
};
在函式ReplicatedPG::do_osd_ops中實現操作的事務封裝,下面重點分析EC的寫操作和write_full操作的實現。
4.1 EC的寫操作
首先驗證如果是EC型別,寫操作的offset必須以stripe_width對齊,否則不支援。
case CEPH_OSD_OP_WRITE:
if(pool.info.requires_aligned_append() &&
(op.extent.offset % pool.info.required_alignment() != 0)) {
result = -EOPNOTSUPP;
break;
}
如果物件不存在,就在mod_desc中新增建立的資訊,否則在mod_desc中新增old size的資訊:
ctx->mod_desc.create();
否則就追加寫:
ctx->mod_desc.append(oi.size);
最後把寫操作新增到事務中:
if(pool.info.require_rollback())
t->append(soid, op.extent.offset, op.extent.length, osd_op.indata, op_flags);
4.2 EC的write_full
如果物件已經存在,呼叫函式ctx->mod_desc.rmobject,如果返回false,說明已經記錄了資訊,直接刪除,如果返回true,就呼叫stash儲存舊的物件資料,用來恢復:
case CEPH_OSD_OP_WRITEFULL:
......
if(obs.exists){
if(ctx->mod.desc.rmobject(ctx->at_version.version)) {
t-stash(soid, ctx->at_version.version);
}else{
t->remove(soid);
}
}
在事務中寫入資料:
t->append(soid, 0, op.entent.length, osd_op.indata, op.flags);
4.3 ECBackend
類ECBackend實現了EC的讀寫操作。ECUtil裡定義了編碼和解碼的函式實現。ECTransaction定義了EC的事務。
目前糾刪碼的研究是一個熱點,它可以極大的提供儲存利用率,降低儲存成本。目前研究都在著力研究糾刪碼如何直接支援塊儲存,也就是隨機overwrite操作的能力。