分布式事務(wù)

事務(wù)與分布式事務(wù)

事務(wù)是將一組操作作為一個(gè)不可分割的執(zhí)行單元,組成事務(wù)的所有操作要么全部成功,事務(wù)提交;要么某一操作執(zhí)行失敗,整個(gè)事務(wù)的回滾。簡(jiǎn)單地說(shuō),事務(wù)提供一種“要么什么都不做,要么做全套(All or Nothing)”的機(jī)制。事務(wù)具有原子性(Atomicity)、一致性(Consistency)、隔離性(Isolatio)和持久性(durability)等特點(diǎn), 簡(jiǎn)稱ACID。

傳統(tǒng)單機(jī)應(yīng)用部署在一臺(tái)機(jī)器上,事務(wù)的所有操作都在一個(gè)實(shí)例內(nèi),可以通過(guò)關(guān)系型數(shù)據(jù)庫(kù)(如MySQL)提供事務(wù)支持。但是在分布式環(huán)境下,事務(wù)參與者、支持事務(wù)的服務(wù)器、資源服務(wù)器以及事務(wù)管理器分別位于分布式系統(tǒng)的不同節(jié)點(diǎn)之上,就需要分布式事務(wù)來(lái)保證這些操作要么一起成功,要么一起失敗。

提到分布式事務(wù),就不得不說(shuō)CAP理論:

  • C (一致性):對(duì)某個(gè)指定的客戶端來(lái)說(shuō),讀操作能返回最新的寫操作的結(jié)果。
  • A (可用性):非故障的節(jié)點(diǎn)在合理的時(shí)間內(nèi)返回合理的響應(yīng)(不是錯(cuò)誤或超時(shí)的響應(yīng))。
  • P (分區(qū)容錯(cuò)性):當(dāng)出現(xiàn)網(wǎng)絡(luò)分區(qū)后,系統(tǒng)能夠繼續(xù)工作。

分布式系統(tǒng)中,網(wǎng)絡(luò)無(wú)法100%可靠,分區(qū)其實(shí)是一個(gè)必然現(xiàn)象,如果我們選擇了CA而放棄了P,那么當(dāng)發(fā)生分區(qū)現(xiàn)象時(shí),為了保證一致性,這個(gè)時(shí)候必須拒絕請(qǐng)求,但是A又不允許,所以分布式系統(tǒng)理論上不可能選擇CA架構(gòu),只能選擇CP或者AP架構(gòu)。常用的Zookeeper就是保證CP, 而Eureka是保證AP。

由于無(wú)法同時(shí)滿足CAP,因此引出了BASE理論(Basically Available基本可用、Soft state軟狀態(tài)和 Eventually consistent 最終一致性 三個(gè)短語(yǔ)的縮寫),是對(duì)CAP中AP的一個(gè)擴(kuò)展。所以分布式事務(wù)都是追求最終一致性,利用重試、補(bǔ)償?shù)确绞?,保證事務(wù)要么提交、要么回滾。分布式算法有2PC(/Open XA)、TCC(Try-Confirm-Cancel)、本地消息表、MQ事務(wù)等,下面重點(diǎn)對(duì)幾個(gè)常用的方案進(jìn)行展開(kāi)。

1. TCC

關(guān)于TCC(Try-Confirm-Cancel)的概念,最早是由Pat Helland于2007年發(fā)表的一篇名為《Life beyond Distributed Transactions:an Apostate’s Opinion》的論文提出的。目前開(kāi)源方案有ByteTCC、 tcc-transaction、seata,具體代碼可查看官方示例。其思想是將業(yè)務(wù)分為try與confirm/cancel兩階段完成,下面以“賬戶A給賬戶B轉(zhuǎn)賬30元”為例進(jìn)行說(shuō)明,具體可參考下圖:

  • Try 操作:資源的檢查和預(yù)留。
    Try 操作要做的事情就是先檢查 A 賬戶的余額是否充足,再凍結(jié)要扣款的 30 元(預(yù)留資源),此階段不會(huì)發(fā)生真正地扣款。
  • Confirm 操作:執(zhí)行真正業(yè)務(wù)的提交。
    Confirm 階段做的事情就是發(fā)生真正地扣款,把 A 賬戶中已經(jīng)凍結(jié)的 30 元錢扣掉。
  • Cancel 操作:預(yù)留資源的釋放。
    如果下游服務(wù)有異常,扣款取消,Cancel 操作執(zhí)行的任務(wù)是釋放 Try 操作凍結(jié)的 30 元錢,使 A 賬戶回到初始狀態(tài)。
