目前大部分的網際網路公司在設計整體架構的時候,都會按照業務模組,將系統拆分成很多小系統,例如訂單系統、卡券系統、支付系統等等,簡單來說,就是分而治之,這樣每個人可以專注維護自己的程式碼。然後不同的小系統自己開發、測試和上線,都不會跟別人耦合在一起,可以自己獨立進行,非常的方便,大大簡化了大規模系統的開發成本。
然而有了多個子系統之後,分散式事務應該怎麼來實現?下面就介紹一下分散式事務的幾種實現方式。
一、分散式事務的概念
分散式事務指的是一個請求在多個系統的呼叫鏈當中如何確保資料一致。例如,一個支付請求,支付成功後,會回撥請求訂單系統修改訂單狀態,會回撥請求卡券系統修改卡券狀態。那麼如果回撥請求因為網路原因丟失了,可能出現使用者支付了,訂單仍然顯示為未支付狀態,卡券仍然在凍結中,無法置為已使用狀態。
二、2PC/3PC/TCC等分散式事務協議
2PC兩階段提交協議
2PC是非常經典的強一致、中心化的原子提交協議,協議中定義了兩類節點:一箇中心化協調者節點和多個參與者節點。2PC分為兩個階段:
準備階段:
1、協調者向所有參與者傳送事務內容,詢問是否可以提交事務,並等待所有參與者答覆。
2、各參與者執行事務操作,將Undo和Redo資訊記入事務日誌中(但不提交事務)。
3、如參與者執行成功,給協調者反饋YES,即可以提交;如執行失敗,給協調者反饋NO,即不可提交。
提交階段:
(所有參與者均反饋YES)
1、協調者向所有參與者發出正式提交事務的請求(即Commit請求)。
2、參與者執行Commit請求,並釋放整個事務期間佔用的資源。
3、各參與者向協調者反饋Ack完成的訊息。
4、協調者收到所有參與者反饋的Ack訊息後,即完成事務提交。
(任何一個參與者反饋NO)
1、協調者向所有參與者發出回滾請求(即Rollback請求)。
2、參與者使用階段1中的Undo資訊執行回滾操作,並釋放整個事務期間佔用的資源。
3、各參與者向協調者反饋Ack完成的訊息。
4、協調者收到所有參與者反饋的Ack訊息後,即完成事務中斷。
2PC兩階段提交過程中會遇到一些問題:
1、效能問題。從流程上我們可以看得出,其最大缺點就在於它的執行過程中間,節點都處於阻塞狀態。各個運算元據庫的節點此時都佔用著資料庫資源,只有當所有節點準備完畢,事務協調者才會通知進行全域性提交,參與者進行本地事務提交後才會釋放資源。這樣的過程會比較漫長,對效能影響比較大。
2、協調者單點故障問題。事務協調者是整個XA模型的核心,一旦事務協調者節點掛掉,會導致參與者收不到提交或回滾的通知,從而導致參與者節點始終處於事務無法完成的中間狀態。
3、丟失訊息導致的資料不一致問題。在第二個階段,如果發生區域性網路問題,一部分事務參與者收到了提交訊息,另一部分事務參與者沒收到提交訊息,那麼就會導致節點間資料的不一致問題。
3PC三階段提交協議
2PC的改進版本,其在兩階段提交的基礎上增加了CanCommit階段,並引入了超時機制。一旦事務參與者遲遲沒有收到協調者的Commit請求,就會自動進行本地commit,這樣相對有效地解決了協調者單點故障的問題。。
階段1:CanCommit
1、協調者向所有參與者發出包含事務內容的CanCommit請求,詢問是否可以提交事務,並等待所有參與者答覆。
2、參與者收到CanCommit請求後,如果認為可以執行事務操作,則反饋YES並進入預備狀態,否則反饋NO。
階段2:PreCommit
事務預提交:(所有參與者均反饋YES時)
1、協調者向所有參與者發出PreCommit請求,進入準備階段。
2、參與者收到PreCommit請求後,執行事務操作,將Undo和Redo資訊記入事務日誌中(但不提交事務)。
3、各參與者向協調者反饋Ack響應或No響應,並等待最終指令。
中斷事務:(任何一個參與者反饋NO,或者等待超時後協調者尚無法收到所有參與者的反饋時)
1、協調者向所有參與者發出abort請求。
2、無論收到協調者發出的abort請求,或者在等待協調者請求過程中出現超時,參與者均會中斷事務。
階段3:do Commit
提交事務:(所有參與者均反饋Ack響應時)
1、如果協調者處於工作狀態,則向所有參與者發出do Commit請求。
2、參與者收到do Commit請求後,會正式執行事務提交,並釋放整個事務期間佔用的資源。
3、各參與者向協調者反饋Ack完成的訊息。
4、協調者收到所有參與者反饋的Ack訊息後,即完成事務提交。
中斷事務:(任何一個參與者反饋NO,或者等待超時後協調者尚無法收到所有參與者的反饋時)
1、如果協調者處於工作狀態,向所有參與者發出abort請求。
2、參與者使用階段1中的Undo資訊執行回滾操作,並釋放整個事務期間佔用的資源。
3、各參與者向協調者反饋Ack完成的訊息。
4、協調者收到所有參與者反饋的Ack訊息後,即完成事務中斷。
TCC補償事務協議
TCC 將事務提交分為 Try - Confirm - Cancel 3個操作。
1、Try:預留業務資源/資料效驗
2、Confirm:確認執行業務操作
3、Cancel:取消執行業務操作
TCC事務處理流程和 2PC 二階段提交類似,不過 2PC通常都是在跨庫的DB層面,而TCC本質就是一個應用層面的2PC。
三、分散式事務使用場景
1、基於訊息實現的事務
基於訊息實現的事務適用於分散式事務的提交或回滾只取決於事務發起方的業務需求,其他資料來源的資料變更跟隨發起方進行的業務場景。
2、補償實現的事務
基於訊息實現的事務並不能解決所有的業務場景,例如以下場景:某筆訂單完成時,同時扣掉使用者的現金。
這裡事務發起方是管理訂單庫的服務,但對整個事務是否提交併不能只由訂單服務決定,因為還要確保使用者有足夠的錢,才能完成這筆交易,而這個資訊在管理現金的服務裡。這裡我們可以引入基於補償實現的事務,其流程如下:
- 建立訂單資料,但暫不提交本地事務
- 訂單服務傳送遠端呼叫到現金服務,以扣除對應的金額
- 上述步驟成功後提交訂單庫的事務
以上這個是正常成功的流程,異常流程需要回滾的話,將額外傳送遠端呼叫到現金服務以加上之前扣掉的金額。
以上流程比基於訊息佇列實現的事務的流程要複雜,同時開發的工作量也更多:
- 編寫訂單服務裡建立訂單的邏輯
- 編寫現金服務里扣錢的邏輯
- 編寫現金服務裡補償返還的邏輯
可以看到,該事務流程相對於基於訊息實現的分散式事務更為複雜,需要額外開發相關的業務回滾方法,也失去了服務間流量削峰填谷的功能。但其僅僅只比基於訊息的事務複雜多一點,若不能使用基於訊息佇列的最終一致性事務,那麼可以優先考慮使用基於補償的事務形態。
3、2PC事務
其適用於參與者較少,單個本地事務執行時間較少,並且參與者自身可用性很高的場景,否則,其很可能導致效能下降嚴重。