天天看點

了解這篇分布式事務文章,可以做到吊打面試官

大家好,我是沐子。

不知道你是否遇到過這樣的情況,去小賣鋪買東西,付了錢,但是店主因為處理了一些其他事,居然忘記你付了錢,又叫你重新付。又或者在網上購物明明已經扣款,但是卻告訴我沒有發生交易。這一系列情況都是因為沒有事務導緻的,這說明了事務在生活中的一些重要性。我們無論在工作或者在面試中,都會碰到分布式事務相關問題。近幾年我在面試開發工程師們,都發現大都存在一個共同的問題,對分布式事務一知半解,且也僅僅停留在初級的使用層面,基于上述情況,小篇給你徹底講清楚分布式事務,在面試時争取可以吊打面試官。

了解這篇分布式事務文章,可以做到吊打面試官

一、 什麼是分布式事務

在分布式電商網站中,訂單和庫存分為兩個子系統對外提供服務,子系統間使用rpc進行通信。使用者對商品進行下單時,需要在訂單庫中建立一條訂單資料,同時需要在庫存庫中修改目前商品的剩餘庫存數量,兩步操作一個添加,一個修改,我們一定要保證這兩步操作一定同時操作成功或失敗,否則業務就會出現問題。但由于跨庫跨機器,mysql的本地事務不能再保證訂單庫和庫存庫的資料一緻性,這時候就需要分布式事務來保證。

了解這篇分布式事務文章,可以做到吊打面試官

對于分布式系統而言,要保證分布式系統中的資料一緻性就需要一種方案,可以保證資料在子系統中始終保持一緻,避免業務出現問題,這種實作方案就叫做分布式事務,要麼一起成功,要麼一起失敗,必須是一個整體性的事務。

二、 分布式事務理論基礎

在講解分布式事務具體方案時,我們首先要熟悉CAP理論和BASE理論,這樣才能更好的掌握分布式事務。

1. CAP理論

CAP定理是由加州大學伯克利分校Eric Brewer教授提出來的,他指出WEB服務無法同時滿足一下3個屬性:

一緻性(Consistency):用戶端知道一系列的操作都會同時發生(生效)

可用性(Availability): 每個操作都必須以可預期的響應結束

分區容錯性(Partition tolerance):即使出現單個元件無法可用,操作依然可以完成

對于多數大型網際網路應用的場景,主機衆多、部署分散,而且現在的叢集規模越來越大,是以節點故障、網絡故障是常态,而且要保證服務可用性達到 N 個 9,即保證 P 和 A,舍棄C(退而求其次保證最終一緻性)。雖然某些地方會影響客戶體驗,但沒達到造成使用者流程的嚴重程度。

對于涉及到錢财這樣不能有一絲讓步的場景,C必須保證。網絡發生故障甯可停止服務,這是保證 CA,舍棄 P。貌似這幾年國内銀行業發生了不下10 起事故,但影響面不大,報道也不多,廣大群衆知道的少。還有一種是保證 CP,舍棄 A。例如網絡故障是隻讀不寫。

由于分布式理論不是本篇的重點,這裡隻做一些簡單概述,如果你想更深入的了解CAP,可以點選我的另外一篇文章CAP理論。

2. BASE理論

BASE理論是Basically Available(基本可用)、Soft state(軟狀态)和Eventually consistent(最終一緻性)三個短語的簡寫,BASE是對CAP中一緻性和可用性權衡的結果,其核心思想是即使無法做到強一緻性,但每個應用都可以根據自身的業務特點,采用适當的方式使系統達到最終一緻性。

➢基本可用:

基本可用是指分布式系統在出現不可預知故障的時候,允許損失部分可用性。這不等價于系統不可用。

響應時間上的損失:正常情況下,一個線上搜尋引擎需要在0.5秒内傳回給使用者相應的查詢結果,但由于出現故障,比如系統部分機房發生斷點或斷網故障,查詢結果的享用時間增加到1~2秒。

功能上的損失:正常情況下,在一個電子商務網站上進行購物,消費者幾乎能夠順利的完成每一筆訂單,但是在一些節日大促購物高峰的時候,由于消費者的購物行為激增,為了保護系統的穩定性,部分消費者可能會被引導到一個降級頁面。

➢弱狀态:

弱狀态也成為軟狀态,和硬狀态相對,是指允許系統中的資料存在中間狀态,并認為該中間狀态的存在不會影響系統的整體可用性,即允許系統在不同節點的資料副本之間進行資料同步的過程存在延遲。

➢最終一緻性:

最終一緻性強調的是系統所有的資料副本,在經過一段時間的同步後,最終能夠達到一個一緻的狀态。是以,最終一緻性的本質是需要系統保證最終資料能能夠達到一緻,而不需要實時保證系統資料的強一緻性。

