乾貨放送 | 細說分佈式事務兩階段提交 一、實現分佈式事務關鍵組件 二、 經典兩階段提交協議 三、 兩階段提交協議異常處理 四、 緩解2PC blocking思路

作者:旺德 阿里雲高級開發工程師

在分佈式系統中,讀寫位於多個節點的數據,如果依舊想保證ACID特性,就必須實現分佈式事務。而其實現關鍵則是適當的提交協議,目前最簡潔,且使用最廣泛的無疑是兩階段提交協議(2PC)。

一、實現分佈式事務關鍵組件

單機系統通過事務管理器(transaction manager,TM)實現本地事務。分佈式系統中,需要協調多個節點的事務管理器,共同提交成功或失敗,因此需要事務協調者(transaction coordinator,TC)。一個分佈式事務管理器,可以粗略地劃分爲這兩個子系統。這兩個子系統根據自己在事務執行中扮演的角色,也可稱之爲參與者與協調者。

本地事務管理器負責本機事務併發控制異常恢復等功能,事務協調者負責開啓事務,將事務劃分爲多個子事務分發到相應的節點執行,並協調事務完成(一起提交成功或失敗)。在實現中,TM和TC可以實現在同一個進程中,也可以部署在不同的節點。

二、 經典兩階段提交協議

兩階段提交的流程比較簡單。當分佈式事務T執行完成進入提交階段,TC便開啓兩階段提交流程。


Phase 1 Prepare:
1.TC寫本地日誌<Prepare T>,並持久化。TC向所有參與者發送Prepare T消息。2.各參與者TM收到Prepare T消息,根據自身情況,決定是否提交事務。

  • 如果決定提交,TM寫<Ready T>日誌並持久化,向TC發送Ready T消息。

  • 如果決定不提交,TM寫<Abort T>日誌並持久化,向TC發送Abort T消息,本地也進入事務abort流程。

Phase 2 Commit :
1.當TC收到所有節點的迴應,或者等待超時,決定事務commit或abort。

  • 如果所有參與者迴應Ready T,則TC先寫<Commit T>日誌並持久化,再向所有參與者發送Commit T消息。

  • 如果收到至少一個參與者Abort T迴應,或者在超時時間內有參與者未迴應,則TC先寫<Abort T>日誌,再向所有參與者發送Abort T消息。

2.參與者收到TC的消息後,寫<Commit T>或<Abort T>日誌並持久化。
兩階段提交協議可以保證分佈式事務執行的一個關鍵點:參與者在向協調者發生Ready T消息前,隨時都可以自己決定是否abort,一旦這個消息發送,那麼這個事務就進入ready狀態,commit和abort完全由協調者控制。Ready T消息本質上是參與者向協調者發送的一個鄭重的、不可逆的承諾。

爲了保證這一個承諾,參與者需要在發送Ready T消息前將所有必要的信息持久化,否則如果參與者在發送Ready T後異常宕機,重啓後可能無法遵守以上承諾。在第二階段,當協調者寫了<Commit T>或<Abort T>日誌,整個事務的命運就被決定了,不會再發生變化了。

爲了優化2PC性能,減少關鍵路徑的持久化和RPC次數是關鍵,一種對經典2PC的優化思路如下:
協調者無狀態,不再持久化日誌,但是爲了方便宕機重啓後恢復事務狀態,需要向每個參與者發送事務的參與者名單並持久化。這樣即使協調者宕機,參與者也可以方便地詢問其他參與者事務狀態了。該思路相當於參與者在協調者宕機時,自己擔當起協調者詢問事務狀態的任務。

只要所有參與者prepare成功,事務一定會成功提交。因此爲了減少提交延時,協調者可以在收到所有參與者prepare成功後就返回客戶端成功,但如此,讀請求可能會因爲提交未完成而等待,從而增大讀請求的延時。反過來,如果協調者確認所有參與者都提交成功才返回客戶端成功,提交延時比較長,但會減少讀請求延時。

三、 兩階段提交協議異常處理

兩階段提交協議的正常流程較爲簡單,但它還需要考慮分佈式系統中各種異常問題(節點失敗,網絡分區等)。

1.如果協調者檢測到參與者失敗

  • 如果參與者在發送Ready T前失敗,則協調者認爲該節點事務Abort,並開始abort流程。

  • 如果參與者在發送Ready T後失敗,證明參與者本地事務已經持久化,協調者忽視參與者失敗,繼續事務流程。

