分布式事務主要解決分布式一致性的問題。說到底就是數據的分布式操作導致僅依靠本地事務無法保證原子性。與單機版的事務不同的是,單機是把多個命令打包成一個統一處理,分布式事務是將多個機器上執行的命令打包成一個命令統一處理。
MySQL 提供了redo log,undo log, Read View,兩階段提交,MVCC 機制等等來保障事務的安全。分布式事務是不是更難呢?拭目以待。
分布式事務其實就在我們身邊,你一直在用,但是你卻一直不注意它。
扣你帳戶的餘額,增加別人帳戶餘額,如果只扣了你的,別人沒增加這是失敗;如果沒扣你的錢別人也增加了那銀行的賠錢。
電商系統中這是很常見的一個場景,用戶下單成功了,店家沒收到單,不發貨;用戶取消了訂單,但是店家卻看到了訂單,發了貨。
當我們的數據量大了之後,我們可能會部署很多獨立的資料庫,但是你的一個邏輯可能會同時操作很多個資料庫的表,這時候該如何保證所有的操作要麼成功,要麼失敗。
微服務的拆分讓各個系統各司其職,但是帶來的也有很多痛苦,一個操作可能會伴隨很多的外部請求,如果某一個外部系統掛掉了,之前操作已完成的這些是否需要回滾。
針對上面這些問題,我們前面學過的資料庫4大特性:ACID 似乎在這裡想要達到就變得很困難,單機情況下你還可以通過鎖和日誌機制來控制數據,在分布式場景又該如何實現呢?在不同的分布式應用架構下,實現一個分布式事務要考慮的問題並不完全一樣,比如對多資源的協調、事務的跨服務傳播等,實現機制也是複雜多變。儘管有這麼多工程細節需要考慮,但分布式事務最核心的還是其 ACID 特性,只是這種 ACID 變換了場景。
傳統的 ACID 模型肯定無法解決分布式環境下的挑戰,基於此加州大學伯克利分校 Eric Brewer教授提出 CAP 定理,他指出 現代 WEB 服務無法同時滿足以下 3 個屬性:
關於一致性的理解後面分出來三個方向:
關於一致性的理解不同,後面對於 CAP 理論的實現就有所不同。
另外 Eric Brewer教授指出現代 WEB 服務無法同時滿足這 3 個屬性,說的是無法同時滿足,那這是為什麼呢?
如果在某個分布式系統中無副本,那麼必然滿足強一致性,同時也滿足可用性,但是如果這個數據宕機了,那麼可用性就得不到保證。
如果一個系統滿足 AP,那麼一致性又得不到保證。所以 CAP 原則的精髓就是要麼 AP,要麼 CP,要麼 AC,但是不存在 CAP。
基於前面提到的 CAP,反正我們都無法同時滿足CAP,設計系統的最高目的就是讓他跑下去不出錯,那麼是不是可以不要求強一致性,最終讓他一致即可。所以後面又提出來了 BASE 定理:
基於現在大型分布式系統的複雜性,我們無法保證服務永遠達到999,那麼是否可以取捨,核心服務達到999,非核心服務允許掛為了保全核心服務。另外在非核心服務 down 機過程中允許系統暫時出現不一致但是這個不一致並不影響系統的核心功能使用。
最終系統恢復,所有服務一起修複數據,最終達到一致的狀態。
業內通常把嚴格遵循 ACID 的事務稱為剛性事務,而基於 BASE 思想實現的事務稱為柔性事務。柔性事務並不是完全放棄了 ACID,僅僅是放寬了一致性要求:事務完成後的一致性嚴格遵循,事務中的一致性可適當放寬。
分布式事務實現方案從類型上去分剛性事務、柔型事務。剛性事務:通常無業務改造,強一致性,原生支持回滾/隔離性,低並發,適合短事務。柔性事務:有業務改造,最終一致性,實現補償接口,實現資源鎖定接口,高並發,適合長事務。
與本地事務一樣,分布式事務場景下也可以採用兩階段提交的方案來實現。XA 的全稱是 eXtended Architecture,它是一個分布式事務協議,通過二階段提交協議保證強一致性。
XA 規範中定義了分布式事務處理模型,這個模型中包含四個核心角色:
XA 協議大概的兩個流程為:
XA 協議是如何滿足 ACID 的呢?
原子性和持久性我們就不用說,我們看看隔離性和一致性。
隔離性
XA 協議中沒有描述如何實現分布式事務的隔離性,但是 XA 協議要求每個資源管理器都要實現本地事務,也就是說基於 XA 協議實現的分布式事務的隔離性是由每個資源管理器本地事務的隔離性來保證的,當一個分布式事務的所有子事務都是隔離的,那麼這個分布式事務天然的就實現了隔離性。
一致性
在單機環境下的一致性就是保證當前伺服器數據一致即可。事務執行完畢數據最終一致,不同的隔離級別下事務執行過程的中間狀態不能被別的事務觀察到。
事務執行完畢最終一致這個好保證,但是在RR 隔離級別下不可見一個未提交事務的中間態在分布式情況該如何做到呢?單機上 MySQL 提供了MVCC機制,採用多版本控制來處理,那分布式事務場景也是否也可以提供這樣的機制呢?XA 協議並沒有定義怎麼實現全局的快照,一個基本思路是用一個集中式或者邏輯上單調遞增的東西來控制生成全局 Snapshot,每個事務或者每條 SQL 執行時都去獲取一次,從而實現不同隔離級別下的一致性。當然開發的難度還是挺大。
存在的問題:
總體來說 XA 方案實現簡單,但是帶來的問題如果放在數據一致性要求嚴格的場景是無法保證數據正確性的。另外事務管理器單點會帶來隱患,同步阻塞模型也致使並發能力弱。
關於 TCC(Try-Confirm-Cancel)的概念,最早是由 Pat Helland 於 2007 年發表的一篇名為《Life beyond Distributed Transactions:an Apostate’s Opinion》的論文提出。 TCC 事務機制相比於上面介紹的 XA,解決了其幾個缺點:
TCC 其實就是採用的補償機制,其核心思想是:針對每個操作,都要註冊一個與其對應的確認和補償(撤銷)操作。TCC 模型完全交由業務實現,每個子業務都需要實現 Try-Confirm-Cancel 三個接口,對業務侵入大,資源鎖定交由業務方。
一個完整的業務活動由一個主業務服務與若干子業務服務組成:
比如一個轉帳操作:
基於 TCC 實現分布式事務,會將原來只需要一個接口就可以實現的邏輯拆分為 Try、Confirm、Cancel 三個接口,所以代碼實現複雜度相對較高,需要在業務中寫很多的補償機制代碼。
TCC將事務提交劃分成兩個階段,Try即為一階段,Confirm 和 Cancel 是二階段並行的兩個分支,二選一。從階段劃分上非常像2PC,我們是否可以說TCC是一種2PC或者2PC變種呢?
對比一下 XA 事務模型,TCC 的兩階段提交與 XA 還是有一些區別:
方案通過在事務主動發起方額外新建事務消息表,事務發起方處理業務和記錄事務消息在本地事務中完成,輪詢事務消息表的數據發送事務消息,事務被動方基於消息中間件消費事務消息表中的事務。
基於本地消息表的方案,每個事務發起方都需要額外新建事務消息記錄表,用於記錄分布式事務的消息的發生、處理狀態。
事務發起方在處理完業務邏輯之後需要將當前事務保存在消息表中,之後將消息發送到消息中間件中,並將消息的狀態設置為 「發送中」。
如果消息在投遞過程中丟失怎麼辦呢?事務發起方可以設置一個定時任務主動掃描狀態為 「發送中」 的消息重新投送。只有消息被業務方消費完畢返回消費成功的結果才確認成功並將消息狀態改為「已發送」。
這裡因為網絡異常或者發送異常導致一個消息可能會被重複發送,所以要求接收方要做到冪等性,允許重複消費。
這種方案的好處就是方案簡單,成本較低,實現也不複雜。
但是壞處也有很多,比如通過消息的方式延遲不好控制;本地消息表與業務耦合在一起沒有做到通用性;本地消息表基於資料庫來實現,有一定的瓶頸。
上面說的本地消息表的模式無法支持本地事務執行和消息發送一致性的問題,如果能在本地事務執行和發消息這兩個操作上加上事務,那豈不是完美。
基於這個思路, 在 MQ 中存儲消息的狀態才是真理,消息生產者先把消息發送給MQ,此時消息狀態為「待確認」,接著生產者去執行本地事務,如果執行成功就給MQ發送消息讓他更改消息狀態為 「待發送」並發送消息,如果執行失敗則刪除消息。
這樣就保證了本地事務和消息發送一致性問題。
注意點:由於MQ通常都會保證消息能夠投遞成功,因此,如果業務沒有及時返回ACK結果,那麼就有可能造成MQ的重複消息投遞問題。因此,對於消息最終一致性的方案,消息的消費者必須要對消息的消費支持冪等,不能造成同一條消息的重複消費的情況。
Saga是什麼?Saga的定義是 「長時間活動的事務 」(Long Lived Transaction,後文簡稱為LLT)。他是普林斯頓大學 HECTOR GARCIA-MOLINA 教授在1987年的一篇關於分布式資料庫的論文中提出來的概念。
Long Lived 從字面意義上不清晰,Long 到底意味著多長?事務持續時間是一個小時、一天甚至一周嗎?其實都不是,時間跨度並不重要。重要的是什麼?關鍵的是跨系統的多次「事務」,Saga 往往由多個外部子事務構成,需要通過多次外部系統的消息交互,才能將整體事務從開始遷移到結束狀態,這和我們原來常見的在一個資料庫的短事務不一樣。比如一個旅行的訂單,是由機票、旅館、租車三個子訂單構成,都需要外部的確認,缺任何一個步驟,不能成行,這就是一個典型的 LLT。
看起來 Sage 的定義與別的分布式事務沒有什麼不同。分布式事務不就是多個不同的子事務構成一個整體嗎?再來看看 補償機制:
每個本地事務有相應的執行模塊和補償模塊,當 Sage 事務中的任意一個本地事務出錯, 可以通過調用相關事務對應的補償方法恢復,達到事務的最終一致性。
Saga 模型是把一個分布式事務拆分為多個本地事務,每個本地事務都有相應的執行模塊和補償模塊(對應TCC 中的 Confirm 和 Cancel),當 Saga 事務中任意一個本地事務出錯時,可以通過調用相關的補償方法恢復之前的事務,達到事務最終一致性。
由於 Saga 模型中沒有 Prepare 階段,因此事務間不能保證隔離性,當多個 Saga 事務操作同一資源時,就會產生更新丟失、髒數據讀取等問題,這時需要在業務層控制並發,例如:
Saga 恢復方式
Saga 支持向前和向後恢復:
雖然 Saga 和 TCC 都是補償事務,但是由於提交階段不同,所以兩者也是有不同的:
因為也是採用補償機制,那麼必然要求服務保持冪等性,如果服務調用超時需要通過冪等性來避免多次請求帶來的問題。
事務特性的滿足:
原子性:Saga 協調器保證整體事務要麼全部執行成功,要麼全部回滾。
一致性:Sage 保證最終一致性。
持久性:Saga 將整體事務拆分成獨立的本地事務,所以持久性在本地事務中很好實現。
但是隔離性 Saga 無法實現,因為大事務被拆分為多個小事務,每個事務提交的時機不同很難保證已提交的小事務不被別人可見。
目前業界提供兩類 Saga 的實現方式:
Seata(Simple Extensible Autonomous Transaction Architecture,簡單可擴展自治事務框架)是 2019 年 1 月份螞蟻金服和阿里巴巴共同開源的分布式事務解決方案。
Seata 會有 4 種分布式事務解決方案,分別是 AT 模式、TCC 模式、Saga 模式和 XA 模式。
XA 模式
XA 模式是 Seata 將會開源的另一種無侵入的分布式事務解決方案,任何實現了 XA 協議的資料庫都可以作為資源參與到分布式事務中,目前主流資料庫,例如 MySql、Oracle、DB2、Oceanbase 等均支持 XA 協議。
XA 協議有一系列的指令,分別對應一階段和二階段操作。「xa start」 和 「xa end」 用於開啟和結束XA 事務;「xa prepare」 用於預提交 XA 事務,對應一階段準備;「xa commit」和「xa rollback」用於提交、回滾 XA 事務,對應二階段提交和回滾。
在 XA 模式下,每一個 XA 事務都是一個事務參與者。分布式事務開啟之後,首先在一階段執行「xa start」、「業務 SQL」、「xa end」和 「xa prepare」 完成 XA 事務的執行和預提交;二階段如果提交的話就執行 「xa commit」,如果是回滾則執行「xa rollback」。這樣便能保證所有 XA 事務都提交或者都回滾。
XA 模式下,用戶只需關注自己的「業務 SQL」,Seata 框架會自動生成一階段、二階段操作;XA 模式的實現如下:
在 XA 模式的一階段,Seata 會攔截「業務 SQL」,在「業務 SQL」之前開啟 XA 事務(「xa start」),然後執行「業務 SQL」,結束 XA 事務「xa end」,最後預提交 XA 事務(「xa prepare」),這樣便完成 「業務 SQL」的準備操作。
執行「xa commit」指令,提交 XA 事務,此時「業務 SQL」才算真正的提交至資料庫。
執行「xa rollback」指令,回滾 XA 事務,完成「業務 SQL」回滾,釋放資料庫鎖資源。
XA 模式下,用戶只需關注「業務 SQL」,Seata 會自動生成一階段、二階段提交和二階段回滾操作。XA 模式和 AT 模式一樣是一種對業務無侵入性的解決方案;但與 AT 模式不同的是,XA 模式將快照數據和行鎖等通過 XA 指令委託給了資料庫來完成,這樣 XA 模式實現更加輕量化。
AT 模式
AT 模式是一種無侵入的分布式事務解決方案。在 AT 模式下,用戶只需關注自己的「業務 SQL」,用戶的 「業務 SQL」 作為一階段,Seata 框架會自動生成事務的二階段提交和回滾操作。
AT 模式的一階段、二階段提交和回滾
均由 Seata 框架自動生成,用戶只需編寫「業務 SQL」,便能輕鬆接入分布式事務,AT 模式是一種對業務無任何侵入的分布式事務解決方案。
TCC 模式
2019 年 3 月份,Seata 開源了 TCC 模式,該模式由螞蟻金服貢獻。TCC 模式需要用戶根據自己的業務場景實現 Try、Confirm 和 Cancel 三個操作;事務發起方在一階段 執行 Try 方式,在二階段提交執行 Confirm 方法,二階段回滾執行 Cancel 方法。
TCC 三個方法描述:
用戶接入 TCC 模式,最重要的事情就是考慮如何將業務模型拆成 2 階段,實現成 TCC 的 3 個方法,並且保證 Try 成功 Confirm 一定能成功。相對於 AT 模式,TCC 模式對業務代碼有一定的侵入性,但是 TCC 模式無 AT 模式的全局行鎖,TCC 性能會比 AT 模式高很多。
Saga 模式
Saga 模式是 Seata 即將開源的長事務解決方案,將由螞蟻金服主要貢獻。在 Saga 模式下,分布式事務內有多個參與者,每一個參與者都是一個衝正補償服務,需要用戶根據業務場景實現其正向操作和逆向回滾操作。
分布式事務執行過程中,依次執行各參與者的正向操作,如果所有正向操作均執行成功,那麼分布式事務提交。如果任何一個正向操作執行失敗,那麼分布式事務會去退回去執行前面各參與者的逆向回滾操作,回滾已提交的參與者,使分布式事務回到初始狀態。
Saga 模式下分布式事務通常是由事件驅動的,各個參與者之間是異步執行的,Saga 模式是一種長事務解決方案。
ServiceComb 是華為開源的微服務框架,目前已升級為 Apache 頂級項目。 準確來說它並不是一個純粹的分布式事務框架而是微服務框架,最開始的版本是 Go 語言,後面支持了 Java。
ServiceComb 由 3 個子項目組成:
從名字上看很顯然是基於 Saga 模式開發的柔性事務方案。Saga系統分為兩部分:Alpha 和 Omega。Alpha 是獨立的服務,扮演事務協調器的作用。Omega 作為開發組件,和業務進程運行在一起。
Omega 會以切面編程的方式向應用程式注入相關的處理模塊。這裡有攔截請求的模塊, 用來幫助我們構建分布式事務調用的上下文。 同時在事務處理初始階段處理事務的相關準備的操作,例如創建 Saga 起始事件,以及相關的子起始事件, 根據事務的執行的成功或者失敗生產相關的事務終止或者失敗事件。
Omega 會與 Alpha 進行連結會把這些事件通知給 Alpha。 Alpha 可以在後臺進行分析,根據 Saga 事務執行的情況給 Omega 下達相關的指令進行相關的回滾恢復。
這樣設計的好處是 Saga 實現代碼與用戶的代碼分離, 用戶只需要添加幾個 annotation,Saga 實現就能 Saga 事件的執行情況並進行相關的處理。
但是不能忘記的是,軟體開發者做的確實有些不地道,」借鑑「了國外友人的源碼,卻沒有添加許可聲明。
大家看完有什麼不懂的可以在下方留言討論.
謝謝你的觀看。
覺得文章對你有幫助的話記得關注我點個讚支持一下!
原文連結:https://www.cnblogs.com/rickiyang/p/13704868.html