Zookeeper系列(2)--2PC、3PC及其應用

  在上一篇中,我們介紹了爲什麼使用分佈式,爲什麼會出現分佈式數據一致性問題,以及相關分佈式理論:CAP/BASE理論,這些是我們進行後邊介紹的分佈式一致性算法的基礎,正是由於在系統的可用性和數據一致性之間反覆的權衡,於是出現了一系列的一致性協議,如2PC,3PC,paxos算法等。本篇就介紹兩個最常見的分佈式一致性算法:兩階段提交(2PC),三階段提交(3PC)以及它們的相關應用。

其實我覺得這些分佈式一致性算法其實應該叫分佈式共識(distributed consensus)算法,不同的一致性上下文含義不同。共識系統則很清晰, 就是多個參與者針對某一個議題達成一致意見,如經典的具有高度容錯的基於消息傳遞的paxos算法,而2pc,3pc無法容錯,太過保守。而不是針對使副本數據都一致。這是我的一點看法,其實我看這個的概念的時候也很迷糊,到現在還是在頭腦中爭議很大,但我覺得,對於這些概念的理解應該放在固定的業務場景中,看在業務中解決了什麼問題,而不是轉牛角尖非要把這些算法分門別類。

另外,我自己的理解,這些分佈式一致性算法的目的並不是達到了副本數據的強一致性,因爲CAP理論告訴我們,這是不可能的,而是在可用性和一致性的均衡處理上,用這些算法可以達到BASE狀態,確保我們的分佈式系統正確運行,那些數據副本的同步問題並不是一致性算法所要解決的,因爲副本的同步不管是Mysql主從同步,還是其他master-slave集羣的同步都是異步的,所以不可能達到強一致性,所以這些一致性算法是用來保證分佈式系統的可用--一致的算法。例如,我們可以將paxos算法看做是分佈式共識算法,它的目的就是怎麼樣對某個值(決議)在集羣中達成一致,所以這可以用於集羣中的leader選舉,寫數據過程。而2pc,3pc就是解決分佈式事務(參與事務的事務管理器,參與者位於不同的節點)的原子性,一致性問題,而且這些算法並不是只能單一的應用於某個問題上,有很多問題都可以轉化爲用這些算法解決。

2PC、3PC的基本概念

2PC,3PC主要是基於分佈式事務的分佈式一致性算法(因爲分佈式事務也可能會導致數據的不一致問題,這跟副本的不一致性從大類上看是都歸於數據的不一致)。
在分佈式系統中,各個節點之間在物理上相互獨立,通過網絡進行溝通和協調。由於存在事務機制,可以保證每個獨立節點上的數據操作可以滿足ACID。但是,相互獨立的節點之間無法準確的知道其他節點中的事務執行情況。所以從理論上講,兩臺機器理論上無法達到一致的狀態。如果想讓分佈式部署的多臺機器中的數據保持一致性,那麼就要保證在所有節點的數據寫操作,要不全部都執行,要麼全部的都不執行。但是,一臺機器在執行本地事務的時候無法知道其他機器中的本地事務的執行結果。所以他也就不知道本次事務到底應該commit還是 roolback。所以,常規的解決辦法就是引入一個“協調者”的組件來統一調度所有分佈式節點的執行。使用2PC,3PC可以實現分佈式的強一致性和分佈式事務(廣義-狹義??)。
分佈式事務的概念:
通常把一個數據庫內部的事務處理,如對多個表的操作,作爲本地事務看待。數據庫的事務處理對象是本地事務,而分佈式事務處理的對象是全局事務。 所謂全局事務,是指分佈式事務處理環境中,多個數據庫可能需要共同完成一個工作,這個工作即是一個全局事務,例如,一個事務中可能更新幾個不同的數據庫(可以是不同的應用對應的數據庫,也可以是副本??)。對數據庫的操作發生在系統的各處但必須全部被提交或回滾。此時一個數據庫對自己內部所做操作的提交不僅依賴本身操作是否成功,還要依賴與全局事務相關的其它數據庫的操作是否成功,如果任一數據庫的任一操作失敗,則參與此事務的所有數據庫所做的所有操作都必須回滾。 一般情況下,某一數據庫無法知道其它數據庫在做什麼。因此,在一個 DTP 環境中,交易中間件是必需的,由它通知和協調相關數據庫的提交或回滾。而一個數據庫只將其自己所做的操作(可恢復)影射到全局事務中。
二階提交協議三階提交協議就是根據這一思想衍生出來的。可以說二階段提交其實就是實現XA分佈式事務的關鍵(確切地說:兩階段提交主要保證了分佈式事務的原子性:即所有結點要麼全做要麼全不做)