圖片來(lái)源于《螞蟻金服分布式事務(wù)開(kāi)源以及實(shí)踐》

使用TCC方案需要注意:

  1. 并發(fā)控制
    用戶在實(shí)現(xiàn) TCC 時(shí),應(yīng)當(dāng)考慮并發(fā)性問(wèn)題,將鎖的粒度降到最低,以最大限度提高分布式事務(wù)的并發(fā)性。
    以下還是以 A 賬戶扣款為例,“賬戶 A 上有 100 元,事務(wù) T1 要扣除其中的 30 元,事務(wù) T2 也要扣除 30 元,出現(xiàn)并發(fā)”。在一階段 Try 操作中,分布式事務(wù) T1 和分布式事務(wù) T2 分別凍結(jié)資金的那一部分資金,相互之間無(wú)干擾。這樣在分布式事務(wù)的二階段,無(wú)論 T1 是提交還是回滾,都不會(huì)對(duì) T2 產(chǎn)生影響,這樣 T1 和 T2 可以在同一筆業(yè)務(wù)數(shù)據(jù)上并行執(zhí)行。
并發(fā)控制,圖片來(lái)源于《螞蟻金服分布式事務(wù)開(kāi)源以及實(shí)踐》
  1. 允許空回滾
    如下圖所示,事務(wù)協(xié)調(diào)器在調(diào)用 TCC 服務(wù)的一階段 Try 操作時(shí),可能會(huì)出現(xiàn)因?yàn)閬G包而導(dǎo)致的網(wǎng)絡(luò)超時(shí)。此時(shí)事務(wù)管理器會(huì)觸發(fā)二階段回滾,調(diào)用 TCC 服務(wù)的 Cancel 操作,而 Cancel 操作調(diào)用未出現(xiàn)超時(shí)。
    TCC 服務(wù)在未收到 Try 請(qǐng)求的情況下收到 Cancel 請(qǐng)求,這種場(chǎng)景被稱為空回滾。空回滾在生產(chǎn)環(huán)境經(jīng)常出現(xiàn),用戶在實(shí)現(xiàn) TCC 服務(wù)時(shí),應(yīng)允許空回滾的執(zhí)行,即收到空回滾時(shí)返回成功。
空回滾出現(xiàn)場(chǎng)景,圖片來(lái)源于《螞蟻金服分布式事務(wù)開(kāi)源以及實(shí)踐》
  1. 防懸掛控制
    如下圖所示,事務(wù)協(xié)調(diào)器在調(diào)用 TCC 服務(wù)的一階段 Try 操作時(shí),可能會(huì)出現(xiàn)因網(wǎng)絡(luò)擁堵而導(dǎo)致的超時(shí)。此時(shí)事務(wù)管理器會(huì)觸發(fā)二階段回滾,調(diào)用 TCC 服務(wù)的 Cancel 操作,Cancel 調(diào)用未超時(shí)。在此之后,擁堵在網(wǎng)絡(luò)上的一階段 Try 數(shù)據(jù)包被 TCC 服務(wù)收到,出現(xiàn)二階段 Cancel 請(qǐng)求比一階段 Try 請(qǐng)求先執(zhí)行的情況,此 TCC 服務(wù)在執(zhí)行晚到的 Try 之后,將永遠(yuǎn)不會(huì)再收到二階段的 Confirm 或者 Cancel,造成 TCC 服務(wù)懸掛。
    用戶在實(shí)現(xiàn) TCC 服務(wù)時(shí),要允許空回滾,但是要拒絕執(zhí)行空回滾之后 Try 請(qǐng)求,要避免出現(xiàn)懸掛。
