分布式事務(wù)解決方案全解析

分布式事務(wù)解決方案

對于剛剛接觸分布式系統(tǒng)的伙伴來說,分布式看起來非常高大上、深不可測。目前已有Dubbo、SpringCloud等較好的分布式框架,但分布式事務(wù)仍是分布式系統(tǒng)一大痛點,本文結(jié)合一些經(jīng)典博客文章,簡單解析一些常見的分布式事務(wù)解決方案。

基礎(chǔ)介紹

事務(wù)

事務(wù)可以看成是由多個小事件一起組成的一個大事件,這些小事件要么全部成功,則整個事件成功;如果有任意一個事件失敗,則所有事件均宣告失敗,并恢復成事件執(zhí)行之前的樣子。

本地事務(wù)

在單應(yīng)用開發(fā)場景中,較多的是通過關(guān)系型數(shù)據(jù)庫來控制事務(wù),利用數(shù)據(jù)庫本身的事務(wù)特性來實現(xiàn)的,稱為數(shù)據(jù)庫事務(wù),也可以稱為本地事務(wù)。數(shù)據(jù)庫事務(wù)在實現(xiàn)時會將一次事務(wù)的所有操作全部納入到一個不可分割的執(zhí)行單元,該執(zhí)行單元的所有操作要么全部成功,要么全部失敗,若其中任意操作執(zhí)行失敗,都將導致整個事務(wù)的回滾。

ACID

嚴格意義上的事務(wù)實現(xiàn)應(yīng)該是具備原子性、一致性、隔離性持久性,簡稱 ACID。

  • A(Atomic)原子性:一個事務(wù)中所有操作,要么都執(zhí)行完成,要么全部不執(zhí)行,不可能出現(xiàn)部分成功部分失敗的情況。

  • C(Consistency)一致性:在事務(wù)執(zhí)行前后,數(shù)據(jù)庫的一致性約束沒有被破壞。可以理解為數(shù)據(jù)是滿足完整性約束的,也就是不會存在中間狀態(tài)的數(shù)據(jù)。比如:張三給李四轉(zhuǎn) 100 元,轉(zhuǎn)賬前和轉(zhuǎn)賬后的數(shù)據(jù)是正確的狀態(tài)這叫一致性,如果出現(xiàn)張三轉(zhuǎn)出 100 元,李四賬戶沒有增加 100 元這就出現(xiàn)了中間狀態(tài)的錯誤數(shù)據(jù),沒有達到一致性。

  • I(Isolation)隔離性:數(shù)據(jù)庫中的事務(wù)一般都是并發(fā)的,隔離性是指并發(fā)的兩個事務(wù)的執(zhí)行互不干擾,一個事務(wù)不能看到其他事務(wù)的運行過程的中間狀態(tài)。通過配置事務(wù)隔離級別可以比避免臟讀、重復讀問題。

  • D(Durability)持久性:一個事務(wù)完成了之后數(shù)據(jù)就被被持久化到數(shù)據(jù)庫,之后的其他操作或故障都不會對事務(wù)的結(jié)果產(chǎn)生影響或者回滾操作。

分布式事務(wù)

隨著互聯(lián)網(wǎng)技術(shù)的發(fā)展與數(shù)據(jù)體量的擴增,軟件系統(tǒng)逐漸由單體應(yīng)用演變?yōu)榉植际较到y(tǒng)/微服務(wù)應(yīng)用。分布式系統(tǒng)把一個單體應(yīng)用系統(tǒng)拆分成可獨立部署的多個微服務(wù),很多場景下需要服務(wù)之間遠程協(xié)作才能完成事務(wù)操作,這種分布式系統(tǒng)環(huán)境下由不同的服務(wù)之間通過網(wǎng)絡(luò)遠程協(xié)作完成事務(wù)稱之為分布式事務(wù)。分布式系統(tǒng)中實現(xiàn)事務(wù),其實是由多個本地事務(wù)組合而成。對于分布式事務(wù)而言幾乎滿足不了 ACID。

場景介紹

  • 跨JVM進程產(chǎn)生分布式事務(wù) 典型的場景就是微服務(wù)架構(gòu):微服務(wù)之間通過遠程調(diào)用完成事務(wù)操作。比如:訂單微服務(wù)和庫存微服務(wù),下單的同時訂單微服務(wù)請求庫存微服務(wù)減少庫存。

    跨JVM進程產(chǎn)生分布式事務(wù)
  • 跨數(shù)據(jù)庫實例產(chǎn)生分布式事務(wù) 當單體應(yīng)用需要訪問多個數(shù)據(jù)庫(實例)時就會產(chǎn)生分布式事務(wù)。比如:用戶信息和訂單信息分別在兩個MySQL實例存儲,用戶管理系統(tǒng)刪除用戶信息,需要分別刪除用戶信息及用戶的訂單信息,由于用戶信息和訂單數(shù)據(jù)分布在不同的數(shù)據(jù)實例,需要通過不同的數(shù)據(jù)庫鏈接去操作數(shù)據(jù),就會產(chǎn)生分布式事務(wù)。

    跨數(shù)據(jù)庫實例產(chǎn)生分布式事務(wù)
  • 多服務(wù)訪問同一個數(shù)據(jù)庫實例 訂單微服務(wù)和庫存微服務(wù)即使訪問同一個數(shù)據(jù)庫也會產(chǎn)生分布式事務(wù),原因就是跨JVM進程,兩個微服務(wù)持有了不同的數(shù)據(jù)庫鏈接進行數(shù)據(jù)庫操作,此時產(chǎn)生分布式事務(wù)。

    多服務(wù)訪問同一個數(shù)據(jù)庫實例

CAP理論

CAP 是 <mark style="box-sizing: border-box; background: rgb(235, 255, 235); color: rgb(34, 34, 34); border-radius: 2px; padding: 2px 4px; margin: 0px 2px; font-weight: 500; text-indent: 0px;">Consistency</mark>、<mark style="box-sizing: border-box; background: rgb(235, 255, 235); color: rgb(34, 34, 34); border-radius: 2px; padding: 2px 4px; margin: 0px 2px; font-weight: 500; text-indent: 0px;">Availability</mark>、<mark style="box-sizing: border-box; background: rgb(235, 255, 235); color: rgb(34, 34, 34); border-radius: 2px; padding: 2px 4px; margin: 0px 2px; font-weight: 500; text-indent: 0px;">Partition tolerance</mark> 三個單詞的縮寫,分別表示一致性可用性分區(qū)容忍性。一個分布式系統(tǒng)最多只能同時滿足一致性、可用性和分區(qū)容錯性這三項中的兩項。

CAP簡介
  • C(Consistency)一致性

一致性是指寫操作后的讀操作可以讀取到最新的數(shù)據(jù)狀態(tài),當數(shù)據(jù)分布在多個節(jié)點上,從任意結(jié)點讀取到的數(shù)據(jù)都是最新的狀態(tài),即<u style="box-sizing: border-box; text-indent: 0px;">所有節(jié)點在同一時間的數(shù)據(jù)完全一致</u>。

A. 從客戶端和服務(wù)端來看一致性:

1.從客戶端來看,一致性主要指的是多并發(fā)訪問時更新過的數(shù)據(jù)如何獲取的問題。

2.從服務(wù)端來看,則是更新如何分布到整個系統(tǒng),以保證數(shù)據(jù)最終一致。

B. 從一致性的程度來看一致性:

1.<mark style="box-sizing: border-box; background: rgb(235, 255, 235); color: rgb(34, 34, 34); border-radius: 2px; padding: 2px 4px; margin: 0px 2px; font-weight: 500; text-indent: 0px;">強一致性</mark>:對于關(guān)系型數(shù)據(jù)庫,要求更新過的數(shù)據(jù)能被后續(xù)的訪問都能看到。

