欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

關(guān)于MySQL與Golan分布式事務(wù)經(jīng)典的七種解決方案

 更新時(shí)間:2021年10月25日 09:15:40   作者:葉東富  
本文介紹了分布式事務(wù)的一些基礎(chǔ)理論,并對(duì)常用的分布式事務(wù)方案進(jìn)行了講解;在文章的后半部分還給出了事務(wù)異常的原因、分類以及優(yōu)雅的解決方案;最后以一個(gè)可運(yùn)行的分布式事務(wù)例子,將前面介紹的內(nèi)容以簡(jiǎn)短的程序進(jìn)行演示,需要的朋友可以參考一下文章具體內(nèi)容

前言:

隨著業(yè)務(wù)的快速發(fā)展、業(yè)務(wù)復(fù)雜度越來(lái)越高,幾乎每個(gè)公司的系統(tǒng)都會(huì)從單體走向分布式,特別是轉(zhuǎn)向微服務(wù)架構(gòu)。隨之而來(lái)就必然遇到分布式事務(wù)這個(gè)難題。
這篇文章首先介紹了相關(guān)的基礎(chǔ)理論,然后總結(jié)了最經(jīng)典的事務(wù)方案,最后給出了子事務(wù)亂序執(zhí)行(冪等、空補(bǔ)償、懸掛問題)的解決方案,分享給大家。

1、基礎(chǔ)理論

在講解具體方案之前,我們先了解一下分布式事務(wù)所涉及到的基礎(chǔ)理論知識(shí)。

我們拿轉(zhuǎn)賬作為例子,A需要轉(zhuǎn)100元給B,那么需要給A的余額-100元,給B的余額+100元,整個(gè)轉(zhuǎn)賬要保證,A-100和B+100同時(shí)成功,或者同時(shí)失敗。看看在各種場(chǎng)景下,是如何解決這個(gè)問題的。

1.1 事務(wù)

把多條語(yǔ)句作為一個(gè)整體進(jìn)行操作的功能,被稱為數(shù)據(jù)庫(kù)事務(wù)。數(shù)據(jù)庫(kù)事務(wù)可以確保該事務(wù)范圍內(nèi)的所有操作都可以全部成功或者全部失敗。

事務(wù)具有 4 個(gè)屬性:原子性、一致性、隔離性持久性。這四個(gè)屬性通常稱為 ACID 特性。

  • Atomicity(原子性):一個(gè)事務(wù)中的所有操作,要么全部完成,要么全部不完成,不會(huì)結(jié)束在中間某個(gè)環(huán)節(jié)。事務(wù)在執(zhí)行過(guò)程中發(fā)生錯(cuò)誤,會(huì)被恢復(fù)到事務(wù)開始前的狀態(tài),就像這個(gè)事務(wù)從來(lái)沒有執(zhí)行過(guò)一樣。
  • Consistency(一致性):在事務(wù)開始之前和事務(wù)結(jié)束以后,數(shù)據(jù)庫(kù)的完整性沒有被破壞。完整性包括外鍵約束、應(yīng)用定義的等約束不會(huì)被破壞。
  • Isolation(隔離性):數(shù)據(jù)庫(kù)允許多個(gè)并發(fā)事務(wù)同時(shí)對(duì)其數(shù)據(jù)進(jìn)行讀寫和修改的能力,隔離性可以防止多個(gè)事務(wù)并發(fā)執(zhí)行時(shí)由于交叉執(zhí)行而導(dǎo)致數(shù)據(jù)的不一致。
  • Durability(持久性):事務(wù)處理結(jié)束后,對(duì)數(shù)據(jù)的修改就是永久的,即便系統(tǒng)故障也不會(huì)丟失。

假如我們的業(yè)務(wù)系統(tǒng)不復(fù)雜,可以在一個(gè)數(shù)據(jù)庫(kù)、一個(gè)服務(wù)內(nèi)對(duì)數(shù)據(jù)進(jìn)行修改,完成轉(zhuǎn)賬,那么,我們可以利用數(shù)據(jù)庫(kù)事務(wù),保證轉(zhuǎn)賬業(yè)務(wù)的正確完成。

1.2 分布式事務(wù)

銀行跨行轉(zhuǎn)賬業(yè)務(wù)是一個(gè)典型分布式事務(wù)場(chǎng)景,假設(shè)A需要跨行轉(zhuǎn)賬給B,那么就涉及兩個(gè)銀行的數(shù)據(jù),無(wú)法通過(guò)一個(gè)數(shù)據(jù)庫(kù)的本地事務(wù)保證轉(zhuǎn)賬的ACID,只能夠通過(guò)分布式事務(wù)來(lái)解決。