我的理解:其實我們也可以將2PC,3PC算法看做是和paxos一樣的用來對某個決議達成共識的算法,這裏的決議就是是否要提交這個事務從而更新數據。但是2PC,3PC存在很多問題,比如太過保守、單點問題,所以纔會產生具有高度容錯性的paxos算法。

2PC

二階段提交的算法思路可以概括爲:參與者將操作成敗通知協調者,再由協調者根據所有參與者的反饋情報決定各參與者是否要提交操作還是中止操作。2PC就是用來解決分佈式事務的原子性問題。所謂的兩個階段是指:第一階段:準備階段(投票階段)和第二階段:提交階段(執行階段)。其過程爲:

第一階段:準備階段

事務協調者(事務管理器)給每個參與者(資源管理器)發送Prepare消息,每個參與者要麼直接返回失敗(如權限驗證失敗),要麼在本地執行事務,寫本地的redo和undo日誌,但不提交,然後向協調者反饋YES,NO。
其具體過程:

  • 協調者節點向所有參與者節點詢問是否可以執行提交操作(vote),並開始等待各參與者節點的響應。
  • 參與者節點執行詢問發起爲止的所有事務操作,並將Undo信息和Redo信息寫入日誌。(注意:若成功這裏其實每個參與者已經執行了事務操作)
  • 各參與者節點響應協調者節點發起的詢問。如果參與者節點的事務操作實際執行成功,則它返回一個”同意”消息;如果參與者節點的事務操作實際執行失敗,則它返回一個”中止”消息。
第二階段:提交階段

如果協調者收到了參與者的失敗消息或者超時,直接給每個參與者發送回滾(Rollback)消息;否則,發送提交(Commit)消息;參與者根據協調者的指令執行提交或者回滾操作,釋放所有事務處理過程中使用的鎖資源。(注意:必須在最後階段釋放鎖資源)
注意:在2PC中,當等待超時,會進行回滾操作。

提交階段可以分兩種情況進行討論:
執行事務提交

當協調者節點從所有參與者節點獲得的相應消息都爲”同意”時:

  • 協調者節點向所有參與者節點發出”正式提交(commit)”的請求。
  • 參與者節點正式完成操作,並釋放在整個事務期間內佔用的資源。
  • 參與者節點向協調者節點發送”完成”消息。
  • 協調者節點受到所有參與者節點反饋的”完成”消息後,完成事務。

如下圖所示:

中斷事務

如果任一參與者節點在第一階段返回的響應消息爲”中止”,或者 協調者節點在第一階段的詢問超時之前無法獲取所有參與者節點的響應消息時:

  • 協調者節點向所有參與者節點發出”回滾操作(rollback)”的請求。
  • 參與者節點利用之前寫入的Undo信息執行回滾,並釋放在整個事務期間內佔用的資源。
  • 參與者節點向協調者節點發送”回滾完成”消息。
  • 協調者節點受到所有參與者節點反饋的”回滾完成”消息後,取消事務。

如下圖所示:


不管最後結果如何,第二階段都會結束當前事務二階段提交確實能夠提供原子性的操作。

2PC的優缺點

優點:2PC的優點是很顯然的,原理簡單,實現方便。目前,絕大多數關係型數據庫都是採用兩階段提交協議來完成分佈式事務處理的。(也就是上邊的2pc過程應用於關係型數據庫的分佈式事務)

缺點:2PC的缺點也很致命:同步阻塞,單點問題,數據不一致,太過保守
  • 1、同步阻塞問題。執行過程中,所有參與節點都是事務阻塞型的。當參與者佔有公共資源時,其他第三方節點訪問公共資源不得不處於阻塞狀態,各個參與者在等待協調者發出提交或中斷請求時,會一直阻塞,而協調者的發出時間要依賴於所有參與者的響應時間,如果協調者宕機了(單點),那麼他就一直阻塞在這,而且無法達成一致(3PC引入了超時提交解決)。
  • 2、單點故障。由於協調者的重要性,一旦協調者發生故障。參與者會一直阻塞下去。尤其在第二階段,協調者發生故障,那麼所有的參與者還都處於鎖定事務資源的狀態中,而無法繼續完成事務操作。(如果是協調者掛掉,可以重新選舉一個協調者,但是無法解決因爲協調者宕機導致的參與者處於阻塞狀態的問題)
  • 3、數據不一致。出現分區,或者網絡故障。在二階段提交的階段二中,當協調者向參與者發送commit請求之後,發生了局部網絡異常或者在發送commit請求過程中協調者發生了故障,這回導致只有一部分參與者接受到了commit請求。而在這部分參與者接到commit請求之後就會執行commit操作。但是其他部分未接到commit請求的機器則無法執行事務提交。於是整個分佈式系統便出現了數據部一致性的現象。
  • 4、太過保守:2pc沒有設計相應的容錯機制,當任意一個參與者節點宕機,那麼協調者超時沒收到響應,就會導致整個事務回滾失敗。
  • 5、二階段無法解決的問題:協調者(在第二階段)發出commit消息之後宕機,而唯一接收到這條消息的參與者同時也宕機了。那麼即使協調者通過選舉協議產生了新的協調者,這條事務的狀態也是不確定的,沒人知道事務是否被已經提交。
