事務 - 2PC

2PC

githubgit

上一篇文章中咱們介紹了本地事務,隨着軟件複雜度的上升,咱們會須要一種能夠在多個數據庫之間完成事務(分佈式事務)的方法,而這個方法也必須可以保證ACID。因而就出現了2PC - Two phase commit protocol。事實上2PC不單單適用於多數據庫事務場景下使用,也適用於全部支持2PC的參與方(Participants)。github

算法介紹

2PC的參與方有:算法

  1. 一個做爲Coordinator的節點
  2. 多個做爲Cohort的網絡節點

2PC假設:數據庫

  1. 全部節點都有一個穩定存儲用以保存WAL(write-ahead log
  2. 沒有一個節點會永遠崩潰(即會最終恢復)
  3. write-ahead log中存儲的數據永遠不會丟失,不會因崩潰而損壞
  4. 任意兩個節點都可以互相通訊

第四個假設太過嚴格,實際上有不是全部的2PC實現都知足。第1、二個假設則大多數2PC實現都能知足。segmentfault

PS. 若是某個節點徹底損壞(好比服務器物理損毀),那麼數據就直接丟失了。服務器

2PC的執行步驟:網絡

  1. Commit request phase /Voting phase。這個階段作:併發

    1. Coordinator發送一個查詢是否贊成commit的請求到全部Cohort,而且等待全部Cohort給出應答。
    2. Cohort收到請求,開始執行事務,執行到就差commit爲止(不commit)。
    3. 每一個Cohort根據操做結果返回Yes或No
  2. Commit phase / Completion phase。這個階段分兩種狀況:分佈式

    1. 成功。全部Cohort應答Yeside

      1. Coordinator發送commit指令到全部Cohort
      2. 每一個Cohort執行commit,併發送ack到Coordinator
      3. 當Coordinator收到每一個Cohort的ack以後則事務完成
    2. 失敗。任意Cohort應答No,或者在commit request階段超時

      1. Coordinator發送rollback指令到全部Cohort
      2. 每一個Cohort執行rollback,併發送ack到Coordinator
      3. 當Coordinator收到每一個Cohort的ack以後則事務撤銷

消息流(摘自wiki):

Coordinator                                         Cohort
                              QUERY TO COMMIT
                -------------------------------->
                              VOTE YES/NO           prepare*/abort*
                <-------------------------------
commit*/abort*                COMMIT/ROLLBACK
                -------------------------------->
                              ACKNOWLEDGMENT        commit*/abort*
                <--------------------------------  
end

2PC的通訊次數是:

  1. 若是實現沒有要求任意兩個Cohort能夠通訊,那麼是2n(n=Cohort數量)
  2. 若是實現要求任意兩個Cohort能夠通訊,那麼是n^2

異常處理

咱們把上面的流程簡化以便說明異常處理:

  1. Coordinator發送query to commit
  2. Cohort執行prepare
  3. Cohort返回ack
  4. Coordinator發送commit/rollback
  5. Cohort執行commit/rollback
  6. Cohort返回ack

從Coordinate角度來看出現異常要怎麼處理:

  1. step 1發生異常,Coordinator須要執行rollback
  2. step 二、3發生異常,意味着Coordinator沒有收到Cohort的響應,這個時候因認定爲失敗,執行rollback
  3. step 4發生異常,Coordinator重試commit/rollback
  4. step 五、6發生異常,意味着Coordinator沒有收到Cohort的響應,這個時候因認定爲失敗,重試commit/rollback

從Cohort角度來看看看出現異常怎麼處理:

  1. step 1,意味着Cohort沒有收到請求,什麼都不須要作
  2. step 2,意味着Cohort沒有執行成功,什麼都不須要作
  3. step 3,意味着Coordinator沒有收到結果,什麼都不須要作,等待Coordinator重試便可。Cohort要保證prepare是冪等的。
  4. step 4,等待Coordinator重試便可,這裏有點tricky,若是Coordinator遲遲不retry,那麼Cohort要自行rollback,不然就會形成資源死鎖。
  5. step 5,等待Coordinator重試便可
  6. step 6,意味着Coordinator沒有收到結果,什麼都不須要作,等待Coordinator重試便可,Cohort要保證commit/rollback是冪等的。

觀察一下你就會發現依然存在漏洞——會出現違反一致性的狀況:

  1. 若Coordinator/Cohort因崩潰遺失了信息,有的Cohort已commit,有的Cohort則恢復到commit以前的狀態。
  2. 若Coordinator在step 4發送commit,而Cohort在rollback(因timeout致使的rollback)。

出現上面的狀況就須要人工介入了。

更多2PC的異常處理推理詳見這篇slides

缺點

根據上面的算法介紹能夠看出2PC是一個阻塞協議:

  • 若是兩個事務針對同一個數據,那麼後面的要等待前面完成,這是因爲Cohort採用的是本地事務所決定的
  • Cohort在commit request phase以後會阻塞,直到進入Coordinator告之Cohort進入commit phase

對於ACID的保證

2PC所保證的ACID和本地事務所提到的ACID不太同樣——事實上對於全部分佈式事務來講都不太同樣:

  • A,正常狀況下保證
  • C,在某個時間點,會出現A庫和B庫的數據違反一致性要求的狀況
  • I,在某個時間點,A事務可以讀到B事務部分提交的結果
  • D,和本地事務同樣,只要commit則數據被持久

XA

XA是一個針對分佈式事務的spec,它實現了2PC協議。在XA中定義了兩種參與方:Transaction Manager(TM)和Resource Manager(RM),其中TM=2PC中的Coordinator,RM=2PC中的Cohort。

Java規範中的JTA(Java Transaction API)定義了XA的Java接口,JTA的實現有Bitronix、Atomikos等等。

參考資料

相關文章
相關標籤/搜索