分布式事務(wù)就是指事務(wù)的發(fā)起者、資源及資源管理器和事務(wù)協(xié)調(diào)者分別位于分布式系統(tǒng)的不同節(jié)點(diǎn)之上。在上述轉(zhuǎn)賬的業(yè)務(wù)中,用戶A-100操作和用戶B+100操作不是位于同一個(gè)節(jié)點(diǎn)上。本質(zhì)上來(lái)說(shuō),分布式事務(wù)就是為了保證在分布式場(chǎng)景下,數(shù)據(jù)操作的正確執(zhí)行。

分布式事務(wù)在分布式環(huán)境下,為了滿足可用性、性能與降級(jí)服務(wù)的需要,降低一致性與隔離性的要求,一方面遵循 BASE 理論(BASE相關(guān)理論,涉及內(nèi)容非常多,感興趣的同學(xué),可以參考BASE理論):

  • 基本業(yè)務(wù)可用性(Basic Availability
  • 柔性狀態(tài)(Soft state
  • 最終一致性(Eventual consistency

同樣的,分布式事務(wù)也部分遵循 ACID 規(guī)范:

  • 原子性:嚴(yán)格遵循
  • 一致性:事務(wù)完成后的一致性嚴(yán)格遵循;事務(wù)中的一致性可適當(dāng)放寬
  • 隔離性:并行事務(wù)間不可影響;事務(wù)中間結(jié)果可見性允許安全放寬
  • 持久性:嚴(yán)格遵循

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

由于分布式事務(wù)方案,無(wú)法做到完全的ACID的保證,沒有一種完美的方案,能夠解決掉所有業(yè)務(wù)問題。因此在實(shí)際應(yīng)用中,會(huì)根據(jù)業(yè)務(wù)的不同特性,選擇最適合的分布式事務(wù)方案。

2.1 兩階段提交/XA

XA是由X/Open組織提出的分布式事務(wù)的規(guī)范,XA規(guī)范主要定義了(全局)事務(wù)管理器(TM)和(局部)資源管理器(RM)之間的接口。本地的數(shù)據(jù)庫(kù)如mysql在XA中扮演的是RM角色

XA一共分為兩階段:

  • 第一階段(prepare):即所有的參與者RM準(zhǔn)備執(zhí)行事務(wù)并鎖住需要的資源。參與者ready時(shí),向TM報(bào)告已準(zhǔn)備就緒。
  • 第二階段 (commit/rollback):當(dāng)事務(wù)管理者(TM)確認(rèn)所有參與者(RM)都ready后,向所有參與者發(fā)送commit命令。

目前主流的數(shù)據(jù)庫(kù)基本都支持XA事務(wù),包括mysqloracle、sqlserver、postgre

XA 事務(wù)由一個(gè)或多個(gè)資源管理器(RM)、一個(gè)事務(wù)管理器(TM)和一個(gè)應(yīng)用程序(ApplicationProgram)組成。

這里的RM、TM、AP三個(gè)角色是經(jīng)典的角色劃分,會(huì)貫穿后續(xù)Saga、Tcc等事務(wù)模式。

把上面的轉(zhuǎn)賬作為例子,一個(gè)成功完成的XA事務(wù)時(shí)序圖如下:

如果有任何一個(gè)參與者prepare失敗,那么TM會(huì)通知所有完成prepare的參與者進(jìn)行回滾。

XA事務(wù)的特點(diǎn)是:

  • 簡(jiǎn)單易理解,開發(fā)較容易
  • 對(duì)資源進(jìn)行了長(zhǎng)時(shí)間的鎖定,并發(fā)度低

如果讀者想要進(jìn)一步研究XAgo語(yǔ)言以及PHP、Python、Java、C# 、Node等都可參考DTM

2.2 SAGA

Saga是這一篇數(shù)據(jù)庫(kù)論文sagas提到的一個(gè)方案。其核心思想是將長(zhǎng)事務(wù)拆分為多個(gè)本地短事務(wù),由Saga事務(wù)協(xié)調(diào)器協(xié)調(diào),如果正常結(jié)束那就正常完成,如果某個(gè)步驟失敗,則根據(jù)相反順序一次調(diào)用補(bǔ)償操作。

把上面的轉(zhuǎn)賬作為例子,一個(gè)成功完成的SAGA事務(wù)時(shí)序圖如下:

Saga一旦到了Cancel階段,那么Cancel在業(yè)務(wù)邏輯上是不允許失敗了。如果因?yàn)榫W(wǎng)絡(luò)或者其他臨時(shí)故障,導(dǎo)致沒有返回成功,那么TM會(huì)不斷重試,直到Cancel返回成功。

Saga事務(wù)的特點(diǎn):

  • 并發(fā)度高,不用像XA事務(wù)那樣長(zhǎng)期鎖定資源
  • 需要定義正常操作以及補(bǔ)償操作,開發(fā)量比XA大
  • 一致性較弱,對(duì)于轉(zhuǎn)賬,可能發(fā)生A用戶已扣款,最后轉(zhuǎn)賬又失敗的情況

論文里面的SAGA內(nèi)容較多,包括兩種恢復(fù)策略,包括分支事務(wù)并發(fā)執(zhí)行,我們這里的討論,僅包括最簡(jiǎn)單的SAGA

SAGA適用的場(chǎng)景較多,長(zhǎng)事務(wù)適用,對(duì)中間結(jié)果不敏感的業(yè)務(wù)場(chǎng)景適用

如果讀者想要進(jìn)一步研究SAGA,可參考DTM,里面包括了SAGA成功、失敗回滾的例子,還包括各類網(wǎng)絡(luò)異常的處理。

2.3 TCC

關(guān)于 TCC(Try-Confirm-Cancel)的概念,最早是由 Pat Helland 2007 年發(fā)表的一篇名為《Life beyond Distributed Transactions:an Apostate's Opinion》的論文提出。

TCC分為3個(gè)階段:

  • Try 階段:嘗試執(zhí)行,完成所有業(yè)務(wù)檢查(一致性), 預(yù)留必須業(yè)務(wù)資源(準(zhǔn)隔離性)
  • Confirm 階段:確認(rèn)執(zhí)行真正執(zhí)行業(yè)務(wù),不作任何業(yè)務(wù)檢查,只使用 Try 階段預(yù)留的業(yè)務(wù)資源,Confirm 操作要求具備冪等設(shè)計(jì),Confirm 失敗后需要進(jìn)行重試。
  • Cancel 階段:取消執(zhí)行,釋放 Try 階段預(yù)留的業(yè)務(wù)資源。Cancel 階段的異常和 Confirm 階段異常處理方案基本上一致,要求滿足冪等設(shè)計(jì)。

把上面的轉(zhuǎn)賬作為例子,通常會(huì)在Try里面凍結(jié)金額,但不扣款,Confirm里面扣款,Cancel里面解凍金額,

一個(gè)成功完成的TCC事務(wù)時(shí)序圖如下:

TCCConfirm/Cancel階段在業(yè)務(wù)邏輯上是不允許返回失敗的,如果因?yàn)榫W(wǎng)絡(luò)或者其他臨時(shí)故障,導(dǎo)致不能返回成功,TM會(huì)不斷的重試,直到Confirm/Cancel返回成功。

TCC特點(diǎn)如下:

  • 并發(fā)度較高,無(wú)長(zhǎng)期資源鎖定。
  • 開發(fā)量較大,需要提供Try/Confirm/Cancel接口。
  • 一致性較好,不會(huì)發(fā)生SAGA已扣款最后又轉(zhuǎn)賬失敗的情況
  • TCC適用于訂單類業(yè)務(wù),對(duì)中間狀態(tài)有約束的業(yè)務(wù)

如果讀者想要進(jìn)一步研究TCC,可參考DTM

2.4 本地消息表

本地消息表這個(gè)方案最初是 ebay 架構(gòu)師 Dan Pritchett 在 2008 年發(fā)表給 ACM 的文章。設(shè)計(jì)核心是將需要分布式處理的任務(wù)通過(guò)消息的方式來(lái)異步確保執(zhí)行。

大致流程如下:

寫本地消息和業(yè)務(wù)操作放在一個(gè)事務(wù)里,保證了業(yè)務(wù)和發(fā)消息的原子性,要么他們?nèi)汲晒?,要么全都失敗?/p>