由於二階段提交存在着諸如同步阻塞、單點問題、腦裂等缺陷,所以,研究者們在二階段提交的基礎上做了改進,提出了三階段提交。

3PC

3PC,即三階段提交,是2階段提交的改進版,其將二階段提交協議的“準備階段”一份爲二,形成了cancommit,precommit,do commit三個階段。

與兩階段提交不同的是,三階段提交有兩個改動點。

1、引入超時機制。(超時提交策略,當第三階段參與者等待協調者超時後會提交事務,解決參與者同步阻塞問題,同時能在發生單點故障時,繼續達成一致)
2、在第一階段和第二階段中插入一個準備階段。(也是爲了減少同步阻塞的發生範圍)

CanCommit階段

3PC的CanCommit階段其實和2PC的準備階段很像。協調者向參與者發送commit請求,參與者如果可以提交就返回Yes響應,否則返回No響應。

  • 1.事務詢問 協調者向參與者發送CanCommit請求。詢問是否可以執行事務提交操作。然後開始等待參與者的響應。
  • 2.響應反饋 參與者接到CanCommit請求之後,正常情況下,如果其自身認爲可以順利執行事務,則返回Yes響應,並進入預備狀態。否則反饋No

PreCommit階段

協調者根據參與者的反應情況來決定是否可以記性事務的PreCommit操作。根據響應情況,有以下兩種可能。

假如協調者從所有的參與者獲得的反饋都是Yes響應,那麼就會執行事務的預執行。

  • 1.發送預提交請求 協調者向參與者發送PreCommit請求,並進入Prepared階段。
  • 2.事務預提交 參與者接收到PreCommit請求後,會執行事務操作,並將undo和redo信息記錄到事務日誌中。
  • 3.響應反饋 如果參與者成功的執行了事務操作,則返回ACK響應,同時開始等待最終指令。

假如有任何一個參與者向協調者發送了No響應,或者等待超時之後,協調者都沒有接到參與者的響應,那麼就執行事務的中斷。

  • 1.發送中斷請求 協調者向所有參與者發送abort請求。
  • 2.中斷事務 參與者收到來自協調者的abort請求之後(或超時之後,仍未收到協調者的請求),執行事務的中斷。

doCommit階段

該階段進行真正的事務提交,也可以分爲以下兩種情況。

執行提交

1.發送提交請求 協調接收到參與者發送的ACK響應,那麼他將從預提交狀態進入到提交狀態。並向所有參與者發送doCommit請求。

2.事務提交 參與者接收到doCommit請求之後,執行正式的事務提交。並在完成事務提交之後釋放所有事務資源。

3.響應反饋 事務提交完之後,向協調者發送Ack響應。

4.完成事務 協調者接收到所有參與者的ack響應之後,完成事務。

中斷事務 協調者沒有接收到參與者發送的ACK響應(可能是接受者發送的不是ACK響應,也可能響應超時),那麼就會執行中斷事務。

1.發送中斷請求 協調者向所有參與者發送abort請求

2.事務回滾 參與者接收到abort請求之後,利用其在階段二記錄的undo信息來執行事務的回滾操作,並在完成回滾之後釋放所有的事務資源。

3.反饋結果 參與者完成事務回滾之後,向協調者發送ACK消息

4.中斷事務 協調者接收到參與者反饋的ACK消息之後,執行事務的中斷。


注意:
在doCommit階段,如果參與者無法及時接收到來自協調者的doCommit或者rebort請求時,會在等待超時之後,會繼續進行事務的提交。
引入超時提交的依據:
其實這個應該是基於概率來決定的,當進入第三階段時,說明參與者在第二階段已經收到了PreCommit請求,那麼協調者產生PreCommit請求的前提條件是他在第二階段開始之前,收到所有參與者的CanCommit響應都是Yes。(一旦參與者收到了PreCommit,意味他知道大家其實都同意修改了)所以,一句話概括就是,當進入第三階段時,由於網絡超時等原因,雖然參與者沒有收到commit或者abort響應,但是他有理由相信:成功提交的機率很大。


