1. 程式人生 > >MICS:副本和糾刪碼混合儲存系統

MICS:副本和糾刪碼混合儲存系統

#摘要
雲端儲存系統的三個指標: 高可靠性,低儲存開銷,高讀寫效能。這三個指標是沒有辦法同時滿足的,很多時候需要進行tradeoff。 副本系統和糾刪碼是兩種在儲存系統中廣泛使用的策略,它們在保證高可靠性的前提下,選擇了不同極端的tradeoff。 副本儲存開銷大,但效能較好;糾刪碼儲存開銷低,但效能較差。本文提出了MICS系統,它將一個物件以兩種形式儲存,一種採用副本,一種採用分片糾刪碼,不僅如此,還設計了針對這種hyprid結構的精細的讀寫協議。 在服務使用者的角度,MICS通過PRAM一致性協議保證高可用性。 實驗方面,在I/O吞吐率上有所提高,同時在多種真實負載情況下,依然表現出優越的效能,方差非常小。

論文名字:MICS : Mingling Chained Storage Combining Replication and Erasure Coding

疑問:
為什麼副本不適合寫密集型應用,而糾刪碼適合?
感覺副本和糾刪碼在寫密集型應用上的效能表現應該是一致的。如果採用三副本以及RS(4,2)編碼,那麼更新(修改)資料的開銷,都需要更新三個塊(副本是三個相同的塊,糾刪碼是1個數據塊加上兩個校驗塊)。 我認為這個寫應該是追加寫,如果是追加4個數據塊的話,對於副本來說系統需要建立12個數據塊才可以滿足三副本的要求,而對於RS(4,2)編碼,系統只需要建立6個數據塊即可,因此更快。

#簡介
MICS是物件儲存系統,對於每一個數據物件使用兩部分進行儲存,Master Node儲存這個物件的完整副本,而糾刪碼節點(ECC)儲存物件的編碼分片。 對於一個數據物件,採用RS(k

k,mm)編碼,那麼會生成k+mk+m個分配,kk個數據片,mm的校驗片。 這所有的資料會被分散儲存到1+k+m1+k+m個儲存節點上,因此稱這種編碼為MICS(1,k,m)編碼。

MICS實現了兩種實際可行的機制:讀寫協議和一致性模型。
讀寫協議:
順序讀,順序寫,隨機寫
Master Node(MN)節點承擔了絕大部分的讀請求,ECC保證了在MN節點掛掉後依然可以對外服務讀;在訪問隨機寫方面,ECC通過Parity Logging機制以較低的網路和延遲實現了隨機寫的過程。
一致性模型:
本文采用了PARM一致性模型來提供有效的讀寫效能。

##嚴格一致性
讀出的資料始終為最近寫入的資料,也就是說這次寫入,後面的所有人的都可以正常讀取,需要全域性鎖,在分散式網路環境不可能實現。

##Sequencial一致性
所有使用者以同樣的順序看到對同一資料的操作,但是該順序不一定是實時的。也就是說,如果有一組對元素x的更新操作,它不保證每次更新都會立刻被隨後的讀取到上次的更新,但是保證先讀到更新一定在前,後讀到的更新一定在後。

##因果一致性
對有因果關係,需要保證順序,無因果關係的可以並行執行,無需關注它們的先後順序。

##Pipe random-access memory一致性
不同使用者的寫操作在不同的使用者上來看是沒有嚴格的順序的,但同一個使用者的寫操作在不同的使用者看來是有嚴格的順序的。 說起來非常拗口,簡單舉個例子: 使用者A進行了A.1, A.2操作,使用者B進行了B.1, B.2操作,那麼在使用者C他可能看到的操作有 A.1 A.2 B.1 B.2, A.1 B.1 B.2 A.2, B.1 B.2 A.1 A.2等等, 也就是說他看到的A的操作必須有A.1在A.2之前,B的操作有B.1在B.2之前,但是對於A和B的操作,他並沒有得到嚴格的順序。

借用wiki的一個例子:
P1:W(x)1
P2: R(x)1W(x)2
P3: R(x)1R(x)2
P4: R(x)2R(x)1
Time ---->
上面的這個是符合PRAM一致性的,這是因為對於P3和P4來說,它們只能看到P1的操作有順序性,P2的操作有順序性,但是並不能看到P1和P2的操作具有順序性。
但是要注意的是,上面的例子不符合強一致性,順序一致性(要求先讀到的一定是先操作的,雖然不是實時的,P4程序不符合要求),因果一致性(x的兩個寫入操作有因果關係,需要保證順序性)

##eventual一致性
在沒有新的更新的情況下,先前的更新會最終同步到所有的節點,可以保證資料最終是一致的,而在更新的過程中,使用者有可能讀到陳舊的資料。

#架構設計
系統架構如下圖
這裡寫圖片描述

本系統主要包含三部分,外部客戶端,代理伺服器PSC,以及底層物件儲存池OSC。
客戶端
前端webserver,三種操作upload, download, update,也就是說順序寫,讀,隨機寫。每一個請求的頭包含如下欄位:
client_id, req_type, node_id, obj_id, req_ts
PSC
幾個高效能機器,維持全域性一致性。PSC負責將使用者的請求導向相應的儲存節點。
OSC
包含10-1000臺的儲存機器叢集,每一臺節點負責管理它上面的物件的metadata。
obj_id, obj_meta