容錯(cuò)機(jī)制:

  • 扣減余額事務(wù) 失敗時(shí),事務(wù)直接回滾,無(wú)后續(xù)步驟
  • 輪序生產(chǎn)消息失敗, 增加余額事務(wù)失敗都會(huì)進(jìn)行重試

本地消息表的特點(diǎn):

  • 長(zhǎng)事務(wù)僅需要分拆成多個(gè)任務(wù),使用簡(jiǎn)單
  • 生產(chǎn)者需要額外的創(chuàng)建消息表
  • 每個(gè)本地消息表都需要進(jìn)行輪詢
  • 消費(fèi)者的邏輯如果無(wú)法通過(guò)重試成功,那么還需要更多的機(jī)制,來(lái)回滾操作

適用于可異步執(zhí)行的業(yè)務(wù),且后續(xù)操作無(wú)需回滾的業(yè)務(wù)

2.5 事務(wù)消息

在上述的本地消息表方案中,生產(chǎn)者需要額外創(chuàng)建消息表,還需要對(duì)本地消息表進(jìn)行輪詢,業(yè)務(wù)負(fù)擔(dān)較重。阿里開源的RocketMQ 4.3之后的版本正式支持事務(wù)消息,該事務(wù)消息本質(zhì)上是把本地消息表放到RocketMQ上,解決生產(chǎn)端的消息發(fā)送與本地事務(wù)執(zhí)行的原子性問題。

