👉 這是一個或許對你有用的社群
-
《專案實戰(影片)》:從書中學,往事上“練” -
《網際網路高頻面試題》:面朝簡歷學習,春暖花開 -
《架構 x 系統設計》:摧枯拉朽,掌控面試高頻場景題 -
《精進 Java 學習指南》:系統學習,網際網路主流技術棧 -
《必讀 Java 原始碼專欄》:知其然,知其所以然

👉這是一個或許對你有用的開源專案
國產 Star 破 10w+ 的開源專案,前端包括管理後臺 + 微信小程式,後端支援單體和微服務架構。
功能涵蓋 RBAC 許可權、SaaS 多租戶、資料許可權、商城、支付、工作流、大屏報表、微信公眾號、CRM 等等功能:
-
Boot 倉庫:https://gitee.com/zhijiantianya/ruoyi-vue-pro -
Cloud 倉庫:https://gitee.com/zhijiantianya/yudao-cloud -
影片教程:https://doc.iocoder.cn
分散式事務一直是微服務設計的一個難點,是解決業務一致性問題的重要手段,一直很謹慎不敢寫這部分內容,擔心水平不夠弄錯誤導大家。
但是如果在專案上出現了跨服務的業務一致性需求,在網路上搜索出來的材料往往是一些理論和具體的框架使用,對問題場景的分析不多。
在和多個公司的架構師討論後,大家的共識是:分散式事務很難有一個通用的解決方案,需要在場景中獲得比較好的平衡,往往是捏著鼻子選擇最容易接受的方案。
本文從場景和問題出發,討論分散式事務的實現思路,並對業界常見的概念進行了辨析,用於指導分散式事務方案選擇。
有哪些常見的分散式事務場景?
常見的最終一致性常見有這些,由簡單到複雜:
-
服務之間的呼叫重試,需要保持一致性。例如,訂單提交後需要傳送通知。一般通知服務是單獨的服務或者產品,因此需要透過一個機制來保證訊息一定會被髮出。 -
多個業務之間的一致性處理。比如使用者在商城消費後,需要非同步的完成積分、會員等級的更新等,這類一致性要求沒那麼嚴格。 -
賬戶餘額管理。使用者消費後需要扣減餘額,而訂單取消後需要再把餘額修改回原來的狀態。賬戶和訂單往往在不同的服務,因此需要處理一致性問題。 -
庫存一致性。庫存一致性問題是供應鏈領域的核心問題,也是難度最高的分散式問題。和賬戶餘額類似,當時其不同點在於,賬戶餘額的扣減併發很低,沒有競爭烈度。而在電商中,庫存屬於高烈度爭用的資源。
這裡只選擇了一些具有代表性的業務一致性問題,但是我們能發現一些規律。我們可以對有業務一致性要求的問題進行分類,可以大概分為兩類:
-
無資源爭用的業務一致性場景。 -
有資源爭用的業務一致性場景。
大部分情況下,無資源爭用的業務一致性很容易被解決,透過重試即可解決。而有資源爭用的業務一致性場景卻很麻煩,其實可以想辦法轉換為無資源爭用的業務一致性場景,這是我們日常解決一致性問題的核心思想。
這就需要用到一些分散式理論的內容,我們主要聊聊有資源爭用的場景如何處理。
基於 Spring Boot + MyBatis Plus + Vue & Element 實現的後臺管理系統 + 使用者小程式,支援 RBAC 動態許可權、多租戶、資料許可權、工作流、三方登入、支付、簡訊、商城等功能
-
專案地址:https://github.com/YunaiV/ruoyi-vue-pro -
影片教程:https://doc.iocoder.cn/video/
分散式事務的理論基礎和分析
一致性問題其實在生活中非常常見。
比如在中學,語文老師和數學老師都想要佔用晚上的自習時間,於是打電話給家裡說不回去吃飯後,讓各自的課代表通知班上的同學,如果晚上的時間只夠一節課,那麼一致性問題就出現了。其中一位老師沒有回去吃飯,卻也不能上課。
當狀態分割槽後,也就是決策者不在一起時,一致性問題必然會出現。
這個問題被描述為眾所周知的 CAP 定理:一致性、可用性和分割槽容忍性只能三選二。
這三個特性的含義為:
-
Consistency:一致性,每個分割槽的狀態保持一致。 -
Availability:可用性,讓作業能進行下去,不會因此阻塞。 -
Partition tolerance:可以允許各個元件分開進行作業,在微服務系統或分庫後,分割槽容忍一定存在。
所以實際上在微服務中,只有 CP 和 AP 可以選擇。
不過,CAP 定理有一個隱喻和誤解,三個特性只能選擇兩個的前提“同時”,如果取得一致性的時間足夠短,也能看做“同時”。
這就是最終一致性的理論基礎:在分散式系統中,我們不一定要放棄一致性,只要在業務允許的時間範圍內取得共識即可。甚至有些業務的時間要求是 T+1,也就是在晚上資料同步作業完成後。
所以 CAP 定理可以視為達成分散式系統共識定理的一種。
最終一致性就好比,一名程式設計師晚上決定加班,只需要在時間允許範圍內給家裡打電話不回來吃飯一樣。對於無資源爭用的情況,最終一致性都能解決。
但是對於有爭用的情況來說,就沒那麼簡單了。我們經常會有這樣的經驗,組織了一場會議,通知參會人員後發現沒有可用的會議室,又不得不取消會議。
對於會議室這種情況,自然的想法是透過預定佔用資源,預定成功後再通知參會人員。
其實在軟體工程上,幾乎所有具有資源爭用的情況也是這樣處理的。
在扣減賬戶餘額、庫存時,如果存在爭用的情況,我們往往會設計一個同步佔用的請求。將資源佔用後,如果後續的過程出錯或者有任何問題再透過最終一致性釋放回去即可。
可以見到,幾乎大部分分散式事務協調方案都是這個思路:臨時佔用,非同步執行或者回退。其實這就是 TCC 模式。
說完了分散式事務背後的思路,下面盤點一下常見的技術方案和框架,例如 TCC。
基於 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 實現的後臺管理系統 + 使用者小程式,支援 RBAC 動態許可權、多租戶、資料許可權、工作流、三方登入、支付、簡訊、商城等功能
-
專案地址:https://github.com/YunaiV/yudao-cloud -
影片教程:https://doc.iocoder.cn/video/
業界常見概念和解決方案
關於分散式事務的話題業界有非常多時髦的詞彙,這裡做一些梳理。
ACID/BASE
ACID/BASE 是兩種事務原則。
ACID 是指:原子性(Atomicity)、一致性(Consistency)、隔離性(Isolation)、永續性(Durability)。這幾個特性是關係型資料庫管理系統(DBMS)的基礎。
BASE 是指:基本可用(Basically Available)、軟狀態(Soft state)和最終一致性(Eventually consistent)。
當分割槽發生時,無法做到 ACID,只能追求 BASE。一般在分散式系統(微服務)實踐中,在服務內部追求 ACID 事務,在服務間追求 BASE 事務。
TCC
TCC 是 Try-Confirm-Cancel 的縮寫,是業界一種常見解決分散式事務的思想。
通常來說,TCC 會侵入業務,工作在應用層,和我們前面的分析部分思路一致,是一種將資源爭用轉換為無資源爭用的模式,用於切斷長事務。
參與分散式事務的各個服務,需要實現和預留佔用、執行、取消三類作業。
-
Try: 往往是同步的,嘗試鎖定或佔用資源,如果在 Try 階段就失敗自然可以整體失敗。 -
Confirm: 消費預先佔用的資源,執行較長的業務邏輯,可以非同步完成。 -
Cancel: 取消執行業務,釋放 Try 階段的預佔的業務資源。
在 Try 階段一般會生成一個事務 ID 來作為冪等因子,用於後面兩步的冪等操作。
值得注意的是,有些專案這三個動作甚至對業務可見,允許使用者手動再次重試 Confirm 或者 Cancel 操作。因此,很多使用 TCC 模式解決了專案上分散式事務問題的團隊甚至不知道這種模式有一個名字。
TCC 是一種被廣泛使用的模式,在電商、支付、銀行領域使用非常多,在這些行業經常會聽到一些黑話,例如一盤貨、多盤貨、衝正、在途庫存等詞可以對應到 TCC 模式中的預佔、取消概念上。
JTA/XA
JTA 是 Java 事務程式設計介面(Java Transaction API);XA 是 eXtended Architecture 協議。
連起來的意思是用支援實現了 JTA 的資料庫驅動管理支援 XA 協議的資料來源來實現資料庫層面的分散式事務。
而這裡的分散式事務其實也不是真的分散式,它依賴一箇中心化的事務,導致業務中大量的中心化的狀態和鎖操作。
XA 需要資料庫的支援(Mysql 僅有 InnoDB 引擎支援),由事務協調器對資料庫本地事務的提交和回滾實現事務。