2.<mark style="box-sizing: border-box; background: rgb(235, 255, 235); color: rgb(34, 34, 34); border-radius: 2px; padding: 2px 4px; margin: 0px 2px; font-weight: 500; text-indent: 0px;">弱一致性</mark>:能容忍后續(xù)的部分或者全部訪問不到。

3.<mark style="box-sizing: border-box; background: rgb(235, 255, 235); color: rgb(34, 34, 34); border-radius: 2px; padding: 2px 4px; margin: 0px 2px; font-weight: 500; text-indent: 0px;">最終一致性</mark>:經(jīng)過一段時間后要求能訪問到更新后的數(shù)據(jù)。

C. 分布式系統(tǒng)一致性的特點:

1.由于存在數(shù)據(jù)同步的過程,寫操作的響應(yīng)會有一定的延遲。

2.為了保證數(shù)據(jù)一致性會對資源暫時鎖定,待數(shù)據(jù)同步完成釋放鎖定資源。

3.如果請求數(shù)據(jù)同步失敗的結(jié)點則會返回錯誤信息,一定不會返回舊數(shù)據(jù)。

  • A(Availability)可用性

可用性指服務(wù)一直可用,任何事務(wù)操作都可以得到響應(yīng)結(jié)果,且不會出現(xiàn)響應(yīng)超時或響應(yīng)錯誤。

  • P(Partition tolerance)分區(qū)容錯性

分區(qū)容錯性指在遇到某節(jié)點或網(wǎng)絡(luò)分區(qū)故障的時候,仍然能夠?qū)ν馓峁M足一致性和可用性的服務(wù)。通常分布式系統(tǒng)的各各結(jié)點部署在不同的子網(wǎng),不可避免的會出現(xiàn)由于網(wǎng)絡(luò)問題而導致結(jié)點之間通信失敗,此時仍可對外提供服務(wù),即為分區(qū)容錯性。分區(qū)容錯性分是布式系統(tǒng)具備的基本能力。

CAP組合

在所有分布式事務(wù)場景中不會同時具備 CAP 三個特性,因為在具備了P的前提下C和A是不能共存的。在生產(chǎn)中對分布式事務(wù)處理時要根據(jù)需求來確定滿足 CAP 的哪兩個方面。

  • AP:滿足可用性和容錯性,舍棄一致性。這也就是意味著你的系統(tǒng)在并發(fā)訪問的時候可能會出現(xiàn)數(shù)據(jù)不一致的情況。這是很多分布式系統(tǒng)設(shè)計時的選擇,大多數(shù)都是犧牲了一致性。但通常實現(xiàn) AP 都會保證最終一致性,比如現(xiàn)在的12306搶票,本來你看到的是還有一張票,其實在這個時刻已經(jīng)被買走了,你填好了信息準備提交訂單的時候發(fā)現(xiàn)系統(tǒng)提示你已售罄。這就是犧牲了一致性,但過一段時間再重新查詢,就會發(fā)現(xiàn)無票,這是實現(xiàn)了最終一致性,只要用戶可以接受在一定的時間查到正確的數(shù)據(jù)即可。

  • CP:滿足一致性和容錯性,舍棄可用性。如果你的系統(tǒng)允許有段時間的訪問失效等問題,這個是可以滿足的。比如多個人并發(fā)買票,后臺網(wǎng)絡(luò)出現(xiàn)故障,你買的時候系統(tǒng)就崩潰了。zookeeper 其實就是追求的強一致,又比如跨行轉(zhuǎn)賬,一次轉(zhuǎn)賬請求要等待雙方銀行系統(tǒng)都完成整個事務(wù)才算完成。

  • CA:滿足一致性和可用性,舍棄容錯性。不考慮由于網(wǎng)絡(luò)不通或結(jié)點掛掉的問題,那么系統(tǒng)將不是一個標準的分布式系統(tǒng),涉及分布式的想法就是把功能分開,部署到不同的機器上。最常用的關(guān)系型數(shù)據(jù)就滿足了 CA。

小結(jié)

CAP 是一個已經(jīng)被證實的理論,一個分布式系統(tǒng)最多只能同時滿足:一致性(Consistency)、可用性(Availability)和分區(qū)容忍性(Partition tolerance)這三項中的兩項。它可以作為我們進行架構(gòu)設(shè)計、技術(shù)選型的考量標準。對于多數(shù)大型互聯(lián)網(wǎng)應(yīng)用的場景,節(jié)點多、部署分散,而且現(xiàn)在的集群規(guī)模越來越大,所以節(jié)點故障、網(wǎng)絡(luò)故障是常態(tài),而且要保證服務(wù)可用性達到 N 個 9(99.99..%),并要達到良好的響應(yīng)性能來提高用戶體驗,因此一般都會做出如下選擇:保證 P 和 A ,舍棄 C 的強一致性,但保證最終一致性。

BASE理論

BASE理論簡介

BASE 是 Basically Available(基本可用)、Soft state(軟狀態(tài))和 Eventually consistent (最終一致性)三個短語的縮寫。BASE 理論是對 CAP 中 AP 的一個擴展,通過犧牲強一致性來獲得可用性,當出現(xiàn)故障允許部分不可用但要保證核心功能可用,允許數(shù)據(jù)在一段時間內(nèi)是不一致的,但最終達到一致狀態(tài)。滿足BASE理論的事務(wù),我們稱之為“柔性事務(wù)”。

  • 基本可用:分布式系統(tǒng)在出現(xiàn)故障時,允許損失部分可用功能,保證核心功能可用。如電商網(wǎng)站交易付款出現(xiàn)問題了,商品依然可以正常瀏覽。

  • 軟狀態(tài):由于不要求強一致性,所以BASE允許系統(tǒng)中存在中間狀態(tài)(也叫軟狀態(tài)),這個狀態(tài)不影響系統(tǒng)可用性,如訂單的"支付中"、“數(shù)據(jù)同步中”等狀態(tài),待數(shù)據(jù)最終一致后狀態(tài)改為“成功”狀態(tài)。

  • 最終一致:最終一致是指經(jīng)過一段時間后,所有節(jié)點數(shù)據(jù)都將會達到一致。如訂單的"支付中"狀態(tài),最終會變 為“支付成功”或者"支付失敗",使訂單狀態(tài)與實際交易結(jié)果達成一致,但需要一定時間的延遲、等待。

強一致性和最終一致性

CAP 理論告訴我們一個分布式系統(tǒng)最多只能同時滿足一致性(Consistency)、可用性(Availability)和分區(qū)容忍性(Partition tolerance)這三項中的兩項,其中AP在實際應(yīng)用中較多,AP 即舍棄一致性,保證可用性和分區(qū)容忍性,但是在實際生產(chǎn)中很多場景都要實現(xiàn)一致性,比如主數(shù)據(jù)庫向從數(shù)據(jù)庫同步數(shù)據(jù),即使不要一致性,但是最終也要將數(shù)據(jù)同步成功來保證數(shù)據(jù)一致,這種一致性和 CAP 中的一致性不同,CAP 中的一致性要求<mark style="box-sizing: border-box; background: rgb(235, 255, 235); color: rgb(34, 34, 34); border-radius: 2px; padding: 2px 4px; margin: 0px 2px; font-weight: 500; text-indent: 0px;">在任何時間查詢每個結(jié)點數(shù)據(jù)都必須一致</mark>,它強調(diào)的是強一致性,但是最終一致性是允許可以在一段時間內(nèi)每個結(jié)點的數(shù)據(jù)不一致,但是經(jīng)過一段時間每個結(jié)點的數(shù)據(jù)必須一致,它強調(diào)的是最終數(shù)據(jù)的一致性。

ACID與BASE
ACID (剛性事務(wù)) BASE (柔性事務(wù))
原子性(Atomicity) 基本可用(Basically Available)
一致性(Consistency) 柔性狀態(tài)(Soft state)
隔離性(Isolation) 最終一致性(Eventually Consistent)
持久性(Durability)

