1. 程式人生 > >[分散式系統學習]閱讀筆記 Distributed systems for fun and profit 之四 Replication 拷貝

[分散式系統學習]閱讀筆記 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 value v.

這個性質可以理解為一致性,也就是業已提出的提案,對其他後來提案都可見,是一種有效性。

為了保證該屬性,proposer(leader)必須首先要求followers出示它們已有的最高數字的提案和值。如果proposer發現某提案已經存在,那麼必須首先完成執行而不是重新提出提案。Lamport表述如下。

P2b. If a proposal with value v is chosen, then every higher-numbered proposal issued by any proposer has value v.

更具體地,

P2c. For any v and n, if a proposal with value v and number n is issued [by a leader], then there is a set S consisting of a majority of acceptors [followers] such that either (a) no acceptor in S has accepted any proposal numbered less than n, or (b) v is the value of the highest-numbered proposal among all proposals numbered less than n accepted by the followers in S.

這是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節點掛
  • 對延時並不太敏感