掃二維碼與項目經(jīng)理溝通
我們在微信上24小時期待你的聲音
解答本文疑問/技術(shù)咨詢/運營咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流
原創(chuàng)
作者:JackHu 2021-11-05 07:18:15
開發(fā)
架構(gòu)
開發(fā)工具
分布式 伴隨著業(yè)務的快速的發(fā)展、越來越高的業(yè)務復雜度,幾乎每個公司的系統(tǒng)都會從單體走向分布式,特別是轉(zhuǎn)向微服務架構(gòu)。

創(chuàng)新互聯(lián)是一家專業(yè)提供樂平企業(yè)網(wǎng)站建設(shè),專注與網(wǎng)站設(shè)計、成都網(wǎng)站設(shè)計、HTML5、小程序制作等業(yè)務。10年已為樂平眾多企業(yè)、政府機構(gòu)等服務。創(chuàng)新互聯(lián)專業(yè)的建站公司優(yōu)惠進行中。
【51CTO.com原創(chuàng)稿件】伴隨著業(yè)務的快速的發(fā)展、越來越高的業(yè)務復雜度,幾乎每個公司的系統(tǒng)都會從單體走向分布式,特別是轉(zhuǎn)向微服務架構(gòu)。
隨之而來就必然遇到分布式事務這個難題。而我的這篇文章總結(jié)了分布式事務的解決方案,希望給大家?guī)韼椭?/p>
什么是事務?舉個生活中的例子:你去小賣鋪買東西,“一手交錢,一手交貨”就是一個事務的例子,交錢和交貨必須全部成功,事務才算成功,任一個活動失敗,事務將撤銷所有已成功的活動。
明白上述例子,再來看事務的定義:事務可以看做是一次大的活動,它由不同的小活動組成,這些活動要么全部成功,要么全部失敗。
在計算機系統(tǒng)中,更多的是通過關(guān)系型數(shù)據(jù)庫來控制事務,這是利用數(shù)據(jù)庫本身的事務特性來實現(xiàn)的,因此叫數(shù)據(jù)庫事務。
由于應用主要靠關(guān)系數(shù)據(jù)庫來控制事務,而數(shù)據(jù)庫通常和應用在同一個服務器,所以基于關(guān)系型數(shù)據(jù)庫的事務又被稱為本地事務。
回顧一下數(shù)據(jù)庫事務的四大特性 ACID:
比如:張三向李四轉(zhuǎn) 100 元,轉(zhuǎn)賬前和轉(zhuǎn)賬后的數(shù)據(jù)是正確狀態(tài)這叫一致性,如果出現(xiàn)張三轉(zhuǎn)出 100 元,李四賬戶沒有增加 100 元這就出現(xiàn)了數(shù)據(jù)錯誤,就沒有達到一致性。
數(shù)據(jù)庫事務在實現(xiàn)時會將一次事務涉及的所有操作全部納入到一個不可分割的執(zhí)行單元,該執(zhí)行單元中的所有操作要么都成功,要么都失敗,只要其中任一操作執(zhí)行失敗,都將導致整個事務的回滾。
銀行跨行轉(zhuǎn)賬業(yè)務是一個典型分布式事務場景,假設(shè) A 需要跨行轉(zhuǎn)賬給 B,那么就涉及兩個銀行的數(shù)據(jù),無法通過一個數(shù)據(jù)庫的本地事務保證轉(zhuǎn)賬的 ACID,只能夠通過分布式事務來解決。
分布式事務就是指事務的發(fā)起者、資源及資源管理器和事務協(xié)調(diào)者分別位于分布式系統(tǒng)的不同節(jié)點之上。
在上述轉(zhuǎn)賬的業(yè)務中,用戶 A-100 操作和用戶 B+100 操作不是位于同一個節(jié)點上。
本質(zhì)上來說,分布式事務就是為了保證在分布式場景下,數(shù)據(jù)操作的正確執(zhí)行。
分布式事務在分布式環(huán)境下,為了滿足可用性、性能與降級服務的需要,降低一致性與隔離性的要求,一方面遵循 BASE 理論(BASE 相關(guān)理論,涉及內(nèi)容非常多,感興趣的程序員們,可以參考 BASE 理論)。
BASE 理論:
同樣的,分布式事務也部分遵循 ACID 規(guī)范:
典型的場景就是微服務架構(gòu):微服務之間通過遠程調(diào)用完成事務操作。
比如:訂單微服務和庫存微服務,下單的同時訂單微服務請求庫存微服務減庫存。簡言之:跨 JVM 進程產(chǎn)生分布式事務。
單體系統(tǒng)訪問多個數(shù)據(jù)庫實例:當單體系統(tǒng)需要訪問多個數(shù)據(jù)庫(實例)時就會產(chǎn)生分布式事務。
比如:用戶信息和訂單信息分別在兩個 MySQL 實例存儲,用戶管理系統(tǒng)刪除用戶信息,需要分別刪除用戶信息及用戶的訂單信息,由于數(shù)據(jù)分布在不同的數(shù)據(jù)實例,需要通過不同的數(shù)據(jù)庫鏈接去操作數(shù)據(jù),此時產(chǎn)生分布式事務。
簡言之:跨數(shù)據(jù)庫實例產(chǎn)生分布式事務。
多服務訪問同一個數(shù)據(jù)庫實例:比如:訂單微服務和庫存微服務即使訪問同一個數(shù)據(jù)庫也會產(chǎn)生分布式事務,原因就是跨 JVM 進程,兩個微服務持有了不同的數(shù)據(jù)庫鏈接進行數(shù)據(jù)庫操作,此時產(chǎn)生分布式事務。
2PC(Two-phase commit protocol),中文叫二階段提交。
二階段提交是一種強一致性設(shè)計,2PC 引入一個事務協(xié)調(diào)者的角色來協(xié)調(diào)管理各參與者(也可稱之為各本地資源)的提交和回滾,二階段分別指的是準備(投票)和提交兩個階段。
XA 是由 X/Open 組織提出的分布式事務的規(guī)范,XA 規(guī)范主要定義了(全局)事務管理器(TM)和(局部)資源管理器(RM)之間的接口。本地的數(shù)據(jù)庫如 MySQL 在 XA 中扮演的是 RM 角色。
XA 一共分為兩階段:
目前主流的數(shù)據(jù)庫基本都支持 XA 事務,包括 MySQL、Oracle、SQL Server、Postgre。
XA 事務由一個或多個資源管理器(RM)、一個事務管理器(TM)和一個應用程序(ApplicationProgram)組成。
如果有任何一個參與者 prepare 失敗,那么 TM 會通知所有完成 prepare 的參與者進行回滾。
XA 事務的特點是:
三階段提交又稱 3PC,相對于 2PC 來說增加了 CanCommit 階段和超時機制。
如果段時間內(nèi)沒有收到協(xié)調(diào)者的 commit 請求,那么就會自動進行 commit,解決了 2PC 單點故障的問題。但是性能問題和不一致問題仍然沒有根本解決。
下面我們還是一起看下三階段流程的是什么樣的?
第一階段:CanCommit 階段。這個階段所做的事很簡單,就是協(xié)調(diào)者詢問事務參與者,你是否有能力完成此次事務。
如果都返回 yes,則進入第二階段。有一個返回 no 或等待響應超時,則中斷事務,并向所有參與者發(fā)送 abort 請求。
第二階段:PreCommit 階段。此時協(xié)調(diào)者會向所有的參與者發(fā)送 PreCommit 請求,參與者收到后開始執(zhí)行事務操作,并將 Undo 和 Redo 信息記錄到事務日志中。
參與者執(zhí)行完事務操作后(此時屬于未提交事務的狀態(tài)),就會向協(xié)調(diào)者反饋“Ack”表示我已經(jīng)準備好提交了,并等待協(xié)調(diào)者的下一步指令。
第三階段:DoCommit 階段。在階段二中如果所有的參與者節(jié)點都可以進行 PreCommit 提交,那么協(xié)調(diào)者就會從“預提交狀態(tài)”轉(zhuǎn)變?yōu)椤疤峤粻顟B(tài)”。
然后向所有的參與者節(jié)點發(fā)送"doCommit"請求,參與者節(jié)點在收到提交請求后就會各自執(zhí)行事務提交操作,并向協(xié)調(diào)者節(jié)點反饋“Ack”消息,協(xié)調(diào)者收到所有參與者的 Ack 消息后完成事務。
相反,如果有一個參與者節(jié)點未完成 PreCommit 的反饋或者反饋超時,那么協(xié)調(diào)者都會向所有的參與者節(jié)點發(fā)送 abort 請求,從而中斷事務。
Saga 是這一篇數(shù)據(jù)庫論文 saga 提到的一個方案。其核心思想是將長事務拆分為多個本地短事務,由 Saga 事務協(xié)調(diào)器協(xié)調(diào),如果正常結(jié)束那就正常完成,如果某個步驟失敗,則根據(jù)相反順序一次調(diào)用補償操作。
把上面的轉(zhuǎn)賬作為例子,一個成功完成的 SAGA 事務時序圖如下:
SAGA 事務的特點:
2PC 是數(shù)據(jù)庫層面的,而 TCC 是業(yè)務層面的分布式事務,就像我前面說的分布式事務不僅僅包括數(shù)據(jù)庫的操作,還包括發(fā)送短信等,這時候 TCC 就派上用場了!
TCC 的 3 個階段:
把上面的轉(zhuǎn)賬作為例子,通常會在 Try 里面凍結(jié)金額,但不扣款,Confirm 里面扣款,Cancel 里面解凍金額。
一個成功完成的 TCC 事務時序圖如下:
TCC 特點如下:
本地消息表其實就是利用了 各系統(tǒng)本地的事務來實現(xiàn)分布式事務。
本地消息表顧名思義就是會有一張存放本地消息的表,一般都是放在數(shù)據(jù)庫中,然后在執(zhí)行業(yè)務的時候?qū)I(yè)務的執(zhí)行和將消息放入消息表中的操作放在同一個事務中,這樣就能保證消息放入本地表中業(yè)務肯定是執(zhí)行成功的。
然后再去調(diào)用下一個操作,如果下一個操作調(diào)用成功了好說,消息表的消息狀態(tài)可以直接改成已成功。
如果調(diào)用失敗也沒事,會有后臺任務定時去讀取本地消息表,篩選出還未成功的消息再調(diào)用對應的服務,服務更新成功了再變更消息的狀態(tài)。
這時候有可能消息對應的操作不成功,因此也需要重試,重試就得保證對應服務的方法是冪等的,而且一般重試會有最大次數(shù),超過最大次數(shù)可以記錄下報警讓人工處理。
可以看到本地消息表其實實現(xiàn)的是最終一致性,容忍了數(shù)據(jù)暫時不一致的情況。
RocketMQ 就很好的支持了消息事務,讓我們來看一下如何通過消息實現(xiàn)事務。
第一步先給 Broker 發(fā)送事務消息即半消息,半消息不是說一半消息,而是這個消息對消費者來說不可見,然后發(fā)送成功后發(fā)送方再執(zhí)行本地事務。再根據(jù)本地事務的結(jié)果向 Broker 發(fā)送 Commit 或者 RollBack 命令。
并且 RocketMQ 的發(fā)送方會提供一個反查事務狀態(tài)接口,如果一段時間內(nèi)半消息沒有收到任何操作請求,那么 Broker 會通過反查接口得知發(fā)送方事務是否執(zhí)行成功,然后執(zhí)行 Commit 或者 RollBack 命令。
如果是 Commit 那么訂閱方就能收到這條消息,然后再做對應的操作,做完了之后再消費這條消息即可。
如果是 RollBack 那么訂閱方收不到這條消息,等于事務就沒執(zhí)行過??梢钥吹酵ㄟ^ RocketMQ 還是比較容易實現(xiàn)的,RocketMQ 提供了事務消息的功能,我們只需要定義好事務反查接口即可。
同時也可以看到消息事務實現(xiàn)的也是最終一致性。
發(fā)起通知方通過一定的機制最大努力將業(yè)務處理結(jié)果通知到接收方。
具體包括:
前面介紹的的本地消息表和消息事務都屬于可靠消息,與這里介紹的最大努力通知有什么不同?
可靠消息一致性,發(fā)起通知方需要保證將消息發(fā)出去,并且將消息發(fā)到接收通知方,消息的可靠性關(guān)鍵由發(fā)起通知方來保證。
最大努力通知,發(fā)起通知方盡最大的努力將業(yè)務處理結(jié)果通知為接收通知方,但是可能消息接收不到,此時需要接收通知方主動調(diào)用發(fā)起通知方的接口查詢業(yè)務處理結(jié)果,通知的可靠性關(guān)鍵在接收通知方。
解決方案上,最大努力通知需要:
最大努力通知適用于業(yè)務通知類型,例如微信交易的結(jié)果,就是通過最大努力通知方式通知各個商戶,既有回調(diào)通知,也有交易查詢接口。
這是阿里開源項目 seata 中的一種事務模式,在螞蟻金服也被稱為 FMT。優(yōu)點是該事務模式使用方式,類似 XA 模式,業(yè)務無需編寫各類補償操作,回滾由框架自動完成,缺點也類似 AT,存在較長時間的鎖,不滿足高并發(fā)的場景。
在 Seata 項目中,最早由阿里巴巴中間件開源出的 AT 模式(Automatic Transaction) 是一套創(chuàng)新的、業(yè)務無侵入的分布式事務解決方案。
截止 Seata 的 GA 版本發(fā)布,AT 模式 已經(jīng)在開源社區(qū)引起了廣泛關(guān)注,很多家企業(yè)用戶已經(jīng)將 Seata 的 AT 模式應用于生產(chǎn)。
AT 模式一階段:首先,在 Seata 的組件中,如果你想開啟分布式事務,那么就應該在你的業(yè)務入口或者事務發(fā)起入口加上 @GlobalTransactional 注解。
如果你是 AT 模式就要做好數(shù)據(jù)源代理(seata1.0 后全面支持自動代理),并被 sqlsessionfactroy 使用(或者直接 jdbc 操作使用被代理數(shù)據(jù)源)。
可以發(fā)現(xiàn)比較關(guān)鍵的異步,與其他模式的區(qū)別便是代理數(shù)據(jù)源,而代理數(shù)據(jù)源又有什么奧秘呢?
AT 模式二階段提交:二階段如果是提交的話,因為“業(yè)務 SQL”在一階段已經(jīng)提交至數(shù)據(jù)庫,所以 Seata 框架只需將一階段保存的快照數(shù)據(jù)和行鎖刪掉,完成數(shù)據(jù)清理即可。
AT 模式二階段回滾:二階段如果是回滾的話,Seata 就需要回滾一階段已經(jīng)執(zhí)行的“業(yè)務 SQL”,還原業(yè)務數(shù)據(jù)。
回滾方式便是用“before image”還原業(yè)務數(shù)據(jù);但在還原前要首先要校驗臟寫,對比“數(shù)據(jù)庫當前業(yè)務數(shù)據(jù)”和 “after image”。
如果兩份數(shù)據(jù)完全一致就說明沒有臟寫,可以還原業(yè)務數(shù)據(jù),如果不一致就說明有臟寫, 出現(xiàn)臟寫就需要轉(zhuǎn)人工處理。
本文介紹了分布式事務的一些基礎(chǔ)理論,并對常用的分布式事務方案進行了講解。
分布式事務本身就是一個技術(shù)難題,業(yè)務中具體使用哪種方案還是需要不同的業(yè)務特點自行選擇。
但是我們也會發(fā)現(xiàn),分布式事務會大大的提高流程的復雜度,會帶來很多額外的開銷工作,「代碼量上去了,業(yè)務復雜了,性能下跌了」。
所以,當我們真實開發(fā)的過程中,能不使用分布式事務就不使用。

我們在微信上24小時期待你的聲音
解答本文疑問/技術(shù)咨詢/運營咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流