BASE理論面向的是大型高可用可擴充的分布式系統,和傳統的事物ACID特性是相反的。它完全不同于ACID的強一緻性模型,是對CAP理論的延伸和補充,主要是對AP的補充,是通過犧牲強一緻性來獲得可用性,并允許資料在一段時間内是不一緻的,但最終達到一緻狀态。但同時,在實際的分布式場景中,不同業務單元群組件對資料一緻性的要求是不同的,是以在具體的分布式系統架構設計過程中,ACID特性和BASE理論往往又會結合在一起。

三、 分布式事務解決方案

分布式事務實作方案從類型上分為剛性事務和柔型事務:

剛性事務滿足CAP的CP理論

柔性事務滿足BASE理論(基本可用,最終一緻)

1. 剛性事務

剛性事務指的是,要使分布式事務,達到像本地式事務一樣,具備資料強一緻性,從CAP來看,就是說,要達到CP狀态。剛性事務通常無業務改造,強一緻性,原生支援復原/隔離性,低并發,适合短事務。由于同步阻塞,處理效率低,不适合大型網站分布式場景。

1.1 事務協定

XA是一個規範、協定,它隻是定義了一系列的接口,隻是目前大多數實作XA的都是資料庫或者MQ,是以提起XA往往多指基于資源層的底層分布式事務解決方案。而XA模型主要使用了兩段送出(2PC - Two-Phase-Commit)來保證分布式事務的完整性。其中兩階段送出(2PC)協定是XA規範定義的資料一緻性協定,三階段送出(3PC)協定對 2PC協定的一種擴充。

1.1.1 兩階段送出(2PC)

兩階段送出又稱2PC,2PC是一個非常經典的強一緻、中心化的原子送出協定。

這裡所說的中心化是指協定中有兩類節點:一個是中心化協調者節點(coordinator)和N個參與者節點(partcipant)。

兩個階段:第一階段:投票階段 和第二階段:送出/執行階段。

舉例 訂單服務A,需要調用 支付服務B 去支付,支付成功則處理購物訂單為待發貨狀态,否則就需要将購物訂單處理為失敗狀态。

了解這篇分布式事務文章,可以做到吊打面試官

第一階段主要分為3步

1)事務詢問

協調者 向所有的 參與者 發送事務預處理請求,稱之為Prepare,并開始等待各 參與者 的響應。

2)執行本地事務

各個 參與者 節點執行本地事務操作,但在執行完成後并不會真正送出資料庫本地事務,而是先向 協調者 報告說:“我這邊可以處理了/我這邊不能處理”。.

3)各參與者向協調者回報事務詢問的響應

如果 參與者 成功執行了事務操作,那麼就回報給協調者 Yes 響應,表示事務可以執行,如果沒有 參與者 成功執行事務,那麼就回報給協調者 No 響應,表示事務不可以執行。

第一階段執行完後,會有兩種可能。1、所有都傳回Yes. 2、有一個或者多個傳回No。

成功條件:所有參與者都傳回Yes。

了解這篇分布式事務文章,可以做到吊打面試官

第二階段主要分為兩步

1)所有的參與者回報給協調者的資訊都是Yes,那麼就會執行事務送出

協調者 向 所有參與者 節點發出Commit請求.

2)事務送出

參與者 收到Commit請求之後,就會正式執行本地事務Commit操作,并在完成送出之後釋放整個事務執行期間占用的事務資源。

異常條件:任何一個 參與者 向 協調者 回報了 No 響應,或者等待逾時之後,協調者尚未收到所有參與者的回報響應。

了解這篇分布式事務文章,可以做到吊打面試官

異常流程第二階段也分為兩步

1)發送復原請求

協調者 向所有參與者節點發出 RoollBack 請求.

2)事務復原

參與者 接收到RoollBack請求後,會復原本地事務。

通過上面的示範,很容易想到2pc所帶來的缺陷

1)性能問題

無論是在第一階段的過程中,還是在第二階段,所有的參與者資源和協調者資源都是被鎖住的,隻有當所有節點準備完畢,事務 協調者 才會通知進行全局送出,

參與者 進行本地事務送出後才會釋放資源。這樣的過程會比較漫長,對性能影響比較大。

2)丢失消息導緻的資料不一緻問題

資料不一緻。當執行事務送出過程中,如果協調者向所有參與者發送Commit請求後,發生局部網絡異常或者協調者在尚未發送完Commit請求,即出現崩潰,最終導緻隻有部分參與者收到、執行請求。于是整個系統将會出現資料不一緻的情形;

3)單節點故障由于協調者的重要性,一旦 協調者 發生故障。參與者 會一直阻塞下去。尤其在第二階段,協調者 發生故障,那麼所有的 參與者 還都處于鎖定事務資源的狀态中,而無法繼續完成事務操作。(雖然協調者挂掉,可以重新選舉一個協調者,但是無法解決因為協調者當機導緻的參與者處于阻塞狀态的問題)

2PC出現單點問題的三種情況

(1)協調者正常,參與者當機

由于 協調者 無法收集到所有 參與者 的回報,會陷入阻塞情況。

解決方案:引入逾時機制,如果協調者在超過指定的時間還沒有收到參與者的回報,事務就失敗,向所有節點發送終止事務請求。