JTA/XA 是資料庫層面的事務,一般來說應用不會干預,可以做到業務無感知。由於這項技術依賴基礎設施,且依然是中心化的架構,筆者僅在一個古老的銀行專案看到過,現代專案幾乎沒有再見到。
如果需要了解細節可以參考 Mysql 文件中 XA 部分:https://dev.mysql.com/doc/refman/8.0/en/xa.html
二階段、三階段
在 XA 中,實現事務協調的機制為兩階段提交(2PC )。二階段、三階段都是分散式協調器集中實現分散式事務的一種原理。
由於它基本不會被用於應用層,我們在日常專案也不太用到,是資料庫實現全域性事務的一種方式。
二階段、三階段提交演算法的成立基於以下假設:
-
在需要實現一致性的分散式系統中,存在一個節點作為協調者(Coordinator),其他節點作為參與者(Participants)。 -
所有節點都採用預寫式日誌,這些日誌是可靠的。 -
參與的節點資料是持久化的可靠資料,或者可以被恢復
關於二階段、三階段的內容維基百科上有清晰的描述,這裡就不浪費篇幅詳細說明。這裡引用維基百科上一段非常容易理解的比喻:
舉例來說,假設有一個決策小組由一個主持人負責與多位組員以電話聯絡方式協調是否透過一個提案,以兩階段提交來說,主持人收到一個提案請求,打電話跟每個組員詢問是否透過並統計回覆,然後將最後決定打電話通知各組員。要是主持人在跟第一位組員通完電話後失憶,而第一位組員在得知結果並執行後老人痴呆,那麼即使重新選出主持人,也沒人知道最後的提案決定是什麼,也許是透過,也許是駁回,不管大家選擇哪一種決定,都有可能與第一位組員已執行過的真實決定不一致,老闆就會認為決策小組溝通有問題而解僱。三階段提交即是引入了另一個步驟,主持人打電話跟組員通知請準備透過提案,以避免沒人知道真實決定而造成決定不一致的失業危機。而三階段提交為什麼能夠解決二階段提交的問題呢?回到剛剛提到的狀況,在主持人通知完第一位組員請準備通過後兩人意外失憶,即使沒人知道全體在第一階段的決定為何,全體決策組員仍可以重新協調過程或直接否決,不會因出現不一致決定而失業。那麼當主持人通知完全體組員請準備透過並得到大家的再次確定後進入第三階段,當主持人通知第一位組員請透過提案後兩人意外失憶,這時候其他組員再重新選出主持人後,仍可以知道目前至少是處於準備透過提案階段,表示第一階段大家都已經決定要通過了,此時便可以直接透過。
SAGA 模式
Saga 是類似於 TCC 這樣的分散式模式,和 TCC 的主要區別是沒有預佔和鎖定。實現 Sata 有兩個假設:
-
應用層所有的參與長事務的服務都具有可取消能力 -
應用層所有的參與長事務的服務具有本地事務
簡單來說,Saga 模式中參與事務的服務優先提交事務,如果出現失敗的情況下,再對前面已經提交的事務進行回滾補償。
其理論基礎來源於 Hector & Kenneth 的論文 《Sagas》,原理參考下圖:

圖片來源:http://seata.io/zh-cn/docs/user/saga.html
實際來說,使用 Saga 的場景不多。原因是無法應用於資源爭用的場景,而無資源爭用的場景又不需要 Saga 模式。
可能潛在的場景有多個系統之間的一致性處理,在某些場景下,第三方系統或者遺留系統不提供預佔機制,但是提供了取消和回滾操作的 API,可以應用 Saga 模式。
在筆者的經驗中,使用類似 Saga 模式處理 ERP、供應鏈、財務等系統整合,透過優先成功的模式處理業務,如果業務失敗,透過一個本地訊息或任務表對第三方系統補償取消的業務。
AT 模式
AT 模式沒有查到相關資料,僅在 Seata 框架文件中提到了該模式,在外網環境下沒有查到權威出處。
根據公開資料顯示和 Seata Committer 的一篇文章:《Seata 分散式事務 XA 與 AT 全面解析》得知。
AT 模式是為了彌補 XA 模式的一些缺點產生的,AT 與 XA 之間的關係主要為工作的分層。XA 模式工作在資料庫實現層,依賴相關資料支援 XA 協議,而 AT 模式工作在事務協調器中,透過解析 SQL 獲取資料的行進行加鎖,犧牲對業務的無侵入性,最佳化效能。
該篇文章也提到:“AT 可以看作時由 Seata 社群進行全方面最佳化,自研的 XA 模式,最大特點就是解決了 XA 模式的效能差的問題。” 查證了 AT 模式是 Seata 社群提供的 XA 改進模式。
不過,AT 模式假設大部分業務場景下,假定每個分支事務都能成功。所有分支事務將直接提交,無需等待事務協調器的指示,以提高速度。然而,這種模式下的全域性事務實際上並非原子性的,在一定時間內會處於髒資料狀態(在併發場景下,如果沒有有效的治理措施,這種情況將更加嚴重)。
分散式事務實現的選擇和要點
上面辨析了這麼多分散式事務實現的概念和模式,那麼對於開發者來說,現實問題是,我該怎麼選擇各種分散式事務模式進行設計呢?
建議充分分析應用場景、使用成本和需求能權衡的情況下做出選擇。參考下面這張概念圖:

一些關鍵的決策點如下:
-
一致性是否只存在資料庫層面,是否存在業務執行上有需要一致性,例如通知傳送 -
對業務的侵入性 -
是否需要引入協調性和基礎設施 -
同一個事務是否有超過兩處資源爭用的情況
一般來說,在落地實現分散式事務中,如果採用了類似 TCC 方案,需要考慮好冪等性、重試和可靠資訊的問題,這些更為細節的內容在以後的話題中討論。
總結
分散式事務在業界是一個長期討論的話題,本文僅僅是對常見概念做了辨析和分析,用於在遇到相關問題時做出合適的選擇和權衡。
一般來說,在解決這類問題時,還是需要從業務場景出發,分析具體的權衡點,避免引入號稱能一勞永逸解決分散式問題的方案或者框架,使用價效比合適的最終方案。
如果深入討論分散式資料庫,會有更多話題,例如全域性時鐘、原子性、讀寫衝突、併發控制、資料複製,在後面有機會和大家討論並整理出來。
參考資料
-
An Illustrated Proof of the CAP Theorem https://mwhittaker.github.io/blog/an_illustrated_proof_of_the_cap_theorem/ -
http://lpd.epfl.ch/sgilbert/pubs/BrewersConjecture-SigAct.pdf -
https://docs.spring.io/spring-boot/docs/2.0.x/reference/html/boot-features-jta.html -
https://www.cnblogs.com/jajian/p/10014145.html -
https://seata.io/zh-cn/blog/tcc-mode-design-principle.html -
https://blog.csdn.net/qq_24313635/article/details/104044297 -
一款 TCC 實現框架 https://github.com/changmingxie/tcc-transaction -
https://www.atomikos.com/Documentation/WhenToUseJtaXa -
Understanding XA Transactions With Practical Examples in Go https://betterprogramming.pub/understanding-xa-transactions-with-practical-examples-in-go-67e99fd333db -
https://blog.csdn.net/crazymakercircle/article/details/113666155 -
https://dev.mysql.com/doc/refman/8.0/en/xa.html -
https://en.wikipedia.org/wiki/Two-phase_commit_protocol -
https://en.wikipedia.org/wiki/Three-phase_commit_protocol -
https://seata.io/zh-cn/docs/user/saga.html -
Seata 分散式事務 XA 與 AT 全面解析 https://developer.aliyun.com/article/919377 -
Saga Paper https://www.cs.cornell.edu/andru/cs711/2002fa/reading/sagas.pdf
歡迎加入我的知識星球,全面提升技術能力。
👉 加入方式,“長按”或“掃描”下方二維碼噢:

星球的內容包括:專案實戰、面試招聘、原始碼解析、學習路線。





文章有幫助的話,在看,轉發吧。
謝謝支援喲 (*^__^*)