背景
分布式事務是企業(yè)集成中的一個技術(shù)難點,也是每一個分布式系統(tǒng)架構(gòu)中都會涉及到的一個東西,特別是在微服務架構(gòu)中,幾乎可以說是無法避免。
ACID
指數(shù)據(jù)庫事務正確執(zhí)行的四個基本要素:
- 原子性(Atomicity)
- 一致性(Consistency)
- 隔離性(Isolation)
- 持久性(Durability)
CAP
CAP原則又稱CAP定理,指的是在一個分布式系統(tǒng)中,一致性(Consistency)、可用性(Availability)、分區(qū)容忍性(Partition tolerance)。CAP 原則指的是,這三個要素最多只能同時實現(xiàn)兩點,不可能三者兼顧。
- 一致性:在分布式系統(tǒng)中的所有數(shù)據(jù)備份,在同一時刻是否同樣的值。
- 可用性:在集群中一部分節(jié)點故障后,集群整體是否還能響應客戶端的讀寫請求。
- 分區(qū)容忍性:以實際效果而言,分區(qū)相當于對通信的時限要求。系統(tǒng)如果不能在時限內(nèi)達成數(shù)據(jù)一致性,就意味著發(fā)生了分區(qū)的情況,必須就當前操作在C和A之間做出選擇。
BASE理論
BASE理論是對CAP中的一致性和可用性進行一個權(quán)衡的結(jié)果,理論的核心思想就是:我們無法做到強一致,但每個應用都可以根據(jù)自身的業(yè)務特點,采用適當?shù)姆绞絹硎瓜到y(tǒng)達到最終一致性。
- Basically Available(基本可用)
- Soft state(軟狀態(tài))
- Eventually consistent(最終一致性)
解決方案
01 兩階段提交(2PC)
兩階段提交2PC是分布式事務中最強大的事務類型之一,兩段提交就是分兩個階段提交,第一階段詢問各個事務數(shù)據(jù)源是否準備好,第二階段才真正將數(shù)據(jù)提交給事務數(shù)據(jù)源。
為了保證該事務可以滿足ACID,就要引入一個協(xié)調(diào)者(Cooradinator)。其他的節(jié)點被稱為參與者(Participant)。協(xié)調(diào)者負責調(diào)度參與者的行為,并最終決定這些參與者是否要把事務進行提交。處理流程如下:
階段一
a) 協(xié)調(diào)者向所有參與者發(fā)送事務內(nèi)容,詢問是否可以提交事務,并等待答復。
b) 各參與者執(zhí)行事務操作,將 undo 和 redo 信息記入事務日志中(但不提交事務)。
c) 如參與者執(zhí)行成功,給協(xié)調(diào)者反饋 yes,否則反饋 no。
階段二
如果協(xié)調(diào)者收到了參與者的失敗消息或者超時,直接給每個參與者發(fā)送回滾(rollback)消息;否則,發(fā)送提交(commit)消息。兩種情況處理如下:
情況1:當所有參與者均反饋 yes,提交事務
a) 協(xié)調(diào)者向所有參與者發(fā)出正式提交事務的請求(即 commit 請求)。
b) 參與者執(zhí)行 commit 請求,并釋放整個事務期間占用的資源。
c) 各參與者向協(xié)調(diào)者反饋 ack(應答)完成的消息。
d) 協(xié)調(diào)者收到所有參與者反饋的 ack 消息后,即完成事務提交。
情況2:當有一個參與者反饋 no,回滾事務
a) 協(xié)調(diào)者向所有參與者發(fā)出回滾請求(即 rollback 請求)。
b) 參與者使用階段 1 中的 undo 信息執(zhí)行回滾操作,并釋放整個事務期間占用的資源。
c) 各參與者向協(xié)調(diào)者反饋 ack 完成的消息。
d) 協(xié)調(diào)者收到所有參與者反饋的 ack 消息后,即完成事務。
問題
1) 性能問題:所有參與者在事務提交階段處于同步阻塞狀態(tài),占用系統(tǒng)資源,容易導致性能瓶頸。
2) 可靠性問題:如果協(xié)調(diào)者存在單點故障問題,或出現(xiàn)故障,提供者將一直處于鎖定狀態(tài)。
3) 數(shù)據(jù)一致性問題:在階段 2 中,如果出現(xiàn)協(xié)調(diào)者和參與者都掛了的情況,有可能導致數(shù)據(jù)不一致。
優(yōu)點:盡量保證了數(shù)據(jù)的強一致,適合對數(shù)據(jù)強一致要求很高的關(guān)鍵領(lǐng)域。(其實也不能100%保證強一致)。
缺點:實現(xiàn)復雜,犧牲了可用性,對性能影響較大,不適合高并發(fā)高性能場景。
02 三階段提交(3PC)
三階段提交是在二階段提交上的改進版本,3PC最關(guān)鍵要解決的就是協(xié)調(diào)者和參與者同時掛掉的問題,所以3PC把2PC的準備階段再次一分為二,這樣三階段提交。處理流程如下:
階段一
a) 協(xié)調(diào)者向所有參與者發(fā)出包含事務內(nèi)容的 canCommit 請求,詢問是否可以提交事務,并等待所有參與者答復。
b) 參與者收到 canCommit 請求后,如果認為可以執(zhí)行事務操作,則反饋 yes 并進入預備狀態(tài),否則反饋 no。
階段二
協(xié)調(diào)者根據(jù)參與者響應情況,有以下兩種可能。
情況1:所有參與者均反饋 yes,協(xié)調(diào)者預執(zhí)行事務
a) 協(xié)調(diào)者向所有參與者發(fā)出 preCommit 請求,進入準備階段。
b) 參與者收到 preCommit 請求后,執(zhí)行事務操作,將 undo 和 redo 信息記入事務日志中(但不提交事務)。
c) 各參與者向協(xié)調(diào)者反饋 ack 響應或 no 響應,并等待最終指令。
情況2:只要有一個參與者反饋 no,或者等待超時后協(xié)調(diào)者尚無法收到所有提供者的反饋,即中斷事務
a) 協(xié)調(diào)者向所有參與者發(fā)出 abort 請求。
b) 無論收到協(xié)調(diào)者發(fā)出的 abort 請求,或者在等待協(xié)調(diào)者請求過程中出現(xiàn)超時,參與者均會中斷事務。
階段三
該階段進行真正的事務提交,也可以分為以下兩種情況。
情況 1:所有參與者均反饋 ack 響應,執(zhí)行真正的事務提交
a) 如果協(xié)調(diào)者處于工作狀態(tài),則向所有參與者發(fā)出 do Commit 請求。
b) 參與者收到 do Commit 請求后,會正式執(zhí)行事務提交,并釋放整個事務期間占用的資源。
c) 各參與者向協(xié)調(diào)者反饋 ack 完成的消息。
d) 協(xié)調(diào)者收到所有參與者反饋的 ack 消息后,即完成事務提交。
情況2:只要有一個參與者反饋 no,或者等待超時后協(xié)調(diào)組尚無法收到所有提供者的反饋,即回滾事務。
a) 如果協(xié)調(diào)者處于工作狀態(tài),向所有參與者發(fā)出 rollback 請求。
b) 參與者使用階段 1 中的 undo 信息執(zhí)行回滾操作,并釋放整個事務期間占用的資源。
c) 各參與者向協(xié)調(diào)組反饋 ack 完成的消息。
d) 協(xié)調(diào)組收到所有參與者反饋的 ack 消息后,即完成事務回滾。
優(yōu)點:相比二階段提交,三階段提交降低了阻塞范圍,在等待超時后協(xié)調(diào)者或參與者會中斷事務。避免了協(xié)調(diào)者單點問題。階段 3 中協(xié)調(diào)者出現(xiàn)問題時,參與者會繼續(xù)提交事務。
缺點:數(shù)據(jù)不一致問題依然存在,當在參與者收到 preCommit 請求后等待 do commite 指令時,此時如果協(xié)調(diào)者請求中斷事務,而協(xié)調(diào)者無法與參與者正常通信,會導致參與者繼續(xù)提交事務,造成數(shù)據(jù)不一致。
03 補償事務(TCC)
TCC 是服務化的二階段編程模型,采用的補償機制:
條件:
需要實現(xiàn)確認和補償邏輯
需要支持冪等
處理流程:
a) Try 階段主要是對業(yè)務系統(tǒng)做檢測及資源預留。
這個階段主要完成:
完成所有業(yè)務檢查( 一致性 ) 。
預留必須業(yè)務資源( 準隔離性 ) 。
Try 嘗試執(zhí)行業(yè)務。
b) Confirm 階段主要是對業(yè)務系統(tǒng)做確認提交。
Try階段執(zhí)行成功并開始執(zhí)行 Confirm階段時,默認 Confirm階段是不會出錯的。即:只要Try成功,Confirm一定成功。
c) Cancel 階段主要是在業(yè)務執(zhí)行錯誤,需要回滾的狀態(tài)下執(zhí)行的業(yè)務取消,預留資源釋放。
優(yōu)點:
性能提升:具體業(yè)務來實現(xiàn)控制資源鎖的粒度變小,不會鎖定整個資源。
數(shù)據(jù)最終一致性:基于 Confirm 和 Cancel 的冪等性,保證事務最終完成確認或者取消,保證數(shù)據(jù)的一致性。
可靠性:解決了 XA 協(xié)議的協(xié)調(diào)者單點故障問題,由主業(yè)務方發(fā)起并控制整個業(yè)務活動,業(yè)務活動管理器也變成多點,引入集群。
缺點:TCC 的 Try、Confirm 和 Cancel 操作功能要按具體業(yè)務來實現(xiàn),業(yè)務耦合度較高,提高了開發(fā)成本。
04 本地消息表(消息隊列)
其核心思想是將分布式事務拆分成本地事務進行處理。
方案通過在消費者額外新建事務消息表,消費者處理業(yè)務和記錄事務消息在本地事務中完成,輪詢事務消息表的數(shù)據(jù)發(fā)送事務消息,提供者基于消息中間件消費事務消息表中的事務。
條件:
服務消費者需要創(chuàng)建一張消息表,用來記錄消息狀態(tài)。
服務消費者和提供者需要支持冪等。
需要補償邏輯。
每個節(jié)點上起定時線程,檢查未處理完成或發(fā)出失敗的消息,重新發(fā)出消息,即重試機制和冪等性機制。
處理流程:
1. 服務消費者把業(yè)務數(shù)據(jù)和消息一同提交,發(fā)起事務。
2. 消息經(jīng)過MQ發(fā)送到服務提供方,服務消費者等待處理結(jié)果。
3. 服務提供方接收消息,完成業(yè)務邏輯并通知消費者已處理的消息。
容錯處理情況如下:
當步驟1處理出錯,事務回滾,相當于什么都沒有發(fā)生。
當步驟2、3處理出錯,由于消息保存在消費者表中,可以重新發(fā)送到MQ進行重試。
如果步驟3處理出錯,且是業(yè)務上的失敗,服務提供者發(fā)送消息通知消費者事務失敗,且此時變?yōu)橄M者發(fā)起回滾事務進行回滾邏輯。
優(yōu)點:從應用設計開發(fā)的角度實現(xiàn)了消息數(shù)據(jù)的可靠性,消息數(shù)據(jù)的可靠性不依賴于消息中間件,弱化了對 MQ 中間件特性的依賴。
缺點:與具體的業(yè)務場景綁定,耦合性強,不可公用。消息數(shù)據(jù)與業(yè)務數(shù)據(jù)同庫,占用業(yè)務系統(tǒng)資源。業(yè)務系統(tǒng)在使用關(guān)系型數(shù)據(jù)庫的情況下,消息服務性能會受到關(guān)系型數(shù)據(jù)庫并發(fā)性能的局限。
MQ事務消息(最終一致性)
支持事務消息的MQ,其支持事務消息的方式采用類似于二階段提交。
基于 MQ 的分布式事務方案其實是對本地消息表的封裝,將本地消息表基于 MQ 內(nèi)部,其他方面的協(xié)議基本與本地消息表一致。
條件:
a) 需要補償邏輯
b) 業(yè)務處理邏輯需要冪等
處理流程:
c) 消費者向MQ發(fā)送half消息。
d) MQ Server將消息持久化后,向發(fā)送方ack確認消息發(fā)送成功。
e) 消費者開始執(zhí)行事務邏輯。
f) 消費者根據(jù)本地事務執(zhí)行結(jié)果向MQ Server提交二次確認或者回滾。
g) MQ Server收到commit狀態(tài)則將half消息標記可投遞狀態(tài)。
h) 服務提供者收到該消息,執(zhí)行本地業(yè)務邏輯。返回處理結(jié)果。
優(yōu)點:
消息數(shù)據(jù)獨立存儲,降低業(yè)務系統(tǒng)與消息系統(tǒng)之間的耦合。
吞吐量優(yōu)于本地消息表方案。
缺點:
一次消息發(fā)送需要兩次網(wǎng)絡請求(half消息 + commit/rollback)。
需要實現(xiàn)消息回查接口。
05 Sagas事務模型(最終一致性)
Saga模式是一種分布式異步事務,一種最終一致性事務,是一種柔性事務,有兩種不同的方式來實現(xiàn)saga事務,最流行的兩種方式是:
一、 事件/編排Choreography:沒有中央?yún)f(xié)調(diào)器(沒有單點風險)時,每個服務產(chǎn)生并聆聽其他服務的事件,并決定是否應采取行動。
該實現(xiàn)第一個服務執(zhí)行一個事務,然后發(fā)布一個事件。該事件被一個或多個服務進行監(jiān)聽,這些服務再執(zhí)行本地事務并發(fā)布(或不發(fā)布)新的事件,當最后一個服務執(zhí)行本地事務并且不發(fā)布任何事件時,意味著分布式事務結(jié)束,或者它發(fā)布的事件沒有被任何Saga參與者聽到都意味著事務結(jié)束。
處理流程:
訂單服務保存新訂單,將狀態(tài)設置為pengding掛起狀態(tài),并發(fā)布名為ORDER_CREATED_EVENT的事件。
支付服務監(jiān)聽ORDER_CREATED_EVENT,并公布事件BILLED_ORDER_EVENT。
庫存服務監(jiān)聽BILLED_ORDER_EVENT,更新庫存,并發(fā)布ORDER_PREPARED_EVENT。
貨運服務監(jiān)聽ORDER_PREPARED_EVENT,然后交付產(chǎn)品。最后,它發(fā)布ORDER_DELIVERED_EVENT。
最后,訂單服務偵聽ORDER_DELIVERED_EVENT并設置訂單的狀態(tài)為concluded完成。
假設庫存服務在事務過程中失敗了。進行回滾:
庫存服務產(chǎn)生PRODUCT_OUT_OF_STOCK_EVENT
訂購服務和支付服務會監(jiān)聽到上面庫存服務的這一事件:
①支付服務會退款給客戶。
②訂單服務將訂單狀態(tài)設置為失敗。
優(yōu)點:事件/編排是實現(xiàn)Saga模式的自然方式; 它很簡單,容易理解,不需要太多的努力來構(gòu)建,所有參與者都是松散耦合的,因為他們彼此之間沒有直接的耦合。如果您的事務涉及2至4個步驟,則可能是非常合適的。
二、 命令/協(xié)調(diào)orchestrator:中央?yún)f(xié)調(diào)器負責集中處理事件的決策和業(yè)務邏輯排序。
saga協(xié)調(diào)器orchestrator以命令/回復的方式與每項服務進行通信,告訴他們應該執(zhí)行哪些操作。
訂單服務保存pending狀態(tài),并要求訂單Saga協(xié)調(diào)器(簡稱OSO)開始啟動訂單事務。
OSO向收款服務發(fā)送執(zhí)行收款命令,收款服務回復Payment Executed消息。
OSO向庫存服務發(fā)送準備訂單命令,庫存服務將回復OrderPrepared消息。
OSO向貨運服務發(fā)送訂單發(fā)貨命令,貨運服務將回復Order Delivered消息。
OSO訂單Saga協(xié)調(diào)器必須事先知道執(zhí)行“創(chuàng)建訂單”事務所需的流程(通過讀取BPM業(yè)務流程XML配置獲得)。如果有任何失敗,它還負責通過向每個參與者發(fā)送命令來撤銷之前的操作來協(xié)調(diào)分布式的回滾。當你有一個中央?yún)f(xié)調(diào)器協(xié)調(diào)一切時,回滾要容易得多,因為協(xié)調(diào)器默認是執(zhí)行正向流程,回滾時只要執(zhí)行反向流程即可。
優(yōu)點:
避免服務之間的循環(huán)依賴關(guān)系,因為saga協(xié)調(diào)器會調(diào)用saga參與者,但參與者不會調(diào)用協(xié)調(diào)器。
集中分布式事務的編排。
只需要執(zhí)行命令/回復(其實回復消息也是一種事件消息),降低參與者的復雜性。
在添加新步驟時,事務復雜性保持線性,回滾更容易管理。
如果在第一筆交易還沒有執(zhí)行完,想改變有第二筆事務的目標對象,則可以輕松地將其暫停在協(xié)調(diào)器上,直到第一筆交易結(jié)束。