出現(xiàn)懸掛場(chǎng)景,圖片來(lái)源于《螞蟻金服分布式事務(wù)開(kāi)源以及實(shí)踐》
  1. 冪等控制
    無(wú)論是網(wǎng)絡(luò)數(shù)據(jù)包重傳,還是異常事務(wù)的補(bǔ)償執(zhí)行,都會(huì)導(dǎo)致 TCC 服務(wù)的 Try、Confirm 或者 Cancel 操作被重復(fù)執(zhí)行;用戶在實(shí)現(xiàn) TCC 服務(wù)時(shí),需要考慮冪等控制,即 Try、Confirm、Cancel 執(zhí)行一次和執(zhí)行多次的業(yè)務(wù)結(jié)果是一樣的。
冪等控制,圖片來(lái)源于《螞蟻金服分布式事務(wù)開(kāi)源以及實(shí)踐》

: 更多地可以參考螞蟻金服分布式事務(wù)開(kāi)源以及實(shí)踐, 本章節(jié)也主要是參考這篇文章。

2. 本地消息表

本地消息表這個(gè)方案最初是由Ebay提出的完整方案In partitioned databases, trading some consistency for availability can lead to dramatic improvements in scalability.。此方案的核心是將需要分布式處理的任務(wù)通過(guò)消息日志的方式來(lái)異步執(zhí)行。消息日志可以存儲(chǔ)到本地文本、數(shù)據(jù)庫(kù)或消息隊(duì)列,再通過(guò)業(yè)務(wù)規(guī)則自動(dòng)或人工發(fā)起重試。人工重試更多的是應(yīng)用于支付場(chǎng)景,通過(guò)對(duì)賬系統(tǒng)對(duì)事后問(wèn)題的處理。

圖片來(lái)源于《再有人問(wèn)你分布式事務(wù),把這篇扔給他》

對(duì)于本地消息表其核心思想是保證消費(fèi)者至少發(fā)送一次消息(重試機(jī)制),消費(fèi)者至多處理一次(冪等機(jī)制)。仍以“賬戶A給賬戶B轉(zhuǎn)賬30元”為例

  1. 上游服務(wù)在執(zhí)行賬戶A扣款時(shí),需要新增一個(gè)本地消息表,將“數(shù)據(jù)庫(kù)中賬戶A的余額減去30元”和“保存發(fā)送給下游服務(wù)的消息(將B賬戶增加30元)到本地消息表”作為一個(gè)事務(wù)來(lái)執(zhí)行,通過(guò)數(shù)據(jù)庫(kù)的事務(wù)來(lái)實(shí)現(xiàn);
  2. 定時(shí)查詢本地消息表,將沒(méi)有成功發(fā)送的消息發(fā)送到MQ上。
  • 如果已經(jīng)發(fā)送成功,將對(duì)應(yīng)的消息標(biāo)為已發(fā)送;
  • 如果消息未成功發(fā)送,下次定時(shí)任務(wù)依舊會(huì)將該消息再次發(fā)送,直到發(fā)送成功;
  1. 下游服務(wù)在接收到消息后,將B賬戶余額增加30元,并告訴MQ該消息已經(jīng)處理,否則MQ會(huì)再次重發(fā)消息直到消息被成功處理,因此下游服務(wù)要做冪等處理。

MQ事務(wù)