(2)協調者當機,參與者正常

無論處于哪個階段,由于協調者當機,無法發送送出請求,所有處于執行了操作但是未送出狀态的參與者都會陷入阻塞情況.

解決方案:引入協調者備份,同時協調者需記錄記錄檔.當檢測到協調者當機一段時間後,協調者備份取代協調者,并讀取記錄檔,向所有參與者詢問狀态。

(3)協調者和參與者都當機

1)發生在第一階段:因為第一階段,所有參與者都沒有真正執行commit,是以隻需重新在剩餘的參與者中重新選出一個協調者,新的協調者在重新執行第一階段和第二階段就可以了。

2)發生在第二階段 并且 挂了的參與者在挂掉之前沒有收到協調者的指令。也就是上面的第4步挂了,這是可能協調者還沒有發送第4步就挂了。這種情形下,新的協調者重新執行第一階段和第二階段操作。

3)發生在第二階段 并且 有部分參與者已經執行完commit操作。就好比這裡訂單服務A和支付服務B都收到協發送的commit資訊,開始真正執行本地事務commit,但突發情況,Acommit成功,B确挂了。這個時候目前來講資料是不一緻的。雖然這個時候可以再通過手段讓他和協調者通信,再想辦法把資料搞成一緻的,但是,這段時間内他的資料狀态已經是不一緻的了!2PC 無法解決這個問題。

2PC 方案比較适合單體應用裡,因為嚴重依賴于資料庫層面來搞定複雜的事務,效率很低,絕對不适合高并發的場景。

1.1.2 三階段送出(3PC)

三階段送出協定(3PC)主要是為了解決兩階段送出協定的阻塞問題,2pc存在的問題是當協作者崩潰時,參與者不能做出最後的選擇。是以參與者可能在協作者恢複之前保持阻塞。三階段送出(Three-phase commit),是二階段送出(2PC)的改進版本。

與兩階段送出不同的是,三階段送出有兩個改動點。

1、引入逾時機制。同時在協調者和參與者中都引入逾時機制。

2、在第一階段和第二階段中插入一個準備階段。保證了在最後送出階段之前各參與節點的狀态是一緻的。

也就是說,除了引入逾時機制之外,3PC把2PC的準備階段再次一分為二,這樣三階段送出就有CanCommit、PreCommit、DoCommit三個階段。

之前2PC的一階段是本地事務執行結束後,最後不Commit,等其它服務都執行結束并傳回Yes,由協調者發生commit才真正執行commit。而這裡的CanCommit指的是 嘗試擷取資料庫鎖 如果可以,就傳回Yes。

了解這篇分布式事務文章,可以做到吊打面試官

這階段主要分為2步

事務詢問 協調者 向 參與者 發送CanCommit請求。詢問是否可以執行事務送出操作。然後開始等待 參與者 的響應。

響應回報 參與者 接到CanCommit請求之後,正常情況下,如果其自身認為可以順利執行事務,則傳回Yes響應,并進入預備狀态。否則回報No

在階段一中,如果所有的參與者都傳回Yes的話,那麼就會進入PreCommit階段進行事務預送出。這裡的PreCommit階段 跟上面的第一階段是差不多的,隻不過這裡 協調者和參與者都引入了逾時機制 (2PC中隻有協調者可以逾時,參與者沒有逾時機制)。

這裡跟2pc的階段二是差不多,這裡就不過多講解了。

相對于2PC,3PC主要解決的單點故障問題,并減少阻塞, 因為一旦參與者無法及時收到來自協調者的資訊之後,他會預設執行commit。而不會一直持有事務資源并處于阻塞狀态。但是這種機制也會導緻資料一緻性問題,因為,由于網絡原因,協調者發送的abort響應沒有及時被參與者接收到,那麼參與者在等待逾時之後執行了commit操作。這樣就和其他接到abort指令并執行復原的參與者之間存在資料不一緻的情況。

基于LCN架構解決分布式事務

TX-LCN定位于一款事務協調性架構,架構其本身并不操作事務,而是基于對事務的協調進而達到事務一緻性的效果,核心采用3PC。(LCN不生産事務,它隻是事務的搬運工)

了解這篇分布式事務文章,可以做到吊打面試官

  2)當請求的發起方(調用方)進入接口業務之前,會通過AOP技術進到@LcnTransaction注解中去LCN協調者那邊生成注冊一個全局的事務組Id(groupId)。

  3)當發起方(調用方)通過rpc調用參與方(被調用方)的時候,lcn重寫了Feign用戶端,會從ThreadLocal中拿到該事務組Id(groupId),并将該事務組Id設定到請求頭中。

  4)參與方(被調用方)在請求頭中擷取到了這個groupId的時候,lcn會辨別該服務為參與方并加入到該事務組,并會被lcn代理資料源,當該服務業務邏輯執行完成後,進行資料源的假關閉,并不會真正的送出或復原目前服務的事務。

  5)當發起方執行完全部業務邏輯的時候,如果無異常會告知lcn協調者,lcn協調者再分别告訴該請求鍊上的所有參與方可以送出了,再進行真正的送出。若發起方調用完參與方後報錯了,也會告知lcn協調者,lcn協調者再告知所有的參與方進行真正的復原操作,這樣就解決了分布式事務的問題。