二階段提交(2PC)

方案簡介

2PC(Two-phase commit protocol),中文叫二階段提交。 2PC 是一種強一致性設(shè)計,有事務(wù)協(xié)調(diào)者事務(wù)參與者兩個主要角色,事務(wù)的發(fā)起者為事務(wù)協(xié)調(diào)者,事務(wù)的其他執(zhí)行者為事務(wù)參與者。事務(wù)協(xié)調(diào)者協(xié)調(diào)管理各事務(wù)參與者的提交和回滾,。我們可以把事務(wù)協(xié)調(diào)者想象為帶頭大哥,而事務(wù)參與者則理解為跟班小弟,由帶頭大哥協(xié)調(diào)所有跟班小弟的任務(wù)執(zhí)行。

流程分析

準備階段

  1. 事務(wù)協(xié)調(diào)者,給所有事務(wù)參與者發(fā)送事務(wù)內(nèi)容,并詢問能否提交事務(wù),然后等待參與者回復。

  2. 事務(wù)參與者,收到事務(wù)內(nèi)容,開始執(zhí)行事務(wù),同時將 <u style="box-sizing: border-box; text-indent: 0px;">undo</u> 和 <u style="box-sizing: border-box; text-indent: 0px;">redo</u> 信息記入事務(wù)日志中,但此時事務(wù)參與者并不提交事務(wù)。

  3. 如果參與者執(zhí)行事務(wù)成功,則給協(xié)調(diào)者響應(yīng) yes ,回答可以進行事務(wù)提交;若果參與者事務(wù)執(zhí)行失敗,則給協(xié)調(diào)者回復 no ,表示不可進行事務(wù)提交。

提交階段

事務(wù)協(xié)調(diào)者會等待收到所有事務(wù)參與者響應(yīng)后才會進行下一步操作,且事務(wù)協(xié)調(diào)者在該階段中有超時機制。如果事務(wù)協(xié)調(diào)者收到事務(wù)參與者響應(yīng)信息為 yes,則向所有事務(wù)參與者發(fā)送提交(commit)信息;如果事務(wù)協(xié)調(diào)者收到事務(wù)參與者的失敗信息或超時信息,則會給所有事務(wù)參與者發(fā)送回滾(rollback)信息進行事務(wù)回滾。

事務(wù)參與者根據(jù)來自事務(wù)協(xié)調(diào)者的指令執(zhí)行提交事務(wù)或者回滾事務(wù)的操作,并釋放所有事務(wù)處理過程中占用的鎖資源(必須在最后階段釋放鎖資源) 。以下是兩種情況具體步驟解析:

  • 當所有事務(wù)參與者均響應(yīng) yes,則提交事務(wù),如下圖。
  1. 事務(wù)協(xié)調(diào)者向所有事務(wù)參與者發(fā)出正式提交事務(wù)(commit)的請求。

  2. 事務(wù)參與者執(zhí)行事務(wù)提交操作,若提交失敗,則會不斷重試,直到提交成功,然后釋放整個事務(wù)期間占用的資源。

  3. 所有事務(wù)參與者向事務(wù)協(xié)調(diào)者響應(yīng)提交事務(wù)完成的 ack 消息。

  4. 事務(wù)協(xié)調(diào)者收到所有事務(wù)參與者響應(yīng)的 ack 消息后,完成提交事務(wù)操作。

2PC-正常事務(wù)
  • 當準備階段任意一個事務(wù)參與者響應(yīng) no,則中斷事務(wù),如下圖:
  1. 事務(wù)協(xié)調(diào)者向所有參與者發(fā)出回滾(rollback)請求。

  2. 事務(wù)參與者使用準備階段中的 <u style="box-sizing: border-box; text-indent: 0px;">undo</u> 信息執(zhí)行事務(wù)回滾操作,若回滾失敗則會不斷重試,直到所有事務(wù)參與者都回滾成功,然后釋放整個事務(wù)期間占用的資源。

  3. 各事務(wù)參與者向事務(wù)協(xié)調(diào)者響應(yīng)回滾事務(wù)完成的 ack 消息。

  4. 事務(wù)協(xié)調(diào)者收到所有參與者響應(yīng)的 ack 消息后,完成事務(wù)中斷操作。

2PC-事務(wù)中斷

方案小結(jié)

2PC 方案實現(xiàn)起來相對簡單,但實際項目中用的比較少,主要因為以下問題:

  1. 性能較低:所有事務(wù)參與者在事務(wù)提交階段處于同步阻塞狀態(tài),占用系統(tǒng)資源,效率低,有性能瓶頸。

  2. 可靠性問題:協(xié)調(diào)者存在單點故障問題,如果事務(wù)協(xié)調(diào)者出現(xiàn)故障,事務(wù)參與者將一直處于鎖死狀態(tài)。

  3. 數(shù)據(jù)一致性問題:在提交階段中,如果發(fā)生網(wǎng)絡(luò)波動,部分事務(wù)參與者收到了提交消息,另一些事務(wù)參與者沒收到提交消息,導致了節(jié)點之間數(shù)據(jù)不一致的問題。

三階段提交(3PC)

方案簡介

3PC 也是強一致性,該方案是 2PC 上的改進版本,主要是在事務(wù)協(xié)調(diào)者和事務(wù)參與者中都引入超時機制,并 將 2PC 方案的準備階段拆分為2個階段,插入了一個預提交階段(PreCommit),以此來處理2PC的提交階段中事務(wù)參與者發(fā)生崩潰或錯誤,或者網(wǎng)絡(luò)波動,導致事務(wù)參與者無法知曉是否提交或回滾的不確定狀態(tài)所引起的延時問題。

流程分析

準備階段(CanCommit)

事務(wù)協(xié)調(diào)者向事務(wù)參與者發(fā)送 commit 請求,參與者如果可以提交就 yes 并進入預提交狀態(tài)(與2PC中的準備階段不同,參與者不執(zhí)行事務(wù)操作),否則返回 no 響應(yīng)。

  1. 事務(wù)協(xié)調(diào)者向所有參與者發(fā)出包含事務(wù)內(nèi)容的 canCommit 請求,詢問是否可以提交事務(wù),并等待所有事務(wù)參與者的響應(yīng)。

  2. 事務(wù)參與者收到 canCommit 請求后,如果認為可以執(zhí)行事務(wù)操作,則響應(yīng) yes 并進入預備狀態(tài),否則反饋 no (與2PC中的準備階段不同,參與者不執(zhí)行事務(wù)操作)。

預提交階段(PreCommit)

3PC中的預提交階段和2PC中的準備階段一樣,執(zhí)行事務(wù)但不提交。事務(wù)協(xié)調(diào)者根據(jù)準備階段中事務(wù)參與者的響應(yīng)決定是否可以進行事務(wù)的預提交操作。

  • 準備階段中所有事務(wù)參與者均響應(yīng) yes,事務(wù)參與者進入預提交狀態(tài)。
  1. 事務(wù)協(xié)調(diào)者向所有事務(wù)參與者發(fā)出 preCommit 請求,進入預提交階段。

  2. 事務(wù)參與者收到 preCommit 請求后,執(zhí)行事務(wù)操作,將 undo 和 redo 信息記入事務(wù)日志中(但不提交事務(wù))。

  3. 所有事務(wù)參與者向事務(wù)協(xié)調(diào)者響應(yīng)執(zhí)行事務(wù)成功的 ack 消息或失敗的 no消息 ,并等待事務(wù)協(xié)調(diào)者發(fā)出最終指令。

3PC-預提交響應(yīng)yes
  • 當準備階段任意一個事務(wù)參與者響應(yīng) no,或者等待超時后事務(wù)協(xié)調(diào)者尚無法收到所有參與者的響應(yīng),則中斷事務(wù)
  1. 事務(wù)協(xié)調(diào)者向所有事務(wù)參與者發(fā)出 abort 請求。

  2. 事務(wù)參與者收到事務(wù)協(xié)調(diào)者發(fā)出的 abort 請求,或在等待事務(wù)協(xié)調(diào)者的指令過程中出現(xiàn)超時,均會中斷事務(wù)。