Rocket MQ4.3.0版本增加了對(duì)事務(wù)消息的支持,實(shí)際上是對(duì)本地消息表的封裝,具體代碼示例可以參考transaction-example,實(shí)現(xiàn)原理是兩階段提交,下面仍以“賬戶A給賬戶B轉(zhuǎn)賬30元”為例進(jìn)行說(shuō)明:

  1. 預(yù)提交
    上游服務(wù)發(fā)送個(gè)Half Message給MQ的Brock端,消息中攜帶“將B賬戶增加30元”的消息。
    所謂的半消息是指該消息會(huì)持久化在Rocket MQ的broker中,但不會(huì)被消費(fèi)者消費(fèi)
    當(dāng)上游服務(wù)知道Half Message發(fā)送成功后,那么開(kāi)始第2步執(zhí)行本地事務(wù)。
    執(zhí)行本地事務(wù)會(huì)有三種情況1.執(zhí)行成功。2.執(zhí)行失敗。3,網(wǎng)絡(luò)等原因?qū)е聸](méi)有響應(yīng)
  2. Commit
    如果本地事務(wù)成功,那么上游服務(wù)向Brock服務(wù)器發(fā)送Commit信號(hào), 這樣下游服務(wù)就可以消費(fèi)之前半消息。
  3. Rollback
    如果本地事務(wù)失敗,那么上有服務(wù)向Brock服務(wù)器發(fā)送Rollback信號(hào), 那么就會(huì)直接刪除之前的半消息。
  4. 定時(shí)回查
    如果因?yàn)榫W(wǎng)絡(luò)等原因遲遲沒(méi)有返回失敗還是成功,那么會(huì)執(zhí)行RocketMQ的回調(diào)接口,來(lái)進(jìn)行事務(wù)的回查。
    具體過(guò)程如下圖所示, 其中下游服務(wù)在處理完消息之后,提交Ack信號(hào)給broker,broker不會(huì)再重發(fā)這個(gè)消息, 否則會(huì)不停地重試,因此下游服務(wù)也需要做冪等處理。
    圖片來(lái)源于《RocketMQ 4.3 正式發(fā)布,支持分布式事務(wù)》

總結(jié)

本文主要闡述了TCC與本地消息表(MQ事務(wù)) 兩種主要的分布式事務(wù)實(shí)現(xiàn)方式, 其實(shí)現(xiàn)原理都是對(duì)于分布式事務(wù)相關(guān)的兩個(gè)服務(wù)或多個(gè)服務(wù),通過(guò)重試機(jī)制,上游服務(wù)至少通知下游服務(wù)一次, 下游服務(wù)通過(guò)冪等處理,保證最多只處理一次。
兩者的主要區(qū)別在于:

  1. TCC
  • 優(yōu)點(diǎn): 實(shí)時(shí)性較高、服務(wù)下游如果因?yàn)橘Y源不足(比如庫(kù)存不足),服務(wù)上游會(huì)知道的,并且服務(wù)上游會(huì)立即失敗
  • 缺點(diǎn): 對(duì)代碼的侵入較高,原來(lái)一次執(zhí)行的,現(xiàn)在需要分兩階段執(zhí)行
  1. 本地消息表
  • 優(yōu)點(diǎn): 實(shí)現(xiàn)簡(jiǎn)單
  • 缺點(diǎn): 實(shí)時(shí)性較低,且上游服務(wù)很難知道下游服務(wù)的執(zhí)行情況,一旦消息提交了,下游服務(wù)會(huì)不停地重試,直到能夠處理完消息。但對(duì)于下游服務(wù)如果,因?yàn)閹?kù)存不足而執(zhí)行失敗,理論上上游服務(wù)也不應(yīng)該執(zhí)行成功,因此這種情況并不適合。

需要注意的是無(wú)論是TCC還是通過(guò)本地消息表來(lái)實(shí)現(xiàn)分布式事務(wù),都需要有對(duì)賬機(jī)制進(jìn)行兜底。且在使用分布式事務(wù)之前,需要考慮清楚是否真的需要分布式事務(wù),不要為了使用而使用。

本文是站在巨人的肩膀上對(duì)分布式事務(wù)進(jìn)行的總結(jié),參考了包括并不限于以下文章,在此一并感謝!

參考:

  1. 再有人問(wèn)你分布式事務(wù),把這篇扔給他
  2. 螞蟻金服分布式事務(wù)開(kāi)源以及實(shí)踐
  3. 布式事務(wù)(3)—RocketMQ實(shí)現(xiàn)分布式事務(wù)原理
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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