3PC解決的問題:

一旦進入階段三:可能會出現:1 協調者出現問題(單點)    2 協調者參與者之間的網絡出現故障

二階段無法解決的問題:協調者(在第二階段)發出commit消息之後宕機,而唯一接收到這條消息的參與者同時也宕機了。那麼即使協調者通過選舉協議產生了新的協調者,這條事務的狀態也是不確定的,沒人知道事務是否被已經提交。

相對於2PC,3PC主要解決的單點故障問題,並減少阻塞,因爲一旦參與者無法及時收到來自協調者的信息之後,他會默認執行commit(所以產生新的協調者之後,可以確定事務的狀態,這就解決了單點)。而不會一直持有事務資源並處於阻塞狀態。

3PC無法解決:數據不一致以及太過保守問題。

但是這種機制也會導致數據一致性問題,因爲,由於網絡原因,協調者發送的abort響應沒有及時被參與者接收到,那麼參與者在等待超時之後執行了commit操作。這樣就和其他接到abort命令並執行回滾的參與者之間存在數據不一致的情況。

2PC,3PC的應用

我們前面說過,對於大多數的關係型數據庫來說,解決分佈式事務的方法就是利用兩階段提交2pc,其過程就是我們上邊介紹的2PC的過程。可參考:Mysql的2pc應用 。主要涉及mysql的幾個日誌:mysql日誌文件 ,另外還有在事務過程中的redo_log undo redo undo

MySQL的主從同步複製原理

我們注意,一般的主從同步並不涉及相關的分佈式協議,基本都是異步使用日誌或鏡像進行主從同步,達成最終一致性。比如:DHFS的namenode,redis的主從同步以及MySQL的主從同步。不要把2pc,3pc,paxos等分佈式一致性算法想象爲處理主從複製這個過程的算法。

複製過程中一個服務器充當主服務器,而一個或多個其它服務器充當從服務器。主服務器將更 新寫入二進制日誌文件,並維護文件的一個索引以跟蹤日誌循環。這些日誌可以記錄發送到從服務器的更新。當一個從服務器連接主服務器時,它通知主服務器從服 務器在日誌中讀取的最後一次成功更新的位置。從服務器接收從那時起發生的任何更新,然後封鎖並等待主服務器通知新的更新。
  MySQL複製技術有以下一些特點:
         (1)    數據分佈 (Data distribution )
         (2)    負載平衡(load balancing)
         (3)    備份(Backups) 
         (4)    高可用性和容錯行 High availability and failover 

 整體上來說,複製有3個步驟:   

       (1)    master將改變記錄到二進制日誌(binary log)中(這些記錄叫做二進制日誌事件,binary log events);
       (2)    slave將master的binary log events拷貝到它的中繼日誌(relay log);
       (3)    slave重做中繼日誌中的事件,將改變反映它自己的數據。


第一步和後邊的步驟是一個異步的過程。

      該過程的第一部分就是master記錄二進制日誌。在每個事務更新數據完成之前,master在二日誌記錄這些改變。MySQL將事務串行的寫入二進制日 志,即使事務中的語句都是交叉執行的。在事件寫入二進制日誌完成後,master通知存儲引擎提交事務。
       下一步就是slave將master的binary log拷貝到它自己的中繼日誌。首先,slave開始一個工作線程——I/O線程。I/O線程在master上打開一個普通的連接,然後開始binlog dump process。Binlog dump process從master的二進制日誌中讀取事件,如果已經跟上master,它會睡眠並等待master產生新的事件。I/O線程將這些事件寫入中 繼日誌。
       SQL slave thread(SQL從線程)處理該過程的最後一步。SQL線程從中繼日誌讀取事件,並重放其中的事件而更新slave的數據,使其與master中的數 據一致。只要該線程與I/O線程保持一致,中繼日誌通常會位於OS的緩存中,所以中繼日誌的開銷很小。
        此外,在master中也有一個工作線程:和其它MySQL的連接一樣,slave在master中打開一個連接也會使得master開始一個線程。複製 過程有一個很重要的限制——複製在slave上是串行化的,也就是說master上的並行更新操作不能在slave上並行操作。


瞭解了2PC和3PC之後,我們可以發現,無論是二階段提交還是三階段提交都無法徹底解決分佈式的一致性問題。Google Chubby的作者Mike Burrows說過,世上只有一種一致性算法,那就是Paxos,所有其他一致性算法都是Paxos算法的不完整版。後面的文章會介紹這個公認爲難於理解但是行之有效的Paxos算法。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章