3PC-預提交響應(yīng)no

提交階段(DoCommit)

該階段與2PC的提交階段一樣,進行真正的事務(wù)提交。進入提交階段后,無論是事務(wù)協(xié)調(diào)者出現(xiàn)問題,或者事務(wù)協(xié)調(diào)者與事務(wù)參與者網(wǎng)絡(luò)出現(xiàn)問題,都會導致事務(wù)參與者無法接收到事務(wù)協(xié)調(diào)者發(fā)出的 do Commit 請求或 abort 請求,事務(wù)參與者都會在等待超時之后,繼續(xù)執(zhí)行事務(wù)提交,因為預提交階段的引入起到了一個統(tǒng)一狀態(tài)的作用,進入到提交階段則事務(wù)參與者默認認為事務(wù)應(yīng)該被提交。

  • 預提交階段中所有事務(wù)參與者均響應(yīng) ack 消息,執(zhí)行真正的事務(wù)提交。
  1. 如果事務(wù)協(xié)調(diào)者處于工作狀態(tài),則向所有事務(wù)參與者發(fā)出 do Commit 請求。

  2. 事務(wù)參與者收到 do Commit 請求后,會正式執(zhí)行事務(wù)提交操作,并釋放整個事務(wù)期間占用的資源。

  3. 所有事務(wù)參與者向協(xié)調(diào)者響應(yīng)提交事務(wù)完成的 ack 消息。

  4. 事務(wù)協(xié)調(diào)者收到所有事務(wù)參與者響應(yīng)的 ack 消息后,完成事務(wù)提交。

3PC-提交事務(wù)
  • 當預提交階段中任意一個事務(wù)參與者響應(yīng) no,或者在預提交階段中事務(wù)協(xié)調(diào)者無法收到所有參與者的響應(yīng),則中斷事務(wù)。
  1. 如果事務(wù)協(xié)調(diào)者處于工作狀態(tài),向所有事務(wù)參與者發(fā)出 abort 請求。

  2. 事務(wù)參與者使用預提交階段中的 undo 信息執(zhí)行回滾操作,并釋放整個事務(wù)期間占用的資源。

  3. 所有事務(wù)參與者向事務(wù)協(xié)調(diào)者響應(yīng)回滾事務(wù)完成的 ack 消息。

  4. 事務(wù)協(xié)調(diào)者收到所有事務(wù)參與者響應(yīng)的 ack 消息后,完成事務(wù)中斷。

3PC-事務(wù)中斷

方案小結(jié)

優(yōu)點:3PC相比2PC,會先詢問事務(wù)參與者是否有條件接事務(wù),不會直接鎖資源,降低了阻塞范圍,在等待超時后事務(wù)協(xié)調(diào)者或事務(wù)參與者會中斷事務(wù)。避免了事務(wù)協(xié)調(diào)者單點故障問題,3PC的提交階段中即使事務(wù)協(xié)調(diào)者出現(xiàn)問題時,事務(wù)參與者會繼續(xù)提交事務(wù)。

缺點:引入一個階段,多一次交互,性能比2PC更低,且絕大部分情況事務(wù)參與者都由能執(zhí)行事務(wù)的條件,仍要先詢問一次。而且數(shù)據(jù)不一致問題依然存在,當在參與者收到 preCommit 請求后等待 do commite 指令時,此時如果協(xié)調(diào)者請求中斷事務(wù),而協(xié)調(diào)者無法與參與者正常通信,會導致參與者繼續(xù)提交事務(wù),造成數(shù)據(jù)不一致。

TCC

方案簡介

2PC 和3PC 都是數(shù)據(jù)庫層面的,而 TCC 是基于業(yè)務(wù)層的分布式事務(wù)。TCC 的概念,最早是由 Pat Helland 于 2007 年發(fā)表的一篇名為《Life beyond Distributed Transactions:an Apostate’s Opinion》的論文提出的。其核心思想是:針對每個操作,都要注冊一個與其對應(yīng)的確認和補償(撤銷)操作。

TCC 是 Try、Con?rm、Cancel 三個詞語的縮寫,TCC 要求每個分支事務(wù)實現(xiàn)三個操作:預處理 Try、確認 Con?rm、撤銷,Try、Confirm、Cancel 可以理解為 SQL 事務(wù)中的 Lock、Commit、Rollback??梢詫CC 簡單理解為服務(wù)化的 2PC 編程模型,都是先試探性的執(zhí)行,如果都可以那就真正的執(zhí)行,如果不行就回滾。

TCC中還可以有一個事務(wù)管理者的角色 - TM 事務(wù)管理器:TM事務(wù)管理器可以實現(xiàn)為獨立的服務(wù),也可以讓全局事務(wù)發(fā)起方充當 TM 的角色,TM 獨立出來是為了成為公用組件,是為了考慮系統(tǒng)結(jié)構(gòu)和軟件復用。TM 在發(fā)起全局事務(wù)時生成全局事務(wù)記錄,全局事務(wù) ID 貫穿整個分布式事務(wù)調(diào)用鏈條,用來記錄事務(wù)上下文, 追蹤和記錄狀態(tài),由于 Con?rm 和 Cancel 失敗需進行重試,因此需要實現(xiàn)為冪等,冪等性是指同一個操作無論請求多少次,其結(jié)果都相同。

TCC 的 Try、Confirm、Cancel 3 個方法均由業(yè)務(wù)編碼實現(xiàn):

  • Try 操作作為一階段,負責資源的檢查、預留和鎖定。

  • Confirm 操作相當于2PC的提交階段,執(zhí)行真正的業(yè)務(wù)。

  • Cancel 是預留資源的取消,可以理解為撤銷預留階段的動作。

流程分析

為了方便理解,下面以電商下單為例進行方案解析,這里把整個過程簡單分為扣減庫存,訂單創(chuàng)建 2 個步驟,庫存服務(wù)和訂單服務(wù)分別在不同的服務(wù)器節(jié)點上。

Try 階段

從執(zhí)行階段來看,與傳統(tǒng)事務(wù)機制中業(yè)務(wù)邏輯相同。但從業(yè)務(wù)角度來看,卻不一樣。

TCC 機制中的 Try 僅是一個初步操作,它和后續(xù)的確認一起才能真正構(gòu)成一個完整的業(yè)務(wù)邏輯,這個階段主要完成:

  • 完成所有業(yè)務(wù)檢查工作( 一致性 ) 。

  • 預留鎖定必須業(yè)務(wù)資源( 準隔離性 ) 。

  • Try 嘗試執(zhí)行業(yè)務(wù)。

TCC 事務(wù)機制是以初步操作(Try)為中心的,確認操作(Confirm)和取消操作(Cancel)都是圍繞初步操作(Try)而展開。因此,Try 階段中的操作,其保障性是最好的,即使失敗,仍然有取消操作(Cancel)可以將其執(zhí)行結(jié)果撤銷。

假設(shè)商品庫存為 100,購買數(shù)量為 2,這里檢查和更新庫存的同時,凍結(jié)用戶購買數(shù)量的庫存,同時創(chuàng)建訂單,訂單狀態(tài)為待確認。

TCC-Try
Confirm階段

Confirm:當 Try 階段服務(wù)全部正常執(zhí)行, 執(zhí)行確認業(yè)務(wù)邏輯操作。Try 階段所有分支事務(wù)執(zhí)行成功后開始執(zhí)行 Con?rm。通常情況下,采用 TCC 則認為 Con?rm 階段是不會出錯的。即:只要 Try 成功,Con?rm 一定成功,若 Con?rm 階段真的出錯了,需引入重試機制或人工處理,所以 Confirm 操作需要滿足冪等性。