由于LCN核心采用3PC協定,它的優點是能夠保證資料的強一緻性;但同時也具有3PC協定的一些缺點,即可能會造成死鎖的現象,比如,訂單服務調用派單服務成功以後,訂單服務還沒執行完畢就當機,此時,TxManage并沒有收到通知,派單服務的事務也不能順利進行,導緻死鎖;其次lcn的性能不是特别強大。但由于代碼侵入性低,實作分布式事務成本低,如果項目對性能要求不是特别高時,可以考慮使用該架構。

2. 柔性事務

在電商領域等網際網路場景下,剛性事務在資料庫性能和處理能力上都暴露出了瓶頸。其中柔性事務有兩個特性:基本可用和柔性狀态。

基本可用是指分布式系統出現故障的時候允許損失一部分的可用性。

柔性狀态是指允許系統存在中間狀态,這個中間狀态不會影響系統整體的可用性,比如資料庫讀寫分離的主從同步延遲等。柔性事務的一緻性指的是最終一緻性。

柔性事務指的是,不要求強一緻性,而是要求最終一緻性,允許有中間狀态,也就是Base理論,換句話說,就是AP狀态。與剛性事務相比,柔性事務的特點為:有業務改造,最終一緻性,實作補償接口,實作資源鎖定接口,高并發,适合長事務。

柔性事務主要分為通知型(MQ事務消息、最大努力通知型)和補償型(TCC)。

2.1 通知型

通知型事務的主流實作是通過MQ(消息隊列)來通知其他事務參與者自己事務的執行狀态,引入MQ元件,有效的将事務參與者進行解耦,各參與者都可以異步執行,是以通知型事務又被稱為異步事務。通知型事務主要适用于那些需要異步更新資料,并且對資料的實時性要求較低的場景,主要包含:異步確定型事務和最大努力通知事務兩種。

異步確定型事務:主要适用于内部系統的資料最終一緻性保障,因為内部相對比較可控,如訂單和購物車、收貨與清算、支付與結算等等場景;

最大努力通知:主要用于外部系統,因為外部的網絡環境更加複雜和不可信,是以隻能盡最大努力去通知實作資料最終一緻性,比如充值平台與營運商、支付對接等等跨網絡系統級别對接;

了解這篇分布式事務文章,可以做到吊打面試官

2.1.1 異步確定型事務

指将一系列同步的事務操作修改為基于消息隊列異步執行的操作,來避免分布式事務中同步阻塞帶來的資料操作性能的下降,其中常見的方案:MQ事務消息方案和本地消息表方案。

a. MQ事務消息方案

基于MQ的事務消息方案主要依靠MQ的半消息機制來實作投遞消息和參與者自身本地事務的一緻性保障。半消息機制實作原理其實借鑒的2PC的思路,是二階段送出的廣義拓展。

半消息:在原有隊列消息執行後的邏輯,如果後面的本地邏輯出錯,則不發送該消息,如果通過則告知MQ發送;

流程

了解這篇分布式事務文章,可以做到吊打面試官

1. 事務發起方首先發送半消息到MQ;

2. MQ通知發送方消息發送成功;

3. 在發送半消息成功後執行本地事務;

4. 根據本地事務執行結果傳回commit或者是rollback;

5. 如果消息是rollback, MQ将丢棄該消息不投遞;如果是commit,MQ将會消息發送給消息訂閱方;

6. 訂閱方根據消息執行本地事務;

7. 訂閱方執行本地事務成功後再從MQ中将該消息标記為已消費;

8. 如果執行本地事務過程中,執行端挂掉,或者逾時,MQ伺服器端将不停的詢問producer來擷取事務狀态;

9. Consumer端的消費成功機制有MQ保證;

基于阿裡 RocketMQ實作MQ異步確定型事務

有一些第三方的MQ是支援事務消息的,這些消息隊列,支援半消息機制,比如RocketMQ,ActiveMQ。但是有一些常用的MQ也不支援事務消息,比如 RabbitMQ 和 Kafka 都不支援。

以阿裡的 RocketMQ 中間件為例,其思路大緻為:

了解這篇分布式事務文章,可以做到吊打面試官

1.producer(本例中指A系統)發送半消息到broker,這個半消息不是說消息内容不完整, 它包含完整的消息内容, 在producer端和普通消息的發送邏輯一緻

2.broker存儲半消息,半消息存儲邏輯與普通消息一緻,隻是屬性有所不同,topic是固定的RMQ_SYS_TRANS_HALF_TOPIC,queueId也是固定為0,這個tiopic中的消息對消費者是不可見的,是以裡面的消息永遠不會被消費。這就保證了在半消息送出成功之前,消費者是消費不到這個半消息的