2.如果參與者在事務提交過程中失敗,其恢復過程,需要根據參與者日誌內容,決定本地事務狀態。

  • 如果日誌中包含<Commit T>日誌,證明事務已經成功提交,REDO(T)。

  • 如果日誌中包含<Abort T>日誌,證明事務已經失敗,UNDO(T)。

  • 如果日誌中包含<Ready T>日誌,參與者P需向其它節點諮詢當前事務狀態。

  • 如果協調者正常,則向告知參與者P,事務已經commit或是abort,參與者依此REDO(T)或UNDO(T)。

  • 如果協調者異常,則向其它參與者詢問事務狀態。

a.如果其他參與者收到信息,並已知事務是commit還是abort狀態,需回覆參與者P事務狀態。

b.如果所有的參與者現在都不知道該事務的狀態(事務上下文銷燬了,或者自己也處於未決狀態),那麼該事務處於暫時既不能commit也不能abort。需要定期向其它節點問詢事務狀態,直到得到答案。(這是2PC最不想遇到的一個場景)

c.如果日誌中不包含上述幾種日誌,說明該參與者在向協調者發送Ready T消息前就失敗了。由於協調者沒有收到參與者的迴應,會超時Abort,因此該參與者在恢復過程中,遇到這種情況也需要abort。

3.如果協調者在事務提交過程中失敗。參與者需要根據全局事務狀態(通過與其它參與者通信)決定本地行爲。

事務狀態已經形成決議:

  • 如果至少有一個參與者中事務T已經提交(參與者包含<Commit T>日誌),說明T必須要提交。

  • 如果至少有一個參與者中事務T已經Abort(參與者包含<Abort T>日誌),說明T必須要Abort。

事務狀態未形成決議:

  • 如果至少有一個參與者沒有進入Ready狀態(參與者不包含<Ready T>日誌)。說明全局還未就提交與否達成協議。有兩種選擇:(1)等待協調者恢復。(2)參與者自行abort。爲了減少資源佔用時間,選擇後者居多。

  • 如果所有參與者都進入了Ready狀態,且都沒有<Commit T>或<Abort T>日誌(事實上,即使有這些日誌,查日誌也是一種比較費的操作,還需要考慮日誌回收的問題),這種情況下,參與者誰都不知道現在事務的狀態,只能死等協調者恢復。(又到了這個最不想遇到的場景)

當參與者均進入ready狀態,等待協調者的下一步指令,協調者在這個時候出現異常,那麼參與者將一直持有系統資源,如果基於鎖實現的併發控制,還會一直持有鎖,導致其他事務等待。

這種情況如果持續較舊,會對系統產生巨大的影響。因此2PC最大的問題就是協調者失敗,可能會導致事務阻塞,未決事務的最終狀態,只能等待協調者恢復後才確定。同時在這種情況下,參與者宕機重啓,回放到這類未決事務,重啓回放完本地WAL,還是會有一個未決的事務不知道怎麼處理,事務持有的資源也不能釋放。

四、 緩解2PC blocking思路

三階段提交是兩階段提交的延伸,目的是解決2PC block的問題,但是也引入了其它問題。它的解決方式是爲參與者引入timeout機制,如果參與者成功PreCommit後,一直收不到協調者最後的DoCommit請求,等待超時自動提交,顯然這樣會引入一致性問題。

例如,協調者收到一個參與者PreCommit失敗,打算髮abort請求給其它參與者時宕機,顯然此時該分佈式事務應該失敗,但一些參與者可能因爲超時而提交。

爲了解決這個問題,3PC多引進了一個階段,就是第一個階段CanCommit階段,協調者詢問所有參與者是否可以提交,參與者如果狀態正常,就會迴應可以提交,但此時並不會佔用任何系統資源。如果協調者及時收到了所有參與者ok的迴應,便會認爲各個參與者正常,之後的提交應該不會失敗。但是實質上,仍有小概率失敗的可能:某參與者PreCommit失敗後,協調者和參與者都宕機,其它參與者超時自動提交,產生不一致。

因此3PC還有一個關鍵優化是協調者宕機後,迅速找到一個繼任者,繼續未完的流程,儘量保證不會出現參與者超時提交的現象。但是如果出現諸如網絡分區等異常,新的協調者聯繫不上參與者,還是會產生一致性問題。

3PC通過犧牲一定的C(onsistency)來提高A(vailability),並且增加了網絡開銷,這些都是OLTP系統很難接受的,所以基本沒有系統會採用。

但是協調者高可用,確實可以使block的時間大幅減少,基於諸如Paxos/Raft的一致性協議的高可用方案,可以讓多個節點就commit/abort達成一致後,再去通知參與者,當協調者出現異常,可以迅速選出新的協調者,推進事務至完成。


更多精彩內容可關注微信公衆號“ 阿里巴巴數據庫技術”。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章