Confirm 階段也可以看成是對 Try 階段的一個補充,Try + Confirm 一起組成了一個完整的業(yè)務(wù)邏輯,Confirm 階段使用的資源一定是 Try 階段預留的業(yè)務(wù)資源。

TCC-Confirm
Cancel 階段

Cancel:當 Try 階段存在服務(wù)執(zhí)行失敗需要回滾, 進入 Cancel 階段,執(zhí)行分支事務(wù)的業(yè)務(wù)取消,釋放Try階段中的預留業(yè)務(wù)員資源。通常情況下,采用 TCC 則認為 Cancel 階段也是一定成功的。若 Cancel 階段真的出錯了,需引入重試機制或人工處理,所以 Cancel 操作需要滿足冪等性。

TCC-CANCEL

方案小結(jié)

TCC 事務(wù)機制相對于傳統(tǒng)事務(wù)機制(X/Open XA),有以下優(yōu)點:

  • 性能提升:讓應(yīng)用自己定義數(shù)據(jù)操作的粒度,使得降低鎖沖突、不會鎖定整個資源,提高吞吐量。

  • 適用范圍廣:基于業(yè)務(wù)實現(xiàn),TCC 可以跨數(shù)據(jù)庫,跨不同業(yè)務(wù)系統(tǒng)實現(xiàn)事務(wù)。

  • 最終一致性:基于 Confirm 和 Cancel 的冪等性,保證事務(wù)最終完成確認或者取消,保證數(shù)據(jù)的最終一致性。

  • 可靠性:解決了 XA 協(xié)議的協(xié)調(diào)者單點故障問題,由主業(yè)務(wù)方發(fā)起并控制整個業(yè)務(wù)活動,業(yè)務(wù)活動管理器也變成多點,引入集群。

缺點:

  • TCC 的 Try、Confirm 和 Cancel 操作功能要按具體業(yè)務(wù)來實現(xiàn),業(yè)務(wù)耦合度較高,提高了開發(fā)成本。

  • 這三個操作方法的實現(xiàn)有一定難度,需要按照網(wǎng)絡(luò)狀態(tài)、系統(tǒng)故障等不同的失敗原因?qū)崿F(xiàn)不同的回滾策略。

本地消息表

方案簡介

本地消息表的方案最初是由 eBay 提出,該方案基于可靠消息最終一致性方案。可靠消息最終一致性方案是指當事務(wù)發(fā)起方執(zhí)行完成本地事務(wù)后并發(fā)出一條消息,事務(wù)參與方(消息消費者)一定能夠接收消息并處理事務(wù)成功,此方案強調(diào)的是只要消息發(fā)給事務(wù)參與方最終事務(wù)要達到一致。

本地消息表核心思路是利用各系統(tǒng)本地的事務(wù)來實現(xiàn)分布式事務(wù)。通過在事務(wù)主動發(fā)起方額外新建事務(wù)消息表,事務(wù)發(fā)起方處理業(yè)務(wù)和記錄事務(wù)消息在本地事務(wù)中完成,輪詢事務(wù)消息表的數(shù)據(jù)發(fā)送事務(wù)消息,事務(wù)被動方基于消息中間件消費事務(wù)消息表中的事務(wù)。

事務(wù)消息表與業(yè)務(wù)數(shù)據(jù)表處于同一個數(shù)據(jù)庫中,然后在執(zhí)行業(yè)務(wù)的時候 <mark style="box-sizing: border-box; background: rgb(235, 255, 235); color: rgb(34, 34, 34); border-radius: 2px; padding: 2px 4px; margin: 0px 2px; font-weight: 500; text-indent: 0px;"><u style="box-sizing: border-box; text-indent: 0px;">將業(yè)務(wù)的執(zhí)行和將消息放入消息表中的操作放在同一個事務(wù)中</u></mark>,保證消息放入本地表中業(yè)務(wù)肯定是執(zhí)行成功的,這樣就能利用本地事務(wù)來保證在對這兩個表的操作滿足事務(wù)特性,并且使用了消息隊列來保證最終一致性。這樣設(shè)計可以避免”業(yè)務(wù)處理成功 + 事務(wù)消息發(fā)送失敗",或"業(yè)務(wù)處理失敗 + 事務(wù)消息發(fā)送成功"的棘手情況出現(xiàn),保證 2 個系統(tǒng)事務(wù)的數(shù)據(jù)一致性。

流程分析

把分布式事務(wù)最先開始處理的事務(wù)方稱為事務(wù)主動方,在事務(wù)主動方之后處理的業(yè)務(wù)內(nèi)的其他事務(wù)稱為事務(wù)被動方。為了方便理解,以電商下單為例進行方案解析,這里把整個過程簡單分為扣減庫存,訂單創(chuàng)建 2 個步驟。

庫存服務(wù)和訂單服務(wù)分別在不同的服務(wù)器節(jié)點上,其中庫存服務(wù)是事務(wù)主動方,訂單服務(wù)是事務(wù)被動方。事務(wù)的主動方需要額外新建事務(wù)消息表,用于記錄分布式事務(wù)的消息的發(fā)生、處理狀態(tài)。

整個業(yè)務(wù)處理流程如下圖:

本地消息表-流程
  1. 事務(wù)主動方處理本地事務(wù)。

    事務(wù)主動方在本地事務(wù)中處理業(yè)務(wù)更新操作和寫消息表操作。上面例子中庫存服務(wù)階段在本地事務(wù)中完成扣減庫存和寫消息表(圖中 1、2)。

  2. 事務(wù)主動方通過消息中間件,通知事務(wù)被動方處理事務(wù)通知事務(wù)待消息。

    消息中間件可以基于 Kafka、RocketMQ 等消息隊列,事務(wù)主動方主動寫消息到消息隊列,事務(wù)消費方消費并處理消息隊列中的消息。

    庫存服務(wù)把事務(wù)待處理消息寫到消息中間件,訂單服務(wù)消費消息中間件的消息,完成新增訂單(圖中 3 - 5)。

  3. 事務(wù)被動方通過消息中間件,通知事務(wù)主動方事務(wù)已處理的消息。

    訂單服務(wù)把事務(wù)已處理消息寫到消息中間件,庫存服務(wù)消費中間件的消息,并將事務(wù)消息的狀態(tài)更新為已完成(圖中 6 - 8)。

為了數(shù)據(jù)的一致性,當處理錯誤需要重試,事務(wù)發(fā)送方和事務(wù)接收方相關(guān)業(yè)務(wù)處理需要支持冪等,而且一般重試會有最大次數(shù),超過最大次數(shù)可以記錄下報警讓人工處理。可以看到本地消息表其實實現(xiàn)的是最終一致性,容忍了數(shù)據(jù)暫時不一致的情況。具體保存一致性的容錯處理如下:

  • 當步驟 1 處理出錯,事務(wù)回滾,相當于什么都沒發(fā)生。

  • 當步驟 2、步驟 3 處理出錯,由于未處理的事務(wù)消息還是保存在事務(wù)發(fā)送方,事務(wù)發(fā)送方可以定時輪詢讀取本地消息表中的超時消息數(shù)據(jù),再次發(fā)送到消息中間件進行處理。事務(wù)被動方消費事務(wù)消息重試處理。

  • 如果是業(yè)務(wù)上的失敗,事務(wù)被動方可以發(fā)消息給事務(wù)主動方進行回滾。

  • 如果多個事務(wù)被動方已經(jīng)消費消息,事務(wù)主動方需要回滾事務(wù)時需要通知事務(wù)被動方回滾。

方案小結(jié)

本地消息表的優(yōu)點如下:

  • 從應(yīng)用設(shè)計開發(fā)的角度實現(xiàn)了事務(wù)參與方接收消息數(shù)據(jù)的可靠性,消息數(shù)據(jù)的可靠性不依賴于消息中間件,弱化了對 MQ 中間件特性的依賴。

  • 引入消息機制后,同步的事務(wù)操作變?yōu)榛谙?zhí)行的異步操作, 避免了分布式事務(wù)中的同步阻塞操作的影響,并實現(xiàn)了兩個服務(wù)的解耦(非業(yè)務(wù)解耦)。

  • 方案輕量,容易實現(xiàn)。

