[分散式系統學習]閱讀筆記 Distributed systems for fun and profit 之四 Replication 拷貝
閱讀http://book.mixu.net/distsys/replication.html的筆記,是本系列的第四章
拷貝其實是一組通訊問題,為一些子問題,例如選舉,失靈檢測,一致性和原子廣播提供了上下文。
同步拷貝
可以看到三個不同階段,首先client傳送請求。然後同步拷貝,同步意味著這時候client還在等待著請求返回。最後,伺服器返回。
這就是N-of-N write,只有等所有N個節點成功寫,才返回寫成功給client。系統不容忍任何伺服器下線。從效能上說,最慢的伺服器決定了寫的速度。
非同步拷貝
相對的是非同步拷貝。
master節點立即返回。該節點可能在本地做了複製,但是不會向其他伺服器傳送拷貝。只有在返回以後,非同步的任務在開始執行。
相對地,這是1-of-N write。效能上說,快。但是不能提供強一致性保證。
主流拷貝的途徑
作者用下面的方式分類:
- 可防止divergence(單拷貝系統)
- 存在divergence(多master系統)
第一類途徑使系統表現得像“single system”,即使系統部分失效。
單拷貝系統按照一次執行中傳遞的訊息數目,可以做一下分類:
- 1n messages(非同步 主從備份)
- 2n messages(同步 主從備份)
- 4n messages(2-phase commit,Multi-Paxos)
- 6n messages (3-phase commit,Paxos with repeated leader election)
下面的圖告訴我們,在不同側重中選擇(折中),我們會得到什麼樣的結果。
等待,效能就變差,一致性上的保證就更強。
主從備份拷貝(Primary/Backup replication)
最常見最基本的拷貝方式。所有的update發生在primary,並且以log的形式拷貝到backup 伺服器。主從備份也分同步和非同步兩種。
MySQL和MongoDB都使用非同步主從備份。同步主從備份保證在返回給client之前,backup節點成功儲存了拷貝(Replica)。不過即使這樣,同步方式也僅能保證較弱的承諾。考慮下面的場景:
- 主伺服器收到write,傳送到backup
- backup 寫成功。返回ACK
- 主伺服器fail,client超時,認為寫失敗。
client現在認為寫失敗,但是backup其實成功。如果backup promote成為primary,那麼就不對了。
2階段提交(2PC)
2PC在很多經典的關係型資料庫中都使用到了。例如MySQL 叢集使用2PC提供同步拷貝。下面是2PC的基本流程
[ Coordinator ] -> OK to commit? [ Peers ] <- Yes / No [ Coordinator ] -> Commit / Rollback [ Peers ] <- ACK
在第一階段,投票,協調者(Coordinator)給所有參與者傳送update。每個參與者投票決定是否commit。如果選擇commit,結果首先存放在臨時區域(the write-ahead log)。除非第二階段完成,這部分都只算“臨時”的update。
在第二階段,決策,協調者決定結果,並通知參與者。如果所有參與者都選擇commit,那結果從臨時區域移除,而成為最終結果。
2PC在最後commit之前,有一個臨時區域,就能夠在節點失效的時候,從而允許回滾。
之前討論過,2PC屬於CA,所以它沒有考慮網路分割,對網路分割並沒有容錯。同時由於是N-of-N write,所以效能上會有一些折扣。
對網路分割容錯的一致性演算法
最著名的是Paxos演算法,不過比較難理解和實現。所以Raft可以講講。
什麼是網路分割(Network Partition)
節點本身正常執行,但是網路鏈路發生問題。不同的Partition甚至還能接收client的請求。
2節點,節點失效 vs 網路分割
3節點,節點失效 vs網路分割
單拷貝一致系統必須找到方法去破壞對稱性。否則如果網路分裂成兩個獨立對等系統,divergence發生,無法保證single-copy。所以必須保證只有一個Partition處於活動狀態。
多數的決策
所以,對分割容錯的一致性演算法依賴多數投票。依賴多數,而不是所有(例如2PC)節點,即可容許少數因為網路分割出現的較慢的,無法到達的節點。只要 (N/2 + 1)of N中的節點正常,系統就可以正常執行。
在對網路分割容錯的演算法中,系統一般使用奇數數目的節點,例如3個節點的系統,允許1個節點失效。5個允許2個失效。這樣發生網路分割,我們總能找到一個“大多數”的Partition。
角色
在系統中,節點要麼都扮演同一角色,要麼各自擔任不同角色。
一致性演算法通常使節點扮演不同角色。有一個固定的領導或主節點,可以讓演算法更加高效。Paxos和Raft都利用了不同角色。在Paxos中,存在Proposer。領導者負責在操作中協調,餘下的節點作為followers(Paxos中的Acceptors 或者 Voters)
Epochs(不翻了。。。)
在Paxos和Raft中,每次操作的週期都叫一個Epoch(”term“ in Raft)。在每個epoch,都有一個節點被指定為領導(Leader,後面都用英文吧,免得有歧義)。這個有點類似中國的朝代。
在每一次成功的選舉後,一個epoch始終使用一個固定的leader,如上圖。一些選舉可能失敗,那麼epoch裡面迭代到下一個。
真有點類似前面提到的邏輯時鐘,讓其他節點可以辨識凹凸的節點。那些分割的節點的epoch計數會比較小。
通過決鬥(Duels),改變Leader
在一開始,某節點變成leader,其他節點成為follower。在操作過程中,leader維護心跳訊息,這樣follower就知道leader是否失靈,或者有網路分割發生。
當某節點發現leader沒有響應了,(或者在初始狀態,沒有leader),該節點就轉換成中間模式(在Raft稱為candidate),然後將其epoch計數加1,發起leader的選舉(election),競爭成為leader。
要成為leader,節點必須接收到足夠多的votes,一種獲取vote的方式就是誰先到誰取得。這樣,最終會選出一個leader。
在epoch內,數字標記提案(Proposal)
在每個epoch,leader提議某值用於投票。提案用一個唯一的遞增的數字標記。followers則接受它們收到的第一個提案。
普通操作
普通操作中,所有提案都經過leader節點。當一個client提交提案(client指分散式系統使用者),leader節點聯絡所有法定人數中的節點,如果在follower的回覆中,沒有與此衝突的提案,leader就提議某值。如果大多數follower都接受該值,那麼該值就被認為正式接收。
當然有可能另外一個節點也在嘗試成為leader,我們需要確保一旦提案被接受,該值永不改變。否則一個已經接受的提案可能被另外一個競爭leader推翻。
Lamport對該屬性有如下表述:
如果對於值v的某提案被選中,那麼每一個有較高數字標記的提案都有該值v。
原文如下:
P2: If a proposal with value
v
is chosen, then every higher-numbered proposal that is chosen has valuev
.
這個性質可以理解為一致性,也就是業已提出的提案,對其他後來提案都可見,是一種有效性。
為了保證該屬性,proposer(leader)必須首先要求followers出示它們已有的最高數字的提案和值。如果proposer發現某提案已經存在,那麼必須首先完成執行而不是重新提出提案。Lamport表述如下。
P2b. If a proposal with value
v
is chosen, then every higher-numbered proposal issued by any proposer has valuev
.
更具體地,
P2c. For any
v
andn
, if a proposal with valuev
and numbern
is issued [by a leader], then there is a setS
consisting of a majority of acceptors [followers] such that either (a) no acceptor inS
has accepted any proposal numbered less thann
, or (b)v
is the value of the highest-numbered proposal among all proposals numbered less thann
accepted by the followers inS
.
這是Paxos演算法的核心。如果之前有多個提案存在,那麼數字標記最高的提案會被提出。為了保證在leader在依次詢問每個acceptor其最新的value的過程中不出現衝突的提案,leader告知follower不要接受比當前提案數字標記小的提案。
那麼在Paxos中要達成一個決策,需要兩輪通訊。
[ Proposer ] -> Prepare(n) [ Followers ] <- Promise(n; previous proposal number and previous value if accepted a proposal in the past) [ Proposer ] -> AcceptRequest(n, own value or the value [ Followers ] associated with the highest proposal number reported by the followers) <- Accepted(n, value)
準備階段,proposer須知道任何衝突或者之前的提案。第二個階段,一個新的value或者之前被提出的value,在proposer處提出。在某些情況下,比如說同時有兩個proposer;比如訊息丟失了;或者大多數節點失靈,那麼不會有提案被多數節點接受。而這時允許的,因為最終的value還是收斂的。(也就是擁有最高數字標記的那個提案)。
這裡非常類似西方民主國家的決策流程。在某項政策需要執行前,首先舉行聽證會,廣泛徵求意見。然後根據反饋的結果確定最後的政策。
當然在工程實現上還有不少挑戰,這裡列舉了一些。
網路分割容忍一致性演算法的例子:Paxos,Raft和ZAB
強一致下的拷貝演算法
總結下各類演算法的一些關鍵特徵。
主從備份
- 單獨的,靜態的master節點
- 複製日誌,slaves節點並不參與執行具體操作
- 拷貝操作的延時沒有上限
- 無法容錯網路分割
- 在不一致和錯誤發生情況下,需要手工干涉
2PC
- 統一投票:提交或者放棄
- 靜態的master節點
- 協調者和普通節點同時掛掉情況下無法保證一致性
- 無法容錯網路分割
Paxos
- 多數投票
- 動態master節點
- 允許n/2-1節點掛
- 對延時並不太敏感