当前位置:网站首页>.Net分布式事務及落地解决方案
.Net分布式事務及落地解决方案
2022-07-05 20:00:00 【李正浩大魔王】
本文主要介紹常見的分布式事務及其在.Net平臺下的落地方案,參考了很多資料,主要來自DTM的官方文檔、知乎
本文發布於我的個人博客站點:
文章鏈接
常用的分布式事務模式
XA(二階段提交)
XA是由X/Open組織提出的分布式事務的規範,XA規範主要定義了(全局)事務管理器和(局部)資源管理器(RM)之間的接口。本地的數據庫如mysql在XA中扮演的是RM角色
XA一共分為兩階段:
第一階段(prepare):即所有的參與者RM准備執行事務並鎖住需要的資源。參與者ready時,向TM報告已准備就緒。
第二階段 (commit/rollback):當事務管理者確認所有參與者(RM)都ready後,向所有參與者發送commit命令。
目前主流的數據庫基本都支持XA事務,包括mysql、oracle、sqlserver、postgre
流程圖如下:
TCC
TCC分為三個階段:
- Try
對各個服務的資源做檢測,對資源進行提前鎖定或者預留(一般來說Try成功的話到了Confirm階段就默認會成功了,如果不成功則可能原因是網絡异常、服務器宕機等) - Confirm
在各個服務中執行實際的操作 - Cancel
如果任何一個服務的業務方法執行出錯,那麼這裏就需要進行補償,即執行已操作成功的業務邏輯的回滾操作
以跨銀行轉賬為案例:
Try階段
先把兩個銀行賬戶中的資金給它凍結住,不讓操作了Confirm階段
執行實際的轉賬操作,A銀行賬戶的資金扣减,B銀行賬戶的資金增加Cancel階段
如果任何一個銀行的操作執行失敗,那麼就需要回滾進行補償
比如A銀行賬戶如果已經扣减了,但是B銀行賬戶資金增加失敗了,那麼就得把A銀行賬戶資金給加回去
適用場景:
對一致性要求較高,常見於資金類的場景,可以使用TCC,自己編寫大量的業務邏輯,自己判斷一個事務中的各個環節是否ok,不ok就執行補償/回滾代碼
本地消息錶
利用數據庫+消息隊列的方式實現异步分布式事務,流程如下:
A系統在本地一個事務裏操作的同時,插入一條數據到消息錶
接著A系統將這個消息發送到MQ
B系統接收到消息後,在一個事務裏,往自己本地消息錶裏插入一條數據,同時執行其他的業務操作,如果這個消息已經被處理過了,那麼此時這個事務會回滾,這樣保證不會重複處理消息
B系統執行成功後,就會更新自己本地消息錶的狀態以及A系統消息錶的狀態
如果B系統處理失敗,那麼就不會更新消息錶狀態,那麼此時A系統會定時掃描自己的消息錶,如果有未處理的消息,會再次發送到MQ中去,讓B再處理
這個方案保證了最終一致性
哪怕B事務失敗了,但是A會不斷重發消息,直到B那邊成功為止
SAGA
Saga是這一篇數據庫論文SAGAS提到的一個分布式事務方案。其核心思想是將長事務拆分為多個本地短事務,由Saga事務協調器協調,如果各個本地事務成功完成那就正常完成,如果某個步驟失敗,則根據相反順序一次調用補償操作
舉例
例如我們要進行一個類似於銀行跨行轉賬的業務,將A中的30元轉給B,根據Saga事務的原理,我們將整個全局事務,切分為以下服務:
- 轉出(TransOut)服務,這裏轉出將會進行操作A-30
- 轉出補償(TransOutCompensate)服務,回滾上面的轉出操作,即A+30
- 轉入(TransIn)服務,轉入將會進行B+30
- 轉入補償(TransInCompensate)服務,回滾上面的轉入操作,即B-30
關於空補償、懸掛、幂等問題及解决方案
以下內容摘自DTM的文檔
异常分類
分布式系統最大的敵人可能就是NPC了,在這裏它是Network Delay, Process Pause, Clock Drift的首字母縮寫。我們先看看具體的NPC問題是什麼:
- Network Delay,網絡延遲。雖然網絡在多數情况下工作的還可以,雖然TCP保證傳輸順序和不會丟失,但它無法消除網絡延遲問題。
- Process Pause,進程暫停。有很多種原因可以導致進程暫停:比如編程語言中的GC(垃圾回收機制)會暫停所有正在運行的線程;再比如,我們有時會暫停雲服務器,從而可以在不重啟的情况下將雲服務器從一臺主機遷移到另一臺主機。我們無法確定性預測進程暫停的時長,你以為持續幾百毫秒已經很長了,但實際上持續數分鐘之久進程暫停並不罕見。
- Clock Drift,時鐘漂移。現實生活中我們通常認為時間是平穩流逝,單調遞增的,但在計算機中不是。計算機使用時鐘硬件計時,通常是石英鐘,計時精度有限,同時受機器溫度影響。為了在一定程度上同步網絡上多個機器之間的時間,通常使用NTP協議將本地設備的時間與專門的時間服務器對齊,這樣做的一個直接結果是設備的本地時間可能會突然向前或向後跳躍。
分布式事務既然是分布式的系統,自然也有NPC問題。因為沒有涉及時間戳,帶來的困擾主要是NP。
空補償
Cancel執行時,Try未執行,事務分支的Cancel操作需要判斷出Try未執行,這時需要忽略Cancel中的業務數據更新,直接返回
一般解决方案:針對該問題,在服務設計時,需要允許空補償,即在沒有找到要補償的業務主鍵時,返回補償成功,並將原業務主鍵記錄下來,標記該業務流水已補償成功
懸掛
Try執行時,Cancel已執行完成,事務分支的Try操作需要判斷出Cancel已執行,這時需要忽略Try中的業務數據更新,直接返回
一般解决方案:需要檢查當前業務主鍵是否已經在空補償記錄下來的業務主鍵中存在,如果存在則要拒絕執行該筆服務,以免造成數據不一致
幂等
由於任何一個請求都可能出現網絡异常,出現重複請求,所有的分布式事務分支操作,都需要保證幂等性(即多次請求和一次請求的結果是一致的,比如將餘額修改為100,不管調用幾次都是100,而將餘額减100,多次調用結果會不一樣,因此在設計時需要考慮到這個問題)
DTM的子事務屏障
dtm中,首創了子事務屏障技術,使用該技術,能够非常便捷的解决异常問題,極大的降低了分布式事務的使用門檻:
子事務屏障技術的原理是,在本地數據庫,建立分支操作狀態錶dtm_barrier,唯一鍵為全局事務id-分支id-分支操作(try|confirm|cancel)
- 開啟本地事務
- 對於當前操作op(try|confirm|cancel),insert ignore一條數據gid-branchid-op,如果插入不成功,提交事務返回成功(常見的幂等控制方法)
- 如果當前操作是cancel,那麼在insert ignore一條數據gid-branchid-try,如果插入成功(注意是成功),則提交事務返回成功
- 調用屏障內的業務邏輯,如果業務返回成功,則提交事務返回成功;如果業務返回失敗,則回滾事務返回失敗
在此機制下,解决了亂序相關的問題
- 空補償控制–如果Try沒有執行,直接執行了Cancel,那麼3中Cancel插入gid-branchid-try會成功,不走屏障內的邏輯,保證了空補償控制
- 幂等控制–2中任何一個操作都無法重複插入唯一鍵,保證了不會重複執行
- 防懸掛控制–Try在Cancel之後執行,那麼Cancel會在3中插入gid-branchid-try,導致Try在2中不成功,就不執行屏障內的邏輯,保證了防懸掛控制
這裏說一下我的理解,在try階段,如果try的op插入成功了,在cancel階段先插入try,正常是失敗的,如果插入成功的話,說明try階段的時候插入失敗了,也就是認為業務未執行,那麼cancel中對應的補償也就不需要調用了,也就是說通過在數據庫中通過gid-branchid-op字段的來同時實現避免空補償和防懸掛,同時gid-branchid-op是唯一的,因此也避免了重複請求的問題,接口的幂等性也不需要我們自己考慮了
分布式事務在.Net中的落地方案
DTM
官網:https://dtm.pub/
强烈推薦,支持多種事務模式,包括SAGA、TCC、二階段消息
.Net的Demo:https://github.com/dtm-labs/dtmcli-csharp-sample 裏面包含了SAGA、TCC和子事務屏障的示例代碼,看完後再閱讀一下dtm的.Net SDK和官方的文檔,基本上就熟悉的差不多了
CAP
官網:https://github.com/dotnetcore/CAP
CAP 是一個基於 .NET Standard 的 C# 庫,它是一種處理分布式事務的解决方案,同樣具有 EventBus 的功能。
其底層通過數據庫+消息隊列的方式來保證分布式事務的可靠性,但是由於是异步的,對於補償機制實現起來較為複雜,適合不需要補償機制的場景(不斷重試直到成功,重試達到一定次數後報警)
如何選擇合適的事務模式
强一致性事務:
- 二階段消息模式: 適合不需要回滾的場景(dtm特有)
- saga模式: 適合需要回滾的場景
- tcc事務模式: 適合一致性要求較高的場景
- xa事務模式: 適合並發要求不高,沒有數據庫行鎖爭搶的場景(基本不用)
异步分布式事務(最終一致):
- 本地消息錶
如果用DTM的話最好還是用子事務屏障,不需要自己考慮空懸掛、幂等、空補償等問題,寫起業務來十分的舒適,降低自己的心智負擔
一個個人認為較好的知乎回答
對於嚴格資金要求絕對不能錯的場景,可以用TCC方案
如果是一般的分布式事務場景,訂單插入之後要調用庫存服務更新庫存,庫存數據沒有資金那麼的敏感,可以用可靠消息最終一致性方案
你其實用任何一個分布式事務的這麼一個方案,都會導致你那塊兒代碼會複雜10倍。很多情况下,系統A調用系統B、系統C、系統D,我們可能根本就不做分布式事務。如果調用報錯會打印异常日志。
每個月也就那麼幾個bug,很多bug是功能性的,體驗性的,真的是涉及到數據層面的一些bug,一個月就幾個,兩三個?如果你為了確保系統自動保證數據100%不能錯,上了幾十個分布式事務,代碼太複雜;性能太差,系統吞吐量、性能大幅度下跌。
99%的分布式接口調用,不要做分布式事務,直接就是監控(發郵件、發短信)、記錄日志(一旦出錯,完整的日志)、事後快速的定比特、排查和出解决方案、修複數據。
每個月,每隔幾個月,都會對少量的因為代碼bug,導致出錯的數據,進行人工的修複數據,自己臨時動手寫個程序,可能要補一些數據,可能要删除一些數據,可能要修改一些字段的值。
比你做50個分布式事務,成本要來的低上百倍,低幾十倍
trade off,權衡,要用分布式事務的時候,一定是有成本,代碼會很複雜,開發很長時間,性能和吞吐量下跌,系統更加複雜更加脆弱反而更加容易出bug;好處,如果做好了,TCC、可靠消息最終一致性方案,一定可以100%保證你那塊數據不會出錯。
1%,0.1%,0.01%的業務,資金、交易、訂單,我們會用分布式事務方案來保證,會員積分、優惠券、商品信息,其實不要這麼搞了
以上內容摘自:小知在知乎的回答,參考文末鏈接3
參考資料
以上內容及圖片或部分內容摘自如下博客 or 回答
- 常用的分布式事務解决方案有哪些? - 網易數帆的回答 - 知乎 https://www.zhihu.com/question/64921387/answer/225784480
- 面試必問:分布式事務六種解决方案 - 敖丙的文章 - 知乎 https://zhuanlan.zhihu.com/p/183753774
- 常用的分布式事務解决方案有哪些? - 小知的回答 - 知乎 https://www.zhihu.com/question/64921387/answer/1976701060
關於分布式事務這方面的內容,可以重點參考Dtm的官方手册,寫的非常詳細,地址:https://dtm.pub/guide/start.html,不僅包含了Dtm框架下各種分布式事務的實現方式,還有不同業務場景下的解决方案提供,非常方便學習
另外,大多數問題可以在dtm的github repository上查找issue,或者在CAP的github repository上查找issue,大多數問題都能在這裏找到,或者進dtm的微信群交流
边栏推荐
猜你喜欢
How to apply smart contracts more wisely in 2022?
Force buckle 729 My schedule I
third-party dynamic library (libcudnn.so) that Paddle depends on is not configured correctl
leetcode刷题:二叉树16(路径总和)
14. Users, groups, and permissions (14)
力扣 1200. 最小绝对差
Webuploader file upload drag upload progress monitoring type control upload result monitoring control
深度学习 卷积神经网络(CNN)基础
How to safely and quickly migrate from CentOS to openeuler
成功入职百度月薪35K,2022Android开发面试解答
随机推荐
【obs】libobs-winrt :CreateDispatcherQueueController
Relationship between floating elements and parent and brother boxes
Autumn byte interviewer asked you any questions? In fact, you have stepped on thunder
Do you know several assertion methods commonly used by JMeter?
Is it safe for Guosen Securities to open an account online?
1: Citation;
leetcode刷题:二叉树12(二叉树的所有路径)
That's awesome. It's enough to read this article
浮动元素与父级、兄弟盒子的关系
c語言oj得pe,ACM入門之OJ~
id选择器和类选择器的区别
Information / data
C application interface development foundation - form control (6) - menu bar, toolbar and status bar controls
How to retrieve the root password of MySQL if you forget it
Force buckle 1200 Minimum absolute difference
Jvmrandom cannot set seeds | problem tracing | source code tracing
c——顺序结构
Summer Challenge harmonyos - realize message notification function
leetcode刷题:二叉树18(最大二叉树)
使用 RepositoryProvider简化父子组件的传值