缺點如下:

  • 與具體的業(yè)務(wù)場景綁定,耦合性強,不可公用。

  • 消息數(shù)據(jù)與業(yè)務(wù)數(shù)據(jù)同庫,占用業(yè)務(wù)系統(tǒng)資源。

  • 業(yè)務(wù)系統(tǒng)在使用關(guān)系型數(shù)據(jù)庫的情況下,消息服務(wù)性能會受到關(guān)系型數(shù)據(jù)庫并發(fā)性能的局限。

MQ 事務(wù)消息

方案簡介

MQ事務(wù)消息方案可以算是最大努力通知方案,最大努力通知方案的目標:發(fā)起通知方通過一定的機制最大努力將業(yè)務(wù)處理結(jié)果通知到接收方。具體包括:

  1. 有一定的消息重復通知機制。因為接收通知方可能沒有接收到通知,此時要有一定的機制對消息重復通知。

  2. 消息校對機制。如果盡最大努力也沒有通知到接收方,或者接收方消費消息后要再次消費,此時可由接收方主動向通知方查詢消息信息來滿足需求。

所以最大努力通知其實只是表明了一種柔性事務(wù)的思想:我已經(jīng)盡力我最大的努力想達成事務(wù)的最終一致了。

基于 MQ 的分布式事務(wù)方案其實是對本地消息表的封裝,將本地消息表基于 MQ 內(nèi)部,其他方面的協(xié)議基本與本地消息表一致。有一些第三方的MQ是支持事務(wù)消息的,比如RocketMQ,他們支持事務(wù)消息的方式也是類似于采用的二階段提交,但是市面上一些主流的MQ都是不支持事務(wù)消息的,比如 RabbitMQ 和 Kafka 都不支持。

流程分析

下面主要基于 RocketMQ 介紹 MQ 的分布式事務(wù)方案。

在本地消息表方案中,保證事務(wù)主動方發(fā)寫業(yè)務(wù)表數(shù)據(jù)和寫消息表數(shù)據(jù)的一致性是基于數(shù)據(jù)庫事務(wù),RocketMQ 的事務(wù)消息相對于普通 MQ,相當于提供了 2PC 的提交接口,第一階段Prepared消息,會拿到消息的地址。 第二階段執(zhí)行本地事務(wù),第三階段通過第一階段拿到的地址去訪問消息,并修改狀態(tài)。方案如下:

  • 正常情況:事務(wù)主動方發(fā)消息
MQ事務(wù)-事務(wù)主動方發(fā)消息

這種情況下,事務(wù)主動方服務(wù)正常,沒有發(fā)生故障,發(fā)消息流程如下:

  1. 發(fā)送方向 MQ 服務(wù)端(MQ Server)發(fā)送事務(wù)消息即半消息(half),半消息不是說一半消息,而是這個消息對消費者來說不可見(圖中 1)。

  2. MQ Server 將消息持久化成功之后,向發(fā)送方 ack 確認消息已經(jīng)發(fā)送成功(圖中 2)。

  3. 發(fā)送方開始執(zhí)行本地事務(wù)邏輯(圖中 3)。

  4. 發(fā)送方根據(jù)本地事務(wù)執(zhí)行結(jié)果向 MQ Server 提交二次確認(commit 或是 rollback)(圖中 4)。

  5. MQ Server 收到 commit 狀態(tài)則將半消息標記為可投遞,訂閱方最終將收到該消息;MQ Server 收到 rollback 狀態(tài)則刪除半消息,訂閱方將不會接受該消息(圖中 5)。

  • 異常情況:事務(wù)主動方消息恢復
MQ事務(wù)-事務(wù)主動方消息恢復

有斷網(wǎng)或者應(yīng)用重啟等異常情況,流程如下:

  1. 提交的二次確認超時未到達 MQ Server(圖中 4)。

  2. MQ Server 對該消息發(fā)起消息回查(圖中 5)。

  3. 發(fā)送方收到消息回查后,需要檢查對應(yīng)消息的本地事務(wù)執(zhí)行的最終結(jié)果(圖中 6)。

  4. 發(fā)送方根據(jù)檢查得到的本地事務(wù)的最終狀態(tài)再次提交二次確認(圖中 7)。

  5. MQ Server基于 commit/rollback 對消息進行投遞或者刪除(圖中 8)。

介紹完 RocketMQ 的事務(wù)消息方案后,由于前面已經(jīng)介紹過本地消息表方案,這里就簡單介紹 RocketMQ 分布式事務(wù):

MQ分布式事務(wù)

方案小結(jié)

最大努力通知與可靠消息一致性有什么不同?

  1. 解決方案思想不同

    可靠消息一致性,發(fā)起通知方需要保證將消息發(fā)出去,并且將消息發(fā)到接收通知方,消息的可靠性關(guān)鍵由發(fā)起通知方來保證。最大努力通知,發(fā)起通知方盡最大的努力將業(yè)務(wù)處理結(jié)果通知為接收通知方,但是可能消息接收不到,此時需要接 收通知方主動調(diào)用發(fā)起通知方的接口查詢業(yè)務(wù)處理結(jié)果,通知的可靠性關(guān)鍵在接收通知方。

  2. 兩者的業(yè)務(wù)應(yīng)用場景不同

    可靠消息一致性關(guān)注的是交易過程的事務(wù)一致,以異步的方式完成交易。最大努力通知關(guān)注的是交易后的通知事務(wù),即將交易結(jié)果可靠的通知出去。

  3. 技術(shù)解決方向不同

    可靠消息一致性要解決消息從發(fā)出到接收的一致性,即消息發(fā)出并且被接收到。最大努力通知無法保證消息從發(fā)出到接收的一致性,只提供消息接收的可靠性機制??煽繖C制是,最大努力的將消息通知給接收方,當消息無法被接收方接收時,由接收方主動查詢消息(業(yè)務(wù)處理結(jié)果)

MQ事務(wù)消息方案相比本地消息表方案,其優(yōu)點是:

  • 消息數(shù)據(jù)獨立存儲 ,降低業(yè)務(wù)系統(tǒng)與消息系統(tǒng)之間的耦合。

  • 系統(tǒng)吞吐量高于本地消息表方案。

缺點是:

  • 一次消息發(fā)送需要兩次網(wǎng)絡(luò)請求(half 消息 + commit/rollback 消息)。

  • 業(yè)務(wù)處理服務(wù)需要實現(xiàn)消息狀態(tài)回查接口。

Saga 事務(wù)

方案簡介

Saga 事務(wù)源于 1987 年普林斯頓大學的 Hecto 和 Kenneth 發(fā)表的如何處理 long lived transaction(長活事務(wù))論文。

Saga 事務(wù)核心思想是將長事務(wù)拆分為多個本地短事務(wù)組成,由 Saga 事務(wù)協(xié)調(diào)器協(xié)調(diào),每個本地事務(wù)有相應(yīng)的執(zhí)行模塊和補償模塊,如果正常結(jié)束那就正常完成,當 Saga 事務(wù)中的任意一個本地事務(wù)出錯了, 可以根據(jù)相反順序調(diào)用相關(guān)事務(wù)對應(yīng)的補償方法恢復,達到事務(wù)的最終一致性。在服務(wù)請求的過程中,可能會出現(xiàn)超時重試的情況,需要通過冪等來避免多次請求所帶來的問題。

ACID與Saga

ACID (剛性事務(wù)) Saga 只提供ACD保證
原子性(Atomicity) 原子性(通過Saga協(xié)調(diào)器實現(xiàn))
一致性(Consistency) 一致性(本地事務(wù) + Saga Log)
隔離性(Isolation) 隔離性(Saga 不保證)
持久性(Durability) 持久性(Saga Log)

