分散式事務之三階段提交
三階段提交(3PC)是二階段提交(2PC)的改進版本,三階段提交協議主要是為了解決兩階段提交協議的阻塞問題,2pc存在的問題是當協調者崩潰時,參與者不能做出最後的選擇。因此參與者可能在協調者恢復之前保持阻塞。三階段提交(Three-phase commit),是二階段提交(2PC)的改進版本。
三階段提交3PC
與兩階段提交不同的是,三階段提交有兩個改動點:
- 引入超時機制。同時在協調者和參與者中都引入超時機制;
- 在第一階段和第二階段中插入一個準備階段。保證了在最後提交階段之前各參與節點的狀態是一致的。
3PC把2PC的準備階段再次一分為二,這樣三階段提交就有CanCommit、PreCommit、DoCommit三個階段。
CanCommit階段
之前2PC的一階段是本地事務執行結束後,最後不Commit,等其它服務都執行結束並返回Yes,由協調者發生commit才真正執行commit。而這裡的CanCommit指的是 嘗試獲取資料庫鎖 如果可以,就返回Yes。這階段主要分為2步
- 事務詢問:協調者向參與者傳送CanCommit請求。詢問是否可以執行事務提交操作。然後開始等待參與者的響應;
- 響應反饋:參與者接到CanCommit請求之後,正常情況下,如果其自身認為可以順利執行事務,則返回Yes響應,並進入預備狀態。否則反饋No。
PreCommit階段
在階段一中,如果所有的參與者都返回Yes的話,那麼就會進入PreCommit階段進行事務預提交。這裡的PreCommit階段 跟上面的第一階段是差不多的,只不過這裡協調者和參與者都引入了超時機制 (2PC中只有協調者可以超時,參與者沒有超時機制),主要包含兩個步驟:
- 事務預提交:參與者接收到PreCommit請求後,會執行事務操作,並將undo和redo資訊記錄到事務日誌中。
- 響應反饋:如果參與者成功的執行了事務操作,則返回ACK響應,同時開始等待最終指令。
假如有任何一個參與者向協調者傳送了No響應,或者等待超時之後,協調者都沒有接到參與者的響應,那麼就執行事務的中斷:
- 傳送中斷請求:協調者向所有參與者傳送abort請求。
- 中斷事務:參與者收到來自協調者的abort請求之後(或超時之後,仍未收到協調者的請求),執行事務的中斷。
DoCommit階段
該階段進行真正的事務提交,也可以分為以下兩種情況。
執行提交,傳送提交請求 協調接收到參與者傳送的ACK響應,那麼他將從預提交狀態進入到提交狀態。並向所有參與者傳送doCommit請求。
- 事務提交:參與者接收到doCommit請求之後,執行正式的事務提交。並在完成事務提交之後釋放所有事務資源。
- 響應反饋:事務提交完之後,向協調者傳送Ack響應。
- 完成事務:協調者接收到所有參與者的ack響應之後,完成事務。
中斷事務,協調者沒有接收到參與者傳送的ACK響應(可能是接受者傳送的不是ACK響應,也可能響應超時),那麼就會執行中斷事務。
- 傳送中斷請求:協調者向所有參與者傳送abort請求
- 事務回滾:參與者接收到abort請求之後,利用其在階段二記錄的undo資訊來執行事務的回滾操作,並在完成回滾之後釋放所有的事務資源。
- 反饋結果:參與者完成事務回滾之後,向協調者傳送ACK訊息
- 中斷事務:協調者接收到參與者反饋的ACK訊息之後,執行事務的中斷。
總結
相對於2PC,3PC主要解決的單點故障問題,並減少阻塞,因為一旦參與者無法及時收到來自協調者的資訊之後,他會預設執行commit。而不會一直持有事務資源並處於阻塞狀態。但是這種機制也會導致資料一致性問題,因為,由於網路原因,協調者傳送的abort響應沒有及時被參與者接收到,那麼參與者在等待超時之後執行了commit操作。這樣就和其他接到abort命令並執行回滾的參與者之間存在資料不一致的情況。
在2PC中一個參與者的狀態只有它自己和協調者知曉,假如協調者提議後自身宕機,在協調者備份啟用前一個參與者又宕機,其他參與者就會進入既不能回滾、又不能強制commit的阻塞狀態,直到參與者宕機恢復。
參與者如果在不同階段宕機,我們來看看3PC如何應對:
- 階段1: 協調者或協調者備份未收到宕機參與者的vote,直接中止事務;宕機的參與者恢復後,讀取logging發現未發出贊成vote,自行中止該次事務
- 階段2: 協調者未收到宕機參與者的precommit ACK,但因為之前已經收到了宕機參與者的贊成反饋(不然也不會進入到階段2),協調者進行commit;協調者備份可以通過問詢其他參與者獲得這些資訊,過程同理;宕機的參與者恢復後發現收到precommit或已經發出贊成vote,則自行commit該次事務
- 階段3: 即便協調者或協調者備份未收到宕機參與者t的commit ACK,也結束該次事務;宕機的參與者恢復後發現收到commit或者precommit,也將自行commit該次事務。