事務(wù)消息發(fā)送及提交:

發(fā)送消息(half消息)
服務(wù)端存儲(chǔ)消息,并響應(yīng)消息的寫入結(jié)果
根據(jù)發(fā)送結(jié)果執(zhí)行本地事務(wù)(如果寫入失敗,此時(shí)half消息對(duì)業(yè)務(wù)不可見,本地邏輯不執(zhí)行)
根據(jù)本地事務(wù)狀態(tài)執(zhí)行Commit或者RollbackCommit操作發(fā)布消息,消息對(duì)消費(fèi)者可見)

正常發(fā)送的流程圖如下:

補(bǔ)償流程:

對(duì)沒有Commit/Rollback的事務(wù)消息(pending狀態(tài)的消息),從服務(wù)端發(fā)起一次“回查”
Producer收到回查消息,返回消息對(duì)應(yīng)的本地事務(wù)的狀態(tài),為Commit或者Rollback
事務(wù)消息方案與本地消息表機(jī)制非常類似,區(qū)別主要在于原先相關(guān)的本地表操作替換成了一個(gè)反查接口

事務(wù)消息特點(diǎn)如下:

  • 長(zhǎng)事務(wù)僅需要分拆成多個(gè)任務(wù),并提供一個(gè)反查接口,使用簡(jiǎn)單
  • 消費(fèi)者的邏輯如果無(wú)法通過(guò)重試成功,那么還需要更多的機(jī)制,來(lái)回滾操作

適用于可異步執(zhí)行的業(yè)務(wù),且后續(xù)操作無(wú)需回滾的業(yè)務(wù)

2.6 最大努力通知

發(fā)起通知方通過(guò)一定的機(jī)制最大努力將業(yè)務(wù)處理結(jié)果通知到接收方。具體包括:

有一定的消息重復(fù)通知機(jī)制。因?yàn)榻邮胀ㄖ娇赡軟]有接收到通知,此時(shí)要有一定的機(jī)制對(duì)消息重復(fù)通知。
消息校對(duì)機(jī)制。如果盡最大努力也沒有通知到接收方,或者接收方消費(fèi)消息后要再次消費(fèi),此時(shí)可由接收方主動(dòng)向通知方查詢消息信息來(lái)滿足需求。
前面介紹的的本地消息表和事務(wù)消息都屬于可靠消息,與這里介紹的最大努力通知有什么不同?

可靠消息一致性,發(fā)起通知方需要保證將消息發(fā)出去,并且將消息發(fā)到接收通知方,消息的可靠性關(guān)鍵由發(fā)起通知方來(lái)保證。

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

解決方案上,最大努力通知需要:

  • 提供接口,讓接受通知放能夠通過(guò)接口查詢業(yè)務(wù)處理結(jié)果
  • 消息隊(duì)列ACK機(jī)制,消息隊(duì)列按照間隔1min、5min、10min30min、1h、2h、5h、10h的方式,逐步拉大通知間隔 ,直到達(dá)到通知要求的時(shí)間窗口上限。之后不再通知

最大努力通知適用于業(yè)務(wù)通知類型,例如微信交易的結(jié)果,就是通過(guò)最大努力通知方式通知各個(gè)商戶,既有回調(diào)通知,也有交易查詢接口

2.7 AT事務(wù)模式

這是阿里開源項(xiàng)目seata中的一種事務(wù)模式,在螞蟻金服也被稱為FMT。優(yōu)點(diǎn)是該事務(wù)模式使用方式,類似XA模式,業(yè)務(wù)無(wú)需編寫各類補(bǔ)償操作,回滾由框架自動(dòng)完成,缺點(diǎn)也類似XA,存在較長(zhǎng)時(shí)間的鎖,不滿足高并發(fā)的場(chǎng)景。從性能的角度看,AT模式會(huì)比XA更高一些,但也帶來(lái)了臟回滾這樣的新問題。

3、異常處理

在分布式事務(wù)的各個(gè)環(huán)節(jié)都有可能出現(xiàn)網(wǎng)絡(luò)以及業(yè)務(wù)故障等問題,這些問題需要分布式事務(wù)的業(yè)務(wù)方做到防空回滾,冪等,防懸掛三個(gè)特性。

3.1 異常情況

下面以TCC事務(wù)說(shuō)明這些異常情況:

空回滾:

  在沒有調(diào)用 TCC 資源 Try 方法的情況下,調(diào)用了二階段的 Cancel 方法,Cancel 方法需要識(shí)別出這是一個(gè)空回滾,然后直接返回成功。

  出現(xiàn)原因是當(dāng)一個(gè)分支事務(wù)所在服務(wù)宕機(jī)或網(wǎng)絡(luò)異常,分支事務(wù)調(diào)用記錄為失敗,這個(gè)時(shí)候其實(shí)是沒有執(zhí)行Try階段,當(dāng)故障恢復(fù)后,分布式事務(wù)進(jìn)行回滾則會(huì)調(diào)用二階段的Cancel方法,從而形成空回滾。

冪等:

  由于任何一個(gè)請(qǐng)求都可能出現(xiàn)網(wǎng)絡(luò)異常,出現(xiàn)重復(fù)請(qǐng)求,所以所有的分布式事務(wù)分支,都需要保證冪等性

懸掛:

  懸掛就是對(duì)于一個(gè)分布式事務(wù),其二階段 Cancel 接口比 Try 接口先執(zhí)行。

  出現(xiàn)原因是在 RPC 調(diào)用分支事務(wù)try時(shí),先注冊(cè)分支事務(wù),再執(zhí)行RPC調(diào)用,如果此時(shí) RPC 調(diào)用的網(wǎng)絡(luò)發(fā)生擁堵,RPC 超時(shí)以后,TM就會(huì)通知RM回滾該分布式事務(wù),可能回滾完成后,Try 的 RPC 請(qǐng)求才到達(dá)參與者真正執(zhí)行。

下面看一個(gè)網(wǎng)絡(luò)異常的時(shí)序圖,更好的理解上述幾種問題

  • 業(yè)務(wù)處理請(qǐng)求4的時(shí)候,Cancel在Try之前執(zhí)行,需要處理空回滾
  • 業(yè)務(wù)處理請(qǐng)求6的時(shí)候,Cancel重復(fù)執(zhí)行,需要冪等
  • 業(yè)務(wù)處理請(qǐng)求8的時(shí)候,Try在Cancel后執(zhí)行,需要處理懸掛

面對(duì)上述復(fù)雜的網(wǎng)絡(luò)異常情況,目前看到各家建議的方案都是業(yè)務(wù)方通過(guò)唯一鍵,去查詢相關(guān)聯(lián)的操作是否已完成,如果已完成則直接返回成功。相關(guān)的判斷邏輯較復(fù)雜,易出錯(cuò),業(yè)務(wù)負(fù)擔(dān)重。

3.2 子事務(wù)屏障

在項(xiàng)目https://github.com/yedf/dtm中,出現(xiàn)了一種子事務(wù)屏障技術(shù),使用該技術(shù),能夠達(dá)到這個(gè)效果,看示意圖:

所有這些請(qǐng)求,到了子事務(wù)屏障后:不正常的請(qǐng)求,會(huì)被過(guò)濾;正常請(qǐng)求,通過(guò)屏障。開發(fā)者使用子事務(wù)屏障之后,前面所說(shuō)的各種異常全部被妥善處理,業(yè)務(wù)開發(fā)人員只需要關(guān)注實(shí)際的業(yè)務(wù)邏輯,負(fù)擔(dān)大大降低。

子事務(wù)屏障提供了方法ThroughBarrierCall,方法的原型為:

func ThroughBarrierCall(db *sql.DB, transInfo *TransInfo, busiCall BusiFunc)

業(yè)務(wù)開發(fā)人員,在busiCall里面編寫自己的相關(guān)邏輯,調(diào)用該函數(shù)。ThroughBarrierCall保證,在空回滾、懸掛等場(chǎng)景下,busiCall不會(huì)被調(diào)用;在業(yè)務(wù)被重復(fù)調(diào)用時(shí),有冪等控制,保證只被提交一次。

子事務(wù)屏障會(huì)管理TCC、SAGA、事務(wù)消息等,也可以擴(kuò)展到其他領(lǐng)域

3.3 子事務(wù)屏障原理