流程分析

Saga 事務(wù)基本協(xié)議如下:

  • 每個 Saga 事務(wù)由一系列冪等的有序子事務(wù)(sub-transaction) Ti 組成。

  • 每個 Ti 都有對應(yīng)的冪等補償動作 Ci,補償動作用于撤銷 Ti 造成的結(jié)果。

可以看到,和 TCC 相比,Saga 沒有“預留”動作,它的 Ti 就是直接提交到庫。

Saga 的執(zhí)行順序有兩種,如下圖。下面以下單流程為例,整個操作包括:創(chuàng)建訂單、扣減庫存、支付、增加積分。

Saga-執(zhí)行順序
  • 事務(wù)正常執(zhí)行完成:T1, T2, T3, ..., Tn,例如:扣減庫存(T1),創(chuàng)建訂單(T2),支付(T3),依次有序完成整個事務(wù)。

  • 事務(wù)回滾:T1, T2, ..., Tj, Cj,..., C2, C1,其中 0 < j < n,例如:扣減庫存(T1),創(chuàng)建訂單(T2),支付(T3,支付失敗),支付回滾(C3),訂單回滾(C2),恢復庫存(C1)。

Saga 定義了兩種恢復策略:

  • 向前恢復(forward recovery):對應(yīng)于上面第一種執(zhí)行順序,適用于必須要成功的場景,發(fā)生失敗進行重試,執(zhí)行順序是類似于這樣的:T1, T2, ..., Tj(失敗), Tj(重試),..., Tn,其中j是發(fā)生錯誤的子事務(wù)(sub-transaction)。該情況下不需要Ci。如下圖:
Saga-向前恢復
  • 向后恢復(backward recovery):對應(yīng)于上面提到的第二種執(zhí)行順序,其中 j 是發(fā)生錯誤的子事務(wù)(sub-transaction),這種做法的效果是撤銷掉之前所有成功的子事務(wù),使得整個 Saga 的執(zhí)行結(jié)果撤銷。
Saga-向后恢復

Saga 事務(wù)常見的有兩種不同的實現(xiàn)方式:

  • 集中式的實現(xiàn)方式 - 命令協(xié)調(diào)(Order Orchestrator):中央?yún)f(xié)調(diào)器負責集中處理事件的決策和業(yè)務(wù)邏輯排序。

中央?yún)f(xié)調(diào)器(Orchestrator,簡稱 OSO)以命令/回復的方式與每項服務(wù)進行通信,全權(quán)負責告訴每個參與者該做什么以及什么時候該做什么。

以電商訂單的例子為例:

  1. 事務(wù)發(fā)起方的主業(yè)務(wù)邏輯請求 OSO 服務(wù)開啟訂單事務(wù)

  2. OSO 向庫存服務(wù)請求扣減庫存,庫存服務(wù)回復處理結(jié)果。

  3. OSO 向訂單服務(wù)請求創(chuàng)建訂單,訂單服務(wù)回復創(chuàng)建結(jié)果。

  4. OSO 向支付服務(wù)請求支付,支付服務(wù)回復處理結(jié)果。

  5. 主業(yè)務(wù)邏輯接收并處理 OSO 事務(wù)處理結(jié)果回復。

Saga-命令協(xié)調(diào)

中央?yún)f(xié)調(diào)器必須事先知道執(zhí)行整個訂單事務(wù)所需的流程(例如通過讀取配置)。如果有任何失敗,它還負責通過向每個參與者發(fā)送命令來撤銷之前的操作來協(xié)調(diào)分布式的回滾。

基于中央?yún)f(xié)調(diào)器協(xié)調(diào)一切時,回滾要容易得多,因為協(xié)調(diào)器默認是執(zhí)行正向流程,回滾時只要執(zhí)行反向流程即可。

  • 分布式的實現(xiàn)方式 - 事件編排(Event Choreography0):沒有中央?yún)f(xié)調(diào)器(沒有單點風險)時,每個服務(wù)產(chǎn)生并觀察其他服務(wù)的事件,并決定是否應(yīng)采取行動。

在事件編排方法中,第一個服務(wù)執(zhí)行一個事務(wù),然后發(fā)布一個事件。該事件被一個或多個服務(wù)進行監(jiān)聽,這些服務(wù)再執(zhí)行本地事務(wù)并發(fā)布(或不發(fā)布)新的事件。

當最后一個服務(wù)執(zhí)行本地事務(wù)并且不發(fā)布任何事件時,意味著分布式事務(wù)結(jié)束,或者它發(fā)布的事件沒有被任何 Saga 參與者聽到都意味著事務(wù)結(jié)束。

以電商訂單的例子為例:

  1. 事務(wù)發(fā)起方的主業(yè)務(wù)邏輯發(fā)布開始訂單事件。

  2. 庫存服務(wù)監(jiān)聽開始訂單事件,扣減庫存,并發(fā)布庫存已扣減事件。

  3. 訂單服務(wù)監(jiān)聽庫存已扣減事件,創(chuàng)建訂單,并發(fā)布訂單已創(chuàng)建事件。

  4. 支付服務(wù)監(jiān)聽訂單已創(chuàng)建事件,進行支付,并發(fā)布訂單已支付事件。

  5. 主業(yè)務(wù)邏輯監(jiān)聽訂單已支付事件并處理。

Saga-事件編排

事件編排是實現(xiàn) Saga 模式的自然方式,它很簡單,容易理解,不需要太多的代碼來構(gòu)建。如果事務(wù)涉及 2 至 4 個步驟,則可能是非常合適的。

隔離性

由于 Saga 模型中只支持ACD,沒有 Prepare 階段,因此事務(wù)間不能保證隔離性。

當多個 Saga 事務(wù)操作同一資源時,就會產(chǎn)生數(shù)據(jù)語義不一致、更新丟失、臟數(shù)據(jù)讀取等問題。需要在業(yè)務(wù)層控制并發(fā),解決方案如下:

  • 在應(yīng)用層面加入邏輯鎖。

  • Session層面隔離保證串行化操作。

  • 業(yè)務(wù)層面預先凍結(jié)資源數(shù)據(jù)。

  • 業(yè)務(wù)操作過程中通過及時讀取當前狀態(tài)的方式獲取更新。

方案小結(jié)

命令協(xié)調(diào)設(shè)計的優(yōu)點如下:

  • 服務(wù)之間關(guān)系簡單,避免服務(wù)之間的循環(huán)依賴關(guān)系,因為 Saga 協(xié)調(diào)器會調(diào)用 Saga 參與者,但參與者不會調(diào)用協(xié)調(diào)器。

  • 程序開發(fā)簡單,只需要執(zhí)行命令/回復(其實回復消息也是一種事件消息),降低參與者的復雜性。

  • 易維護擴展,在添加新步驟時,事務(wù)復雜性保持線性,回滾更容易管理,更容易實施和測試,易于監(jiān)控和協(xié)調(diào)。

命令協(xié)調(diào)設(shè)計缺點如下:

  • 中央?yún)f(xié)調(diào)器容易處理邏輯容易過于復雜,導致難以維護。

  • 存在協(xié)調(diào)器單點故障風險。

事件編排設(shè)計優(yōu)點如下:

  • 避免中央?yún)f(xié)調(diào)器單點故障風險。

  • 當涉及的步驟較少服務(wù)開發(fā)簡單,容易實現(xiàn)。

事件/編排設(shè)計缺點如下:

  • 服務(wù)之間存在循環(huán)依賴的風險。

  • 當涉及的步驟較多,服務(wù)間關(guān)系混亂,難以追蹤調(diào)測。

Seata 方案

方案簡介

Seata 是由阿里中間件團隊發(fā)起的開源項目 Fescar,后更名為 Seata,它是一個是開源的分布式事務(wù)框架。