3.broker端半消息存儲成功并傳回後,A系統執行本地事務,并根據本地事務的執行結果來決定半消息的送出狀态為送出或者復原

4.A系統發送結束半消息的請求,并帶上送出狀态(送出 or 復原)

5.broker端收到請求後,首先從RMQ_SYS_TRANS_HALF_TOPIC的queue中查出該消息,設定為完成狀态。如果消息狀态為送出,則把半消息從RMQ_SYS_TRANS_HALF_TOPIC隊列中複制到這個消息原始topic的queue中去(之後這條消息就能被正常消費了);如果消息狀态為復原,則什麼也不做。

6.producer發送的半消息結束請求是 oneway 的,也就是發送後就不管了,隻靠這個是無法保證半消息一定被送出的,rocketMq提供了一個兜底方案,這個方案叫消息反查機制,Broker啟動時,會啟動一個TransactionalMessageCheckService 任務,該任務會定時從半消息隊列中讀出所有逾時未完成的半消息,針對每條未完成的消息,Broker會給對應的Producer發送一個消息反查請求,根據反查結果來決定這個半消息是需要送出還是復原,或者後面繼續來反查

7.consumer(本例中指B系統)消費消息,執行本地資料變更(至于B是否能消費成功,消費失敗是否重試,這屬于正常消息消費需要考慮的問題)

在RocketMq中,不論是producer收到broker存儲半消息成功傳回後執行本地事務,還是broker向producer反查消息狀态,都是通過回調機制完成,我把producer端的代碼貼出來你就明白了:

了解這篇分布式事務文章,可以做到吊打面試官

半消息發送時,會傳入一個回調類TransactionListener,使用時必須實作其中的兩個方法,executeLocalTransaction 方法會在broker傳回半消息存儲成功後執行,我們會在其中執行本地事務;checkLocalTransaction方法會在broker向producer發起反查時執行,我們會在其中查詢庫表狀态。兩個方法的傳回值都是消息狀态,就是告訴broker應該送出或者復原半消息。

了解這篇分布式事務文章,可以做到吊打面試官

b. 本地消息表方案

有時候我們目前的MQ元件并不支援事務消息,或者我們想盡量少的侵入業務方。這時我們需要另外一種方案“基于DB本地消息表“。

本地消息表最初由eBay提出來解決分布式事務的問題,是目前業界使用的比較多的方案之一,它的核心思想就是将分布式事務拆分成本地事務進行處理。

本地消息表流程

了解這篇分布式事務文章,可以做到吊打面試官

發送消息方:

需要有一個消息表,記錄着消息狀态相關資訊。

業務資料和消息表在同一個資料庫,要保證它倆在同一個本地事務。直接利用本地事務,将業務資料和事務消息直接寫入資料庫。

在本地事務中處理完業務資料和寫消息表操作後,通過寫消息到 MQ 消息隊列。使用專門的投遞工作線程進行事務消息投遞到MQ,根據投遞ACK去删除事務消息表記錄。

消息會發到消息消費方,如果發送失敗,即進行重試。

消息消費方:

處理消息隊列中的消息,完成自己的業務邏輯。

如果本地事務處理成功,則表明已經處理成功了。

如果本地事務處理失敗,那麼就會重試執行。

如果是業務層面的失敗,給消息生産方發送一個業務補償消息,通知進行復原等操作。

c. MQ事務消息 VS 本地消息表

二者的共性:

1、 事務消息都依賴MQ進行事務通知,是以都是異步的。

2、事務消息在投遞方都是存在重複投遞的可能,需要有配套的機制去降低重複投遞率,實作更友好的消息投遞去重。

3、事務消息的消費方,因為投遞重複的無法避免,是以需要進行消費去重設計或者服務幂等設計。

二者的差別:

MQ事務消息:

需要MQ支援半消息機制或者類似特性,在重複投遞上具有比較好的去重處理;

具有比較大的業務侵入性,需要業務方進行改造,提供對應的本地操作成功的回查功能;

DB本地消息表:

使用了資料庫來存儲事務消息,降低了對MQ的要求,但是增加了存儲成本;

事務消息使用了異步投遞,增大了消息重複投遞的可能性;

了解這篇分布式事務文章,可以做到吊打面試官

根據上述MQ事務消息 和 本地消息表對比,我們一般會不采用本地消息表,而是采用MQ事務消息方案(RocketMQ)來實作分布式事務。

最大努力通知方案的目标,就是發起通知方通過一定的機制,最大努力将業務處理結果通知到接收方。本質是通過引入定期校驗機制實作最終一緻性,對業務的侵入性較低,适合于對最終一緻性敏感度比較低、業務鍊路較短的場景。

最大努力通知事務主要用于外部系統,因為外部的網絡環境更加複雜和不可信,是以隻能盡最大努力去通知實作資料最終一緻性,比如充值平台與營運商、支付對接、商戶通知等等跨平台、跨企業的系統間業務互動場景;

而異步確定型事務主要适用于内部系統的資料最終一緻性保障,因為内部相對比較可控,比如訂單和購物車、收貨與清算、支付與結算等等場景。