子事務(wù)屏障技術(shù)的原理是,在本地?cái)?shù)據(jù)庫(kù),建立分支事務(wù)狀態(tài)表sub_trans_barrier,唯一鍵為全局事務(wù)id-子事務(wù)id-子事務(wù)分支名稱(try|confirm|cancel

  • 開啟事務(wù)
  • 如果是Try分支,則那么insert ignore插入gid-branchid-try,如果成功插入,則調(diào)用屏障內(nèi)邏輯
  • 如果是Confirm分支,那么insert ignore插入gid-branchid-confirm,如果成功插入,則調(diào)用屏障內(nèi)邏輯
  • 如果是Cancel分支,那么insert ignore插入gid-branchid-try,再插入gid-branchid-cancel,如果try未插入并且cancel插入成功,則調(diào)用屏障內(nèi)邏輯
  • 屏障內(nèi)邏輯返回成功,提交事務(wù),返回成功
  • 屏障內(nèi)邏輯返回錯(cuò)誤,回滾事務(wù),返回錯(cuò)誤

在此機(jī)制下,解決了網(wǎng)絡(luò)異常相關(guān)的問題

  • 空補(bǔ)償控制--如果Try沒有執(zhí)行,直接執(zhí)行了Cancel,那么Cancel插入gid-branchid-try會(huì)成功,不走屏障內(nèi)的邏輯,保證了空補(bǔ)償控制
  • 冪等控制--任何一個(gè)分支都無(wú)法重復(fù)插入唯一鍵,保證了不會(huì)重復(fù)執(zhí)行
  • 防懸掛控制--Try在Cancel之后執(zhí)行,那么插入的gid-branchid-try不成功,就不執(zhí)行,保證了防懸掛控制

對(duì)于SAGA、事務(wù)消息等,也是類似的機(jī)制。

3.4 子事務(wù)屏障小結(jié)

子事務(wù)屏障技術(shù),為https://github.com/yedf/dtm首創(chuàng),它的意義在于設(shè)計(jì)簡(jiǎn)單易實(shí)現(xiàn)的算法,提供了簡(jiǎn)單易用的接口,在首創(chuàng),它的意義在于設(shè)計(jì)簡(jiǎn)單易實(shí)現(xiàn)的算法,提供了簡(jiǎn)單易用的接口,在這兩項(xiàng)的幫助下,開發(fā)人員徹底的從網(wǎng)絡(luò)異常的處理中解放出來(lái)。

該技術(shù)目前需要搭配yedf/dtm事務(wù)管理器,目前SDK已經(jīng)提供給Go、Python語(yǔ)言的開發(fā)者。其他語(yǔ)言的sdk正在規(guī)劃中。對(duì)于其他的分布式事務(wù)框架,只要提供了合適的分布式事務(wù)信息,能夠按照上述原理,快速實(shí)現(xiàn)該技術(shù)。

4、分布式事務(wù)實(shí)踐

我們以前面介紹的SAGA事務(wù)為例,以DTM作為事務(wù)框架,來(lái)完成一個(gè)具體的分布式事務(wù)。本例子采用Go語(yǔ)言,如果您對(duì)此不感興趣,可以直接跳到文章最后的小結(jié)。

4.1 一個(gè)SAGA事務(wù)

我們先編寫核心業(yè)務(wù)代碼,調(diào)整用戶的賬戶余額

func qsAdjustBalance(uid int, amount int) (interface{}, error) {
    _, err := dtmcli.SdbExec(sdbGet(), "update dtm_busi.user_account set balance = balance + ? where user_id = ?", amount, uid)
    return dtmcli.ResultSuccess, err
}


下面我們來(lái)編寫具體的正向操作/補(bǔ)償操作的處理函數(shù)

    app.POST(qsBusiAPI+"/TransIn", common.WrapHandler(func(c *gin.Context) (interface{}, error) {
        return qsAdjustBalance(2, 30)
    }))
    app.POST(qsBusiAPI+"/TransInCompensate", common.WrapHandler(func(c *gin.Context) (interface{}, error) {
        return qsAdjustBalance(2, -30)
    }))
    app.POST(qsBusiAPI+"/TransOut", common.WrapHandler(func(c *gin.Context) (interface{}, error) {
        return qsAdjustBalance(1, -30)
    }))
    app.POST(qsBusiAPI+"/TransOutCompensate", common.WrapHandler(func(c *gin.Context) (interface{}, error) {
        return qsAdjustBalance(1, 30)
    }))

到此各個(gè)子事務(wù)的處理函數(shù)已經(jīng)OK了,然后是開啟SAGA事務(wù),進(jìn)行分支調(diào)用

    req := &gin.H{"amount": 30} // 微服務(wù)的載荷
    // DtmServer為DTM服務(wù)的地址
    saga := dtmcli.NewSaga(DtmServer, dtmcli.MustGenGid(DtmServer)).
        // 添加一個(gè)TransOut的子事務(wù),正向操作為url: qsBusi+"/TransOut", 逆向操作為url: qsBusi+"/TransOutCompensate"
        Add(qsBusi+"/TransOut", qsBusi+"/TransOutCompensate", req).
        // 添加一個(gè)TransIn的子事務(wù),正向操作為url: qsBusi+"/TransOut", 逆向操作為url: qsBusi+"/TransInCompensate"
        Add(qsBusi+"/TransIn", qsBusi+"/TransInCompensate", req)
    // 提交saga事務(wù),dtm會(huì)完成所有的子事務(wù)/回滾所有的子事務(wù)
    err := saga.Submit()

至此,一個(gè)完整的SAGA分布式事務(wù)編寫完成。

如果您想要完整運(yùn)行一個(gè)成功的示例,那么按照yedf/dtm項(xiàng)目的說(shuō)明搭建好環(huán)境之后,通過(guò)下面命令運(yùn)行saga的例子即可:

go run app/main.go quick_start

4.2 處理網(wǎng)絡(luò)異常

假設(shè)提交給dtm的事務(wù)中,調(diào)用轉(zhuǎn)入操作時(shí),出現(xiàn)短暫的故障怎么辦?按照SAGA事務(wù)的協(xié)議,dtm會(huì)重試未完成的操作,這時(shí)我們要如何處理?故障有可能是轉(zhuǎn)入操作完成后出網(wǎng)絡(luò)故障,也有可能是轉(zhuǎn)入操作完成中出現(xiàn)機(jī)器宕機(jī)。如何處理才能夠保障賬戶余額的調(diào)整是正確無(wú)問題的?

我們使用了子事務(wù)屏障功能,保證多次重試,只會(huì)有一次成功提交。

我們把處理函數(shù)調(diào)整為:

func sagaBarrierAdjustBalance(sdb *sql.Tx, uid int, amount int) (interface{}, error) {
    _, err := dtmcli.StxExec(sdb, "update dtm_busi.user_account set balance = balance + ? where user_id = ?", amount, uid)
    return dtmcli.ResultSuccess, err

}

func sagaBarrierTransIn(c *gin.Context) (interface{}, error) {
    return dtmcli.ThroughBarrierCall(sdbGet(), MustGetTrans(c), func(sdb *sql.Tx) (interface{}, error) {
        return sagaBarrierAdjustBalance(sdb, 1, reqFrom(c).Amount)
    })
}

func sagaBarrierTransInCompensate(c *gin.Context) (interface{}, error) {
    return dtmcli.ThroughBarrierCall(sdbGet(), MustGetTrans(c), func(sdb *sql.Tx) (interface{}, error) {
        return sagaBarrierAdjustBalance(sdb, 1, -reqFrom(c).Amount)
    })
}

這里的dtmcli.TroughBarrierCall調(diào)用會(huì)使用子事務(wù)屏障技術(shù),保證第三個(gè)參數(shù)里的回調(diào)函數(shù)僅被處理一次。

您可以嘗試多次調(diào)用這個(gè)TransIn服務(wù),僅有一次余額調(diào)整。您可以運(yùn)行以下命令,運(yùn)行新的處理方式:

go run app/main.go saga_barrier

4.3 處理回滾

假如銀行將金額準(zhǔn)備轉(zhuǎn)入用戶2時(shí),發(fā)現(xiàn)用戶2的賬戶異常,返回失敗,會(huì)怎么樣?我們調(diào)整處理函數(shù),讓轉(zhuǎn)入操作返回失敗

func sagaBarrierTransIn(c *gin.Context) (interface{}, error) {
    return dtmcli.ResultFailure, nil
}

我們給出事務(wù)失敗交互的時(shí)序圖

這里有一點(diǎn),TransIn的正向操作什么都沒有做,就返回了失敗,此時(shí)調(diào)用TransIn的補(bǔ)償操作,會(huì)不會(huì)導(dǎo)致反向調(diào)整出錯(cuò)了呢?

不用擔(dān)心,前面的子事務(wù)屏障技術(shù),能夠保證TransIn的錯(cuò)誤如果發(fā)生在提交之前,則補(bǔ)償為空操作;TransIn的錯(cuò)誤如果發(fā)生在提交之后,則補(bǔ)償操作會(huì)將數(shù)據(jù)提交一次;如果TransIn還在進(jìn)行中,則補(bǔ)償操作會(huì)等待TransIn最終提交/回滾,然后再提交補(bǔ)償/空回滾。

您可以將返回錯(cuò)誤的TransIn改成:

func sagaBarrierTransIn(c *gin.Context) (interface{}, error) {
    dtmcli.ThroughBarrierCall(sdbGet(), MustGetTrans(c), func(sdb *sql.Tx) (interface{}, error) {
        return sagaBarrierAdjustBalance(sdb, 1, 30)
    })
    return dtmcli.ResultFailure, nil
}

最后的結(jié)果余額依舊沒有問題

5、總結(jié)

本文介紹了分布式事務(wù)的一些基礎(chǔ)理論,并對(duì)常用的分布式事務(wù)方案進(jìn)行了講解;在文章的后半部分還給出了事務(wù)異常的原因、分類以及優(yōu)雅的解決方案;最后以一個(gè)可運(yùn)行的分布式事務(wù)例子,將前面介紹的內(nèi)容以簡(jiǎn)短的程序進(jìn)行演示。

到此這篇關(guān)于關(guān)于MySQL與Golan分布式事務(wù)經(jīng)典的七種解決方案的文章就介紹到這了,更多相關(guān)分布式事務(wù)經(jīng)典的七種解決方案內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 在Windows主機(jī)上定時(shí)備份遠(yuǎn)程VPS(CentOS)數(shù)據(jù)的批處理

    在Windows主機(jī)上定時(shí)備份遠(yuǎn)程VPS(CentOS)數(shù)據(jù)的批處理

    我想在自己的 Windows7 下每天/周運(yùn)行一次備份,就有了這個(gè)小工具
    2012-05-05
  • 詳解Mysql如何實(shí)現(xiàn)數(shù)據(jù)同步到Elasticsearch

    詳解Mysql如何實(shí)現(xiàn)數(shù)據(jù)同步到Elasticsearch

    要通過(guò)Elasticsearch實(shí)現(xiàn)數(shù)據(jù)檢索,首先要將Mysql中的數(shù)據(jù)導(dǎo)入Elasticsearch,并實(shí)現(xiàn)數(shù)據(jù)源與Elasticsearch數(shù)據(jù)同步,這里使用的數(shù)據(jù)源是Mysql數(shù)據(jù)庫(kù)。目前Mysql與Elasticsearch常用的同步機(jī)制大多是基于插件實(shí)現(xiàn)的,希望這篇文章能對(duì)大家有所幫助
    2021-11-11
  • MySql存儲(chǔ)過(guò)程學(xué)習(xí)知識(shí)小結(jié)

    MySql存儲(chǔ)過(guò)程學(xué)習(xí)知識(shí)小結(jié)

    這篇文章主要介紹了MySql存儲(chǔ)過(guò)程學(xué)習(xí)知識(shí)小結(jié)的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下
    2016-06-06
  • MySQL 刪除大表的性能問題解決方案

    MySQL 刪除大表的性能問題解決方案

    MySQL在刪除大表engine=innodb(30G+)時(shí),如何減少M(fèi)ySQL hang的時(shí)間,本為將提供詳細(xì)的解決方案,需要了解的朋友可以參考下
    2012-11-11
  • MySQL為id選擇合適的數(shù)據(jù)類型

    MySQL為id選擇合適的數(shù)據(jù)類型

    為 id 列選擇一個(gè)好的數(shù)據(jù)類型非常重要,id 列會(huì)經(jīng)常用于做比較(例如聯(lián)合查詢的條件),以及用于查找其他列。而且,id 也經(jīng)常用于外鍵。因此,id 列的數(shù)據(jù)類型不僅僅關(guān)系自身數(shù)據(jù)表,也關(guān)系到與之關(guān)聯(lián)的其他數(shù)據(jù)表。因此,id 用何種數(shù)據(jù)類型就顯得十分重要
    2021-06-06
  • MySQL 百萬(wàn)級(jí)分頁(yè)優(yōu)化(Mysql千萬(wàn)級(jí)快速分頁(yè))

    MySQL 百萬(wàn)級(jí)分頁(yè)優(yōu)化(Mysql千萬(wàn)級(jí)快速分頁(yè))

    MySql 性能到底能有多高?用了php半年多,真正如此深入的去思考這個(gè)問題還是從前天開始。有過(guò)痛苦有過(guò)絕望,到現(xiàn)在充滿信心
    2012-11-11
  • 老鳥帶你開發(fā)專業(yè)規(guī)范的MySQL啟動(dòng)腳本

    老鳥帶你開發(fā)專業(yè)規(guī)范的MySQL啟動(dòng)腳本

    這篇文章主要介紹了老鳥帶你開發(fā)專業(yè)規(guī)范的MySQL啟動(dòng)腳本,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-09-09
  • 通過(guò)實(shí)例判斷mysql update是否會(huì)鎖表

    通過(guò)實(shí)例判斷mysql update是否會(huì)鎖表

    這篇文章主要介紹了通過(guò)實(shí)例判斷mysql update是否會(huì)鎖表,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-09-09
  • win8.1安裝mysql5.6時(shí)遇到問題解決方案

    win8.1安裝mysql5.6時(shí)遇到問題解決方案

    本文主要記錄的是作者在win8.1安裝mysql5.6時(shí)遇到問題的解決方案,網(wǎng)上查了很多方法都沒能解決,這里把最后的方法分享給大家
    2016-10-10
  • mysql索引使用技巧及注意事項(xiàng)

    mysql索引使用技巧及注意事項(xiàng)

    本篇文章主要介紹了mysql索引使用技巧及注意事項(xiàng),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-03-03

最新評(píng)論