傳統(tǒng) 2PC 的問題在 Seata 中得到了解決,它通過對本地關(guān)系數(shù)據(jù)庫的分支事務(wù)的協(xié)調(diào)來驅(qū)動完成全局事務(wù),是工作在應(yīng)用層的中間件。主要優(yōu)點是性能較好,且不長時間占用連接資源,它以高效并且對業(yè)務(wù) 0 侵入的方式解決微服務(wù)場景下面臨的分布式事務(wù)問題,它目前提供 AT 模式(即 2PC)及 TCC 模式的分布式事務(wù)解決方案。

設(shè)計思想

Seata 的設(shè)計目標其一是對業(yè)務(wù)無侵入,因此從業(yè)務(wù)無侵入的 2PC 方案著手,在傳統(tǒng) 2PC的基礎(chǔ)上演進,并解決 2PC 方案面臨的問題。

Seata 把一個分布式事務(wù)理解成一個包含了若干分支事務(wù)全局事務(wù)。全局事務(wù)的職責是協(xié)調(diào)其下管轄的分支事務(wù)達成一致,要么一起成功提交,要么一起失敗回滾。此外,通常分支事務(wù)本身就是一個關(guān)系數(shù)據(jù)庫的本地事務(wù),下圖是全局事務(wù)與分支事務(wù)的關(guān)系圖:

seata1

與傳統(tǒng) 2PC 的模型類似,Seata 定義了 3 個組件來協(xié)議分布式事務(wù)的處理過程:

seata2
  • Transaction Coordinator(TC):事務(wù)協(xié)調(diào)器,它是獨立的中間件,需要獨立部署運行,它維護全局事務(wù)的運行狀態(tài),接收 TM 指令發(fā)起全局事務(wù)的提交與回滾,負責與 RM 通信協(xié)調(diào)各各分支事務(wù)的提交或回滾。

  • Transaction Manager(TM): 事務(wù)管理器,TM 需要嵌入應(yīng)用程序中工作,它負責開啟一個全局事務(wù),并最終向 TC 發(fā)起全局提交或全局回滾的指令。

  • Resource Manager(RM):控制分支事務(wù),負責分支注冊、狀態(tài)匯報,并接收事務(wù)協(xié)調(diào)器 TC 的指令,驅(qū)動分支(本地)事務(wù)的提交和回滾。

流程分析

拿新用戶注冊送積分舉例,簡單分析Seata的分布式事務(wù)過程:

seata3
  1. 用戶服務(wù)的 TM 向 TC 申請開啟一個全局事務(wù),全局事務(wù)創(chuàng)建成功并生成一個全局唯一的 XID。

  2. 用戶服務(wù)的 RM 向 TC 注冊分支事務(wù),該分支事務(wù)在用戶服務(wù)執(zhí)行新增用戶邏輯,并將其納入 XID 對應(yīng)全局事務(wù)的管轄。

  3. 用戶服務(wù)執(zhí)行分支事務(wù),向用戶表插入一條記錄。

  4. 邏輯執(zhí)行到遠程調(diào)用積分服務(wù)時(XID 在微服務(wù)調(diào)用鏈路的上下文中傳播)。積分服務(wù)的 RM 向 TC 注冊分支事務(wù),該分支事務(wù)執(zhí)行增加積分的邏輯,并將其納入 XID 對應(yīng)全局事務(wù)的管轄。

  5. 積分服務(wù)執(zhí)行分支事務(wù),向積分記錄表插入一條記錄,執(zhí)行完畢后,返回用戶服務(wù)。

  6. 用戶服務(wù)分支事務(wù)執(zhí)行完畢。

  7. TM 向 TC 發(fā)起針對 XID 的全局提交或回滾決議。

  8. TC 調(diào)度 XID 下管轄的全部分支事務(wù)完成提交或回滾請求。

方案小結(jié)

Seata方案與傳統(tǒng) 2PC 方案對比,有以下優(yōu)點:

  • 架構(gòu)層次方面:傳統(tǒng) 2PC 方案的 RM 實際上是在數(shù)據(jù)庫層,RM 本質(zhì)上就是數(shù)據(jù)庫自身,通過 XA 協(xié)議實現(xiàn),而 Seata 的 RM 是以 jar 包的形式作為中間件層部署在應(yīng)用程序這一側(cè)的。

  • 二階段提交方面:傳統(tǒng) 2PC無論提交階段的決議是 commit 還是 rollback ,事務(wù)性資源的鎖都要保持到 Phase2 完成才釋放。而 Seata 的做法是在 Phase1 就將本地事務(wù)提交,這樣就可以省去 Phase2 持鎖的時間,整體提高效率。

由于 Seata 的 0 侵入性并且解決了傳統(tǒng) 2PC 長期鎖資源的問題,推薦采用 Seata 實現(xiàn) 2PC。

總結(jié)

方案對比

19

各方案使用場景

介紹完分布式事務(wù)相關(guān)理論和常見解決方案后,最終的目的在實際項目中運用,因此,總結(jié)一下各個方案的常見的使用場景:

  • 2PC/3PC:依賴于數(shù)據(jù)庫,能夠很好的提供強一致性和強事務(wù)性,但相對來說延遲比較高,比較適合傳統(tǒng)的單體應(yīng)用,在同一個方法中存在跨庫操作的情況,不適合高并發(fā)和高性能要求的場景。

  • TCC:適用于執(zhí)行時間確定且較短,實時性要求高,對數(shù)據(jù)一致性要求高,比如互聯(lián)網(wǎng)金融企業(yè)最核心的三個服務(wù):交易、支付、賬務(wù)。

  • 本地消息表/MQ 事務(wù):都適用于事務(wù)中參與方支持操作冪等,對一致性要求不高,業(yè)務(wù)上能容忍數(shù)據(jù)不一致到一個人工檢查周期,事務(wù)涉及的參與方、參與環(huán)節(jié)較少,業(yè)務(wù)上有對賬/校驗系統(tǒng)兜底。

  • Saga 事務(wù):由于 Saga 事務(wù)不能保證隔離性,需要在業(yè)務(wù)層控制并發(fā),適合于業(yè)務(wù)場景事務(wù)并發(fā)操作同一資源較少的情況。 Saga 相比缺少預提交動作,導致補償動作的實現(xiàn)比較麻煩,例如業(yè)務(wù)是發(fā)送短信,補償動作則得再發(fā)送一次短信說明撤銷,用戶體驗比較差。Saga 事務(wù)較適用于補償動作容易處理的場景。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 背景 分布式事務(wù)是企業(yè)集成中的一個技術(shù)難點,也是每一個分布式系統(tǒng)架構(gòu)中都會涉及到的一個東西,特別是在微服務(wù)架構(gòu)中,...
    xiaotian是個混子閱讀 614評論 0 1
  • 眾所周知,數(shù)據(jù)庫能實現(xiàn)本地事務(wù),也就是在同一個數(shù)據(jù)庫中,你可以允許一組操作要么全都正確執(zhí)行,要么全都不執(zhí)行。這里特...
    martin4096閱讀 234評論 0 0
  • 轉(zhuǎn)自:https://www.toutiao.com/i6717432946736759299/?tt_from=...
    AnyL8023閱讀 514評論 0 1
  • 推薦指數(shù): 6.0 書籍主旨關(guān)鍵詞:特權(quán)、焦點、注意力、語言聯(lián)想、情景聯(lián)想 觀點: 1.統(tǒng)計學現(xiàn)在叫數(shù)據(jù)分析,社會...
    Jenaral閱讀 5,950評論 0 5
  • 昨天,在回家的路上,坐在車里悠哉悠哉地看著三毛的《撒哈拉沙漠的故事》,我被里面的內(nèi)容深深吸引住了,盡管上學時...
    夜闌曉語閱讀 3,921評論 2 9

友情鏈接更多精彩內(nèi)容