了解這篇分布式事務文章,可以做到吊打面試官

普通消息是無法解決本地事務執行和消息發送的一緻性問題的。因為消息發送是一個網絡通信的過程,發送消息的過程就有可能出現發送失敗、或者逾時的情況。逾時有可能發送成功了,有可能發送失敗了,消息的發送方是無法确定的,是以此時消息發送方無論是送出事務還是復原事務,都有可能出現不一緻性情況,是以,通知型事務的難度在于:投遞消息和參與者本地事務的一緻性保障。因為核心要點一緻,都是為了保證消息的一緻性投遞,是以,最大努力通知事務在投遞流程上跟異步確定型是一樣的,是以也有兩個分支:

基于MQ自身的事務消息方案

基于DB的本地事務消息表方案

要實作最大努力通知,可以采用 MQ 的 ACK 機制。

最大努力通知事務在投遞之前,跟異步確定型流程都差不多,關鍵在于投遞後的處理。

因為異步確定型在于内部的事務處理,是以MQ和系統是直連并且無需嚴格的權限、安全等方面的思路設計。

了解這篇分布式事務文章,可以做到吊打面試官

業務活動的主動方,在完成業務處理之後,向業務活動的被動方發送消息,允許消息丢失。

主動方可以設定時間階梯型通知規則,在通知失敗後按規則重複通知,直到通知N次後不再通知。

3. 主動方提供校對查詢接口給被動方按需校對查詢,用于恢複丢失的業務消息。

4. 業務活動的被動方如果正常接收了資料,就正常傳回響應,并結束事務。

5. 如果被動方沒有正常接收,根據定時政策,向業務活動主動方查詢,恢複丢失的業務消息。

特點

用到的服務模式:可查詢操作、幂等操作;

被動方的處理結果不影響主動方的處理結果;

适用于對業務最終一緻性的時間敏感度低的系統;

适合跨企業的系統間的操作,或者企業内部比較獨立的系統間的操作,比如銀行通知、商戶通知等;

要實作最大努力通知,可以采用 定期檢查本地消息表的機制 。

了解這篇分布式事務文章,可以做到吊打面試官

在本地事務中處理完業務資料和寫消息表操作後,通過寫消息到 MQ 消息隊列。使用專門的投遞工作線程進行事務消息投遞到MQ,根據投遞ACK去删除事務消息表記錄

生産方定時掃描本地消息表,把還沒處理完成的消息或者失敗的消息再發送一遍。如果有靠譜的自動對賬補賬邏輯,這種方案還是非常實用的。

最大努力通知事務在于第三方系統的對接,是以最大努力通知事務有幾個特性:

業務主動方在完成業務處理後,向業務被動方(第三方系統)發送通知消息,允許存在消息丢失。

業務主動方提供遞增多擋位時間間隔(5min、10min、30min、1h、24h),用于失敗重試調用業務被動方的接口;在通知N次之後就不再通知,報警+記日志+人工介入。

業務被動方提供幂等的服務接口,防止通知重複消費。

業務主動方需要有定期校驗機制,對業務資料進行兜底;防止業務被動方無法履行責任時進行業務復原,確定資料最終一緻性。

c. 最大努力通知事務 VS 異步確定型事務

最大努力通知事務在我認知中,其實是基于異步確定型事務發展而來适用于外部對接的一種業務實作。他們主要有的是業務差别,如下:

• 從參與者來說:最大努力通知事務适用于跨平台、跨企業的系統間業務互動;異步確定型事務更适用于同網絡體系的内部服務傳遞。

• 從消息層面說:最大努力通知事務需要主動推送并提供多檔次時間的重試機制來保證資料的通知;而異步確定型事務隻需要消息消費者主動去消費。

• 從資料層面說:最大努力通知事務還需額外的定期校驗機制對資料進行兜底,保證資料的最終一緻性;而異步確定型事務隻需保證消息的可靠投遞即可,自身無需對資料進行兜底處理。

2.2 補償型

基于消息實作的事務并不能解決所有的業務場景,例如以下場景:某筆訂單完成時,同時扣掉使用者的現金。這裡事務發起方是管理訂單庫的服務,但對整個事務是否送出并不能隻由訂單服務決定,因為還要確定使用者有足夠的錢,才能完成這筆交易,而這個資訊在管理現金的服務裡。這裡我們可以引入基于補償實作的事務,其流程如下:

建立訂單資料,但暫不送出本地事務

訂單服務發送遠端調用到現金服務,以扣除對應的金額

上述步驟成功後送出訂單庫的事務

以上這個是正常成功的流程,異常流程需要復原的話,将額外發送遠端調用到現金服務以加上之前扣掉的金額。

以上流程比基于消息隊列實作的事務的流程要複雜,同時開發的工作量也更多:

編寫訂單服務裡建立訂單的邏輯

編寫現金服務裡扣錢的邏輯

編寫現金服務裡補償返還的邏輯

