新聞中心
為什么不建議你用分布式事務(wù)?
原創(chuàng)
作者:JackHu 2021-11-05 07:18:15
開發(fā)
架構(gòu)
開發(fā)工具
分布式 伴隨著業(yè)務(wù)的快速的發(fā)展、越來越高的業(yè)務(wù)復(fù)雜度,幾乎每個公司的系統(tǒng)都會從單體走向分布式,特別是轉(zhuǎn)向微服務(wù)架構(gòu)。

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


咨詢
建站咨詢