#寫協議
順序寫
首先到達Master Node,然後這個object被成功儲存之後,MN節點對這個物件進行分片,並將資料和校驗分片散步到ECC中多個節點上,每個ECC節點在儲存完畢之後向MN返回ACK,MN在收到所有ECC分片的ACK之後迴應客戶端。同時每一個節點都在mem_table中插入以下條目:
obj_id, data_type, version_history
資料型別記錄了分片的位置資訊,版本歷史是一個連結串列記錄了此物件的歷史更新,它包含一堆entry,每一個entry就是一個version,包括client_id, req_ts, offset(初始上傳之後,此處offset為0),delta_block,tag(標記資料是clean還是dirty)。

也就是說在物件第一個建立之後,對於這一個物件會生成一個這樣的條目。它的key就是物件的id,value是資料的型別以及版本歷史。剛建立之後會插入第一個版本,即offset=0的版本。
隨機寫:
採用parity-logging演算法更新。
1,當資料更新的請求到達之後,它首先被PSC導向到更新位置對應的ECC的資料節點上(注意此處並不會先到達MN節點),當前資料節點立刻用新的資料覆蓋,並計算出一個delta_block,同時會構建一個新的version,此時version的tag為dirty資料。
2,新的version和delta_block會被髮送給其他的校驗ECC節點,而單獨的version會被髮送給MN節點。 所有的節點會將這個version插入到對應的obj_id的version_history中。
3,校驗ECC節點在收到delta_block後,會將其加入到version對應delta_block條目中(當然其實就是一個指標而已),delta_block只會放入到一個buffer中,然後將指標賦值給version的條目即可。
4,被更新的資料節點在收到所有校驗節點的ACK之後,就會迴應客戶端更新成功。然後通知MN發起reconstruct資料。
5,MN在收到重構請求後,開始從所有的ECC資料節點並行讀取分片,並構建物件,儲存物件,然後將第2步中收到的version置位為clean。
6,最後MN也通知其他的ECC節點將version的tag改為clean,校驗節點將先前儲存的delta_block刪除即可。

具體如下圖:
這裡寫圖片描述

#讀協議
正常讀
RnorR_{nor},MN節點工作正常,具有相應的資料,可以直接讀取即可。
輕量failure讀
RlwfR_{lwf}, MN節點沒有相應的資料,而所有的資料節點可以正常回應。所有的ECC資料節點將內容assemble在一起傳遞給客戶端即可。 同時,也會像Mn傳送重建請求並由MN決定是否發起物件重建。 如果MN只是由於暫時的延遲導致沒有迴應,那麼MN就不會發送重建;如果資料確實故障了,需要發起重建請求,進行重建操作。
嚴重failure讀
RhfR_{hf}, 當MN以及至少一個data_node不能正常回應後,需要啟動資料重建解碼過程。 當解碼完畢後,資料返回給客戶端。 同時重建請求也會發送給MN以及failed data_nodes來讓它們決定是否發起資料重建操作。

#PRAM一致性模型
如果read請求的req_ts是強制的,那麼返回的物件版本一定不能比req_ts舊。

  1. 對於R_nor這樣的強制請求,返回物件的最新版本。
  2. 對於R_nor這樣的非強制請求,從版本歷史中尋找一個合適的版本返回:
    1. 如果最新版本是clean的,那麼直接返回。
    2. 如果最新版本是dirty的,MICS向前掃描version_history直到以下某一個條件滿足:(a)obj_id是請求的物件id, (b)當前的版本是clean的,©找到開頭。 MICS然後返回給客戶端,它要不從磁碟中讀取,要不使用delta_block去重構它。
  3. 對於R_lwf和R_hf, MICS重建最新的版本並返回給客戶端,parity_nodes將所有的delta_blocks進行合併到校驗塊中,並刪除除了最後一個的delta_blocks.

再加入一個一致性的圖
這裡寫圖片描述

實現不再描述了。

關於一致性的問題:
upload建立資料物件,直到資料物件完全儲存好,才返回給客戶端,可以保證一致性。
update更新物件在所有的校驗節點收到delta_block就會立刻返回客戶端更新成功。 它如何保持的PRAM一致性的? 當單個客戶端發起連續的兩個請求,只有在第一個請求的所有delta_block完全寫入到對應的校驗節點上,才會返回給客戶端,這樣第二個請求才可以繼續發起。 通過這種方式,兩個delta_block肯定具有著先後順序的,當其他使用者發起讀請求後,一定只會先讀到第一個請求的更新,再讀到第二個更新,也就是說不同的客戶的看到單獨的某一個客戶端的請求具有順序性。 當有多個客戶端同時發起請求後,例如A和B,它們同時發起update(x)的請求,這兩個請求來源於不同的客戶端,由於網路傳輸的不同,有些parity節點會先收到A的更新,有些parity節點會先收到B的更新,這樣在pairty只收到一個節點更新的情況下,有外部的其他的客戶端來讀,它們就有可能命中到先收到A的parity節點從而讀取到A,也有可能從其他parity節點讀取到B,也就是說不同的客戶端看到不同的客戶端的請求不具有順序性,但是所有的parity節點最終都會收到A和B的請求,雖然它們到來的順序不同,最後這些delta_block會在對應的parity節點上按照req_ts進行排序,從而保證最終一致性

關於更新的磁碟開銷。
4副本和MICS(1,4,2)對比: 44 / (4+1+2) = 2.33x
4副本和RS(6,3)對比: 4
6 / (1+3) = 6x