可以看到,該事務流程相對于基于消息實作的分布式事務更為複雜,需要額外開發相關的業務復原方法,也失去了服務間流量削峰填谷的功能。但其僅僅隻比基于消息的事務複雜多一點,若不能使用基于消息隊列的最終一緻性事務,那麼可以優先考慮使用基于補償的事務形态。

補償模式使用一個額外的協調服務來協調各個需要保證一緻性的業務服務,協調服務按順序調用各個業務微服務,如果某個業務服務調用異常(包括業務異常和技術異常)就取消之前所有已經調用成功的業務服務。

TCC 事務模型

TCC 分布式事務模型包括三部分:

了解這篇分布式事務文章,可以做到吊打面試官

Try 階段:調用 Try 接口,嘗試執行業務,完成所有業務檢查,預留業務資源。

Confirm 或 Cancel 階段:兩者是互斥的,隻能進入其中一個,并且都滿足幂等性,允許失敗重試。

Confirm 操作:對業務系統做确認送出,确認執行業務操作,不做其他業務檢查,隻使用 Try 階段預留的業務資源。

Cancel 操作:在業務執行錯誤,需要復原的狀态下執行業務取消,釋放預留資源。

Try 階段失敗可以 Cancel,如果 Confirm 和 Cancel 階段失敗了怎麼辦?

TCC 中會添加事務日志,如果 Confirm 或者 Cancel 階段出錯,則會進行重試,是以這兩個階段需要支援幂等;如果重試失敗,則需要人工介入進行恢複和處理等。

TCC事務模型的要求:

可查詢操作:服務操作具有全局唯一的辨別,操作唯一的确定的時間。

幂等操作:重複調用多次産生的業務結果與調用一次産生的結果相同。一是通過業務操作實作幂等性,二是系統緩存所有請求與處理的結果,最後是檢測到重複請求之後,自動傳回之前的處理結果。

3.TCC操作:Try階段,嘗試執行業務,完成所有業務的檢查,實作一緻性;預留必須的業務資源,實作準隔離性。Confirm階段:真正的去執行業務,不做任何檢查,僅适用Try階段預留的業務資源,Confirm操作還要滿足幂等性。Cancel階段:取消執行業務,釋放Try階段預留的業務資源,Cancel操作要滿足幂等性。TCC與2PC(兩階段送出)協定的差別:TCC位于業務服務層而不是資源層,TCC沒有單獨準備階段,Try操作兼備資源操作與準備的能力,TCC中Try操作可以靈活的選擇業務資源,鎖定粒度。TCC的開發成本比2PC高。實際上TCC也屬于兩階段操作,但是TCC不等同于2PC操作。

4.可補償操作:Do階段:真正的執行業務處理,業務處理結果外部可見。Compensate階段:抵消或者部分撤銷正向業務操作的業務結果,補償操作滿足幂等性。限制:補償操作在業務上可行,由于業務執行結果未隔離或者補償不完整帶來的風險與成本可控。實際上,TCC的Confirm和Cancel操作可以看做是補償操作。

T就是Try,兩個C分别是Confirm和Cancel。

Try就是嘗試,請求鍊路中每個參與者依次執行Try邏輯,如果都成功,就再執行Confirm邏輯,如果有失敗,就執行Cancel邏輯。

TCC與XA兩階段送出有着異曲同工之妙,下圖列出了二者之間的對比

了解這篇分布式事務文章,可以做到吊打面試官

在階段1:

在XA中,各個RM準備送出各自的事務分支,事實上就是準備送出資源的更新操作(insert、delete、update等);

而在TCC中,是主業務活動請求(try)各個從業務服務預留資源。

在階段2:

XA根據第一階段每個RM是否都prepare成功,判斷是要送出還是復原。如果都prepare成功,那麼就commit每個事務分支,反之則rollback每個事務分支。

TCC中,如果在第一階段所有業務資源都預留成功,那麼confirm各個從業務服務,否則取消(cancel)所有從業務服務的資源預留請求。

TCC和2PC不同的是:

XA是資源層面的分布式事務,強一緻性,在兩階段送出的整個過程中,一直會持有資源的鎖。基于資料庫鎖實作,需要資料庫支援XA協定,由于在執行事務的全程都需要對相關資料加鎖,一般高并發性能會比較差.

TCC是業務層面的分布式事務,最終一緻性,不會一直持有資源的鎖,性能較好。但是對微服務的侵入性強,微服務的每個事務都必須實作try、confirm、cancel等3個方法,開發成本高,今後維護改造的成本也高為了達到事務的一緻性要求,try、confirm、cancel接口必須實作幂等性操作由于事務管理器要記錄事務日志,必定會損耗一定的性能,并使得整個TCC事務時間拉長。

TCC它會弱化每個步驟中對于資源的鎖定,以達到一個能承受高并發的目的(基于最終一緻性)。

TCC 的使用場景

TCC是可以解決部分場景下的分布式事務的,但是,它的一個問題在于,需要每個參與者都分别實作Try,Confirm和Cancel接口及邏輯,這對于業務的侵入性是巨大的。

TCC 方案嚴重依賴復原和補償代碼,最終的結果是:復原代碼邏輯複雜,業務代碼很難維護。是以,TCC 方案的使用場景較少,但是也有使用的場景。比如說跟錢打交道的,支付、交易相關的場景,大家會用 TCC方案,嚴格保證分布式事務要麼全部成功,要麼全部自動復原,嚴格保證資金的正确性,保證在資金上不會出現問題。

了解這篇分布式事務文章,可以做到吊打面試官

Seata事務解決方案

在 Seata 開源之前,Seata 對應的内部版本在阿裡經濟體内部一直扮演着分布式一緻性中間件的角色,幫助經濟體平穩的度過曆年的雙11,對各BU業務進行了有力的支撐。商業化産品GTS 先後在阿裡雲、金融雲進行售賣。

Seata 是一款開源的分布式事務解決方案,緻力于在微服務架構下提供高性能和簡單易用的分布式事務服務。Seata 将為使用者提供了 AT、TCC、SAGA 和 XA 事務模式,具體的實踐案例就不在這裡說明了,大家可以在網上找到相關的内容。

四、 分布式事務方案對比分析

<col>

2PC

TCC

異步確定型

最大努力通知

一緻性

強一緻性

最終一緻性

吞吐量

實作複雜性

架構

LCN,Seata

RocketMQ

MQ

1)2PC 最大的诟病是一個阻塞協定。RM 在執行分支事務後需要等待 TM 的決定,此時服務會阻塞并鎖定資源。由于其阻塞機制和最差時間複雜度高,是以,這種設計不能适應随着事務涉及的服務數量增加而擴充的需要,很難用于并發較高以及子事務生命周期較長(long-running transactions) 的分布式服務中。如果我們的項目中并發量要求不是特别的高,可以采用lcn或者seata架構實作分布式事務。

2)如果拿TCC事務的處理流程與2PC兩階段送出做比較,2PC 通常都是在跨庫的 DB 層面,而 TCC則在應用層面的處理,需要通過業務邏輯來實作。這種分布式事務的實作方式的優勢在于,可以讓應用自己定義資料操作的粒度,使得降低鎖沖突、提高吞吐量成為可能。而不足之處則在于對應用的侵入性非常強,業務邏輯的每個分支都需要實作 Try、Confirm、Cancel 三個操作。此外,其實作難度也比較大,需要按照網絡狀态、系統故障等不同的失敗原因實作不同的復原政策。典型的使用場景:滿減,登入送優惠券等,開源的架構可以采用lcn或者seata架構實作分布式事務。

3)異步確定型(可靠消息最終一緻性)事務适合執行周期長且實時性要求不高的場景。引入消息機制後,同步的事務操作變為基于消息執行的異步操作, 避免了分布式事務中的同步阻塞操作的影響,并實作了兩個服務的解耦。典型的使用場景:注冊送積分,登入送優惠券等。由于本地消息表耦合到業務中等缺點,我們一般采用RocketMQ實作分布式事務。

4)最大努力通知是分布式事務中要求最低的一種,适用于一些最終一緻性時間敏感度低的業務;允許發起通知方處理業務失敗,在接收通知方收到通知後積極進行失敗處理,無論發起通知方如何處理結果都會不影響到接收通知方的後續處理;發起通知方需提供查詢執行情況接口,用于接收通知方校對結果。典型的使用場景:銀行通知、支付結果通知等,一般也采用MQ來實作分布式事務。

在條件允許的情況下,我們盡可能選擇本地事務單資料源,因為它減少了網絡互動帶來的性能損耗,且避免了資料弱一緻性帶來的種種問題。若某系統頻繁且不合理的使用分布式事務,應首先從整體設計角度觀察服務的拆分是否合理,是否高内聚低耦合?是否粒度太小?分布式事務一直是業界難題,因為網絡的不确定性,而且我們習慣于拿分布式事務與單機事務 ACID 做對比。

總之,無論是資料庫層的 XA、還是應用層 TCC、異步確定型(可靠消息)、最大努力通知等方案,都沒有完美解決分布式事務問題,它們不過是各自在性能、一緻性、可用性等方面做取舍,尋求某些場景偏好下的權衡。

沐子總結了上述幾種常見的分布式事務方案,篇幅有些長,大家耐心的多看幾遍,多多總結,相信在面試或者工作中會有一定的幫助。最後,如果我的文章對你有所幫助或者有所啟發,還請幫忙點贊、在看、轉發一下,你的支援會激勵我輸出更高品質的文章,非常感謝!

最後,如果我的文章對你有所幫助或者有所啟發,歡迎關注公衆号(微信搜尋公衆号:首席架構師專欄),裡面有許多技術幹貨,也有我對技術的思考和感悟,還有作為架構師的驗驗分享;關注後回複 【面試題】,有我準備的面試題、架構師大型項目實戰視訊等福利 , 小編會帶着你一起學習、成長,讓我們一起加油!!!

繼續閱讀