天天看點

一篇文章帶你了解分布式事務

作者:尚矽谷教育

1 什麼是事務

不知道你是否遇到過這樣的情況,去小賣鋪買東西,付了錢,但是店主因為處理了一些其他事,居然忘記你付了錢,又叫你重新付。

再比如,微信轉錢。你給你女朋友轉5000塊錢,當你微信轉錢時,發現網絡錯誤,此時系統提示網絡錯誤,你的錢已經從你的卡裡扣除了,但是你女朋友賬戶卻沒收到任何轉賬。

又或者在網上購物明明已經扣款,但是卻告訴我沒有發生交易。這一系列情況都是因為沒有事務導緻的。這說明了事務在生活中的一些重要性。

有了事務,你去小賣鋪買東西,那就是一手交錢一手交貨。你給你女朋友轉賬,要不就是你款扣5000,女朋友賬戶多5000。否則就是你錢沒扣,女朋友那裡也不會出現多收款情況。有了事務,你去網上購物,扣款即産生訂單交易。

事務提供一種機制将一個活動涉及的所有操作納入到一個不可分割的執行單元,組成事務的所有操作隻有在所有操作均能正常執行的情況下方能送出,隻要其中任一操作執行失敗,都将導緻整個事務的復原。

2 事務的四大特性

A:原子性(Atomicity),一個事務(transaction)中的所有操作,要麼全部完成,要麼全部不完成,不會結束在中間某個環節。

事務在執行過程中發生錯誤,會被復原(Rollback)到事務開始前的狀态,就像這個事務從來沒有執行過一樣。

就像你買東西要麼交錢收貨一起都執行,要麼發不出貨,就退錢。

C:一緻性(Consistency),事務的一緻性指的是在一個事務執行之前和執行之後資料庫都必須處于一緻性狀态。

如果事務成功地完成,那麼系統中所有變化将正确地應用,系統處于有效狀态。

如果在事務中出現錯誤,那麼系統中的所有變化将自動地復原,系統傳回到原始狀态。

I:隔離性(Isolation),指的是在并發環境中,當不同的事務同時操縱相同的資料時,每個事務都有各自的完整資料空間。

由并發事務所做的修改必須與任何其他并發事務所做的修改隔離。事務檢視資料更新時,資料所處的狀态要麼是另一事務修改它之前的狀态,要麼是另一事務修改它之後的狀态,事務不會檢視到中間狀态的資料。

打個比方,你買東西這個事情,是不影響其他人的。

D:持久性(Durability),指的是隻要事務成功結束,它對資料庫所做的更新就必須儲存下來。

即使發生系統崩潰,重新啟動資料庫系統後,資料庫還能恢複到事務成功結束時的狀态。

打個比方,你買東西的時候需要記錄在賬本上,即使老闆忘記了那也有據可查。

3 本地事務

什麼是本地事務(Local Transaction)?本地事務也稱為資料庫事務或傳統事務(相對于分布式事務而言)。尤其對于資料庫而言,為了資料安全,提供了以下的幾個步驟來完成本地事務的送出以及復原

1. transaction begin

2. insert/delete/update

3. insert/delete/update

4. 多個寫操作...

5. transaction commit/rollback

一篇文章帶你了解分布式事務
一篇文章帶你了解分布式事務

在單體應用下,我們開發的業務邏輯比較複雜。尤其某些業務之間存在強依賴性、強耦合性,資料需要保持特别嚴苛的強一緻性。比如購買功能:我們購買對應的商品,一部分需要扣除我們的餘額,餘額扣減後商家就安排相應的發貨。如果不使用事務,比如第一步扣減餘額出現問題,後面商家就直接發貨了。再或者餘額扣了,商家發貨執行失敗。無論哪種情況出現,都會造成巨大的缺陷,要不就是客戶付錢不發貨,要不就是客戶不付錢卻收到了貨。這兩種無論出現哪種問題後果都将會是緻命的。是以我們需要事務控制,把兩者作為一個整體,要不兩者同時成功,要不全部失敗;不可能出現一個成功,一個失敗的情況。

如下面代碼邏輯所示

1. transaction begin

try{

2.調用賬戶子產品扣減餘額,(資料庫更新操作)

3. 判斷餘額是否扣減成功

4. 餘額扣減成功,商品發貨(更新資料庫)

5.兩者都成功, transaction commit

}catch(Exception e){

6. transaction rollback

}finally{

7.關閉資源

}

4 什麼是分布式事務

事務涉及多個操作,多個操作之間的資料必須保持強一緻性。分布式事務是指組成事務的參與者,每個業務部分都分别部署在不同的伺服器上。在微服務架構中多個節點的協調工作必須保持原子性,多個節點的邏輯必須同時成功或者同時失敗。不能出現部分節點成功,部分失敗的情況。一次大的操作由不同的小操作組成的,這些小的操作分布在不同的伺服器上,分布式事務需要保證這些小操作要麼全部成功,要麼全部失敗。

本質上來說,分布式事務就是為了保證不同資料庫、不同伺服器節點的資料一緻性。

一篇文章帶你了解分布式事務

4.1 庫存訂單案例

現在有一個庫存服務,部署在A機器,有一個訂單微服務,部署在B機器。客戶此時在用戶端發起一次訂單請求,需要兩個微服務協調工作。背景根據請求,首先将請求路由到庫存服務更新商品的庫存,庫存扣減完畢後再調用訂單服務建立訂單。

一篇文章帶你了解分布式事務

正常情況下,兩個資料庫各自更新成功,兩邊資料維持着一緻性。

一篇文章帶你了解分布式事務

但是,在非正常情況下,比如訂單服務崩潰了,請求逾時。導緻庫存的扣減完成了,随後的訂單記錄卻因為某些原因插入失敗。這個時候,兩邊資料就失去了應有的一緻性。

一篇文章帶你了解分布式事務

這時候,我們并不能再像單體服務那樣調用本地事務解決,因為組成事務的每個步驟現在都在不同的伺服器節點上。通常對于分布式的資料安全問題,我們主要有以下幾點解決方案:

4.2 分布式事務解決方案

4.2.1 基于XA協定的兩階段送出(2PC)

XA協定:XA是一個分布式事務協定。XA中大緻分為兩部分:事務管理器和本地資料總管。其中本地資料總管往往由資料庫實作,比如Oracle、DB2這些商業資料庫都實作了XA接口,而事務管理器作為全局的排程者,負責各個本地資源的送出和復原。

4.2.1.1 思路

2PC顧名思義分為兩個階段,其實施思路可概括為:

(1)投票階段(voting phase):參與者将操作結果通知協調者;

(2)送出階段(commit phase):收到參與者的通知後,協調者再向參與者發出通知,根據回報情況決定各參與者是否要送出還是復原;

4.2.1.2 舉例

ABCDE五個室友,A組織一場王者榮耀開黑遊戲,A需要拉其他四個室友五排,為了大家都有時間,你需要發送資訊去問室友。這時候A就屬于協調者,BCDE屬于參與者、

投票階段:

(1)A在寝室群發送一條消息,說今晚下課後寝室五黑,詢問室友是否有時間;

(2)B回複有時間;

(3)C回複有時間;

(4)D回複有時間

(5)E遲遲不回複,此時對于這個活動,ABCD均處于阻塞狀态,算法無法繼續進行;

送出階段:

(1)協調者A将收集到的結果回報給BCDE(什麼時候回報,以及回報結果如何,在此例中取決與E的時間與決定);

(2)B收到;

(3)C收到;

(4)D收到;

(5)E收到;

4.2.1.3 實際應用互動流程

第一階段:2PC中包含着兩個角色:事務協調者和事務參與者。讓我們來看一看他們之間的互動流程:

一篇文章帶你了解分布式事務

在分布式事務的第一階段,作為事務協調者的節點會首先向所有的參與者節點發送Prepare請求。

在接到Prepare請求之後,每一個參與者節點會各自執行與事務有關的資料更新,寫入Undo Log和Redo Log。如果參與者執行成功,暫時不送出事務,而是向事務協調節點傳回“完成”消息。

當事務協調者接到了所有參與者的傳回消息,整個分布式事務将會進入第二階段。

第二階段:

一篇文章帶你了解分布式事務

在2PC分布式事務的第二階段,如果事務協調節點在之前所收到都是正向傳回,那麼它将會向所有事務參與者發出Commit請求。

接到Commit請求之後,事務參與者節點會各自進行本地的事務送出,并釋放鎖資源。當本地事務完成送出後,将會向事務協調者傳回“完成”消息。

當事務協調者接收到所有事務參與者的“完成”回報,整個分布式事務完成。

以上所描述的是2PC兩階段送出的正向流程,接下來我們看一看失敗情況的處理流程

第一階段

一篇文章帶你了解分布式事務

第二階段

一篇文章帶你了解分布式事務

在2PC的第一階段,如果某個事務參與者回報失敗消息,說明該節點的本地事務執行不成功,必須復原。

于是在第二階段,事務協調節點向所有的事務參與者發送Abort(中止)請求。接收到Abort請求之後,各個事務參與者節點需要在本地進行事務的復原操作,復原操作依照Undo Log來進行。

以上就是2PC兩階段送出協定的詳細過程。

4.2.1.4 不足點

1.性能問題

2PC遵循強一緻性。在事務執行過程中,各個節點占用着資料庫資源,隻有當所有節點準備完畢,事務協調者才會通知送出,參與者送出後釋放資源。這樣的過程有着非常明顯的性能問題。

2.協調者單點故障問題

事務協調者是整個2PC模型的核心,一旦事務協調者節點挂掉,參與者收不到送出或是復原通知,參與者會一直處于中間狀态無法完成事務。

3.丢失消息導緻的不一緻問題。

在2PC協定的第二個階段,如果發生局部網絡問題,一部分事務參與者收到了送出消息,另一部分事務參與者沒收到送出消息,那麼就導緻了節點之間資料的不一緻。

4.2.2 代碼補償事務(TCC)

TCC的作用主要是解決跨服務調用場景下的分布式事務問題

4.2.2.1場景案例

以航班預定的案例,來介紹TCC要解決的事務場景。在這裡虛構一個場景,把自己當做航班預定的主人公,來介紹這個案例。從合肥 –> 昆明 –> 大理。

準備從合肥出發,到雲南大理去遊玩,然後使用美團App(機票代理商)來訂機票。發現沒有從合肥直達大理的航班,需要到昆明進行中轉。如下圖:

一篇文章帶你了解分布式事務

從圖中我們可以看出來,從合肥到昆明乘坐的是四川航空,從昆明到大理乘坐的是東方航空。

由于使用的是美團App預定,當我選擇了這種航班預定方案後,美團App要去四川航空和東方航空各幫我購買一張票。如下圖:

一篇文章帶你了解分布式事務

考慮最簡單的情況:美團先去川航幫我買票,如果買不到,那麼東航也沒必要買了。如果川航購買成功,再去東航購買另一張票。

現在問題來了:假設美團先從川航成功買到了票,然後去東航買票的時候,因為天氣問題,東航航班被取消了。那麼此時,美團必須取消川航的票,因為隻有一張票是沒用的,不取消就是浪費我的錢。那麼如果取消會怎樣呢?如果讀者有取消機票經曆的話,非正常退票,肯定要扣手續費的。在這裡,川航本來已經購買成功,現在因為東航的原因要退川航的票,川航應該是要扣代理商的錢的。

那麼美團就要保證,如果任一航班購買失敗,都不能扣錢,怎麼做呢?

兩個航空公司都為美團提供以下3個接口:機票預留接口、确認接口、取消接口。美團App分2個階段進行調用,如下所示:

一篇文章帶你了解分布式事務

在第1階段:

美團分别請求兩個航空公司預留機票,兩個航空公司分别告訴美團預留成功還是失敗。航空公司需要保證,機票預留成功的話,之後一定能購買到。

在第2階段:

如果兩個航空公司都預留成功,則分别向兩個公司發送确認購買請求。

如果兩個航空公司任意一個預留失敗,則對于預留成功的航空公司也要取消預留。這種情況下,對于之前預留成功機票的航班取消,也不會扣使用者的錢,因為購買并沒實際發生,之前隻是請求預留機票而已。

通過這種方案,可以保證兩個航空公司購買機票的一緻性,要不都成功,要不都失敗,即使失敗也不會扣使用者的錢。如果在兩個航班都已經已經确認購買後,再退票,那肯定還是要扣錢的。

當然,實際情況肯定這裡提到的肯定要複雜,通常航空公司在第一階段,對于預留的機票,會要求在指定的時間必須确認購買(支付成功),如果沒有及時确認購買,會自動取消。假設川航要求10分鐘内支付成功,東航要求30分鐘内支付成功。以較短的時間算,如果使用者在10分鐘内支付成功的話,那麼美團會向兩個航空公司都發送确認購買的請求,如果超過10分鐘(以較短的時間為準),那麼就不能進行支付。

這個方案提供給我們一種跨服務保證事務一緻性的一種解決思路,可以把這種方案當做TCC的雛形。

TCC是Try ( 嘗試 ) — Confirm(确認) — Cancel ( 取消 ) 的簡稱:

一篇文章帶你了解分布式事務

有哥們立馬會想到,TCC與XA兩階段送出有着異曲同工之妙,下圖列出了二者之間的對比:

一篇文章帶你了解分布式事務

1) 在階段1:

在XA中,各個RM準備送出各自的事務分支,事實上就是準備送出資源的更新操作(insert、delete、update等);而在TCC中,是主業務活動請求(try)各個從業務服務預留資源。

2) 在階段2:

XA根據第一階段每個RM是否都prepare成功,判斷是要送出還是復原。如果都prepare成功,那麼就commit每個事務分支,反之則rollback每個事務分支。

TCC中,如果在第一階段所有業務資源都預留成功,那麼confirm各個從業務服務,否則取消(cancel)所有從業務服務的資源預留請求。

一篇文章帶你了解分布式事務

其核心在于将業務分為兩個操作步驟完成。不依賴 RM 對分布式事務的支援,而是通過對業務邏輯的分解來實作分布式事務

一篇文章帶你了解分布式事務

例如:A要向 B 轉賬,思路大概是

假設使用者user表中有兩個字段:可用餘額(available_money)、當機餘額(frozen_money)

A扣錢對應服務A(ServiceA)

B加錢對應服務B(ServiceB)

轉賬訂單服務(OrderService)

業務轉賬方法服務(BusinessService)

ServiceA,ServiceB,OrderService都需分别實作try(),confirm(),cancle()方法,方法對應業務邏輯如下

一篇文章帶你了解分布式事務

其中業務調用方BusinessService中就需要調用ServiceA.try()ServiceB.try()OrderService.try()

1、當所有try()方法均執行成功時,對全局事物進行送出,即由事物管理器調用每個微服務的confirm()方法

2、 當任意一個方法try()失敗(預留資源不足,抑或網絡異常,代碼異常等任何異常),由事物管理器調用每個微服務的cancle()方法對全局事務進行復原

4.2.2.2 優點

跟2PC(很多第三方架構)比起來,實作以及流程相對簡單了一些,但資料的一緻性比2PC也要差一些

4.2.2.3 缺點

缺點還是比較明顯的,在2,3步中都有可能失敗。TCC屬于應用層的一種補償方式,是以需要程式員在實作的時候多寫很多補償的代碼,在一些場景中,一些業務流程可能用TCC不太好定義及處理。

4.2.3 Seata(理想方式)

seata是阿裡開源的一個分布式事務架構,能夠讓大家在操作分布式事務時,像操作本地事務一樣簡單。一個注解搞定分布式事務。

解決分布式事務問題,有兩個設計初衷

對業務無侵入:即減少技術架構上的微服務化所帶來的分布式事務問題對業務的侵入高性能:減少分布式事務解決方案所帶來的性能消耗

seata中有兩種分布式事務實作方案,AT及TCC

  • AT模式主要關注多 DB 通路的資料一緻性,當然也包括多服務下的多 DB 資料通路一緻性問題 2PC-改進
  • TCC 模式主要關注業務拆分,在按照業務橫向擴充資源時,解決微服務間調用的一緻性問題

那 Seata 是怎麼做到的呢?下面說說它的各個子產品之間的關系。

Seata 的設計思路是将一個分布式事務可以了解成一個全局事務,下面挂了若幹個分支事務,而一個分支事務是一個滿足 ACID 的本地事務,是以我們可以操作分布式事務像操作本地事務一樣。

2019 年 1 月,阿裡巴巴中間件團隊發起了開源項目 Fescar(Fast & EaSy Commit And Rollback),和社群一起共建開源分布式事務解決方案。Fescar 的願景是讓分布式事務的使用像本地事務的使用一樣,簡單和高效,并逐漸解決開發者們遇到的分布式事務方面的所有難題。

4.2.3.1 AT模式

它使得應用代碼可以像使用本地事務一樣使用分布式事務,完全屏蔽了底層細節

AT 模式下,把每個資料庫被當做是一個 Resource,Seata 裡稱為 DataSource Resource。業務通過 JDBC 标準接口通路資料庫資源時,Seata 架構會對所有請求進行攔截,做一些操作。每個本地事務送出時,Seata RM(Resource Manager,資料總管) 都會向 TC(Transaction Coordinator,事務協調器) 注冊一個分支事務。當請求鍊路調用完成後,發起方通知 TC 送出或復原分布式事務,進入二階段調用流程。此時,TC 會根據之前注冊的分支事務回調到對應參與者去執行對應資源的第二階段。TC 是怎麼找到分支事務與資源的對應關系呢?每個資源都有一個全局唯一的資源 ID,并且在初始化時用該 ID 向 TC 注冊資源。在運作時,每個分支事務的注冊都會帶上其資源 ID。這樣 TC 就能在二階段調用時正确找到對應的資源。

一篇文章帶你了解分布式事務

解釋:

Transaction Coordinator (TC):事務協調器,維護全局事務的運作狀态,負責協調并決定全局事務的送出或復原。Transaction Manager(TM):控制全局事務的邊界,負責開啟一個全局事務,并最終發起全局送出或全局復原的決議。Resource Manager (RM):資料總管,負責本地事務的注冊,本地事務狀态的彙報(投票),并且負責本地事務的送出和復原。

XID:一個全局事務的唯一辨別

其中,TM是一個分布式事務的發起者和終結者,TC負責維護分布式事務的運作狀态,而RM則負責本地事務的運作。如下圖所示:

一篇文章帶你了解分布式事務

下面是一個分布式事務在Seata中的執行流程:

  • TM 向 TC 申請開啟一個全局事務,全局事務建立成功并生成一個全局唯一的 XID
  • XID 在微服務調用鍊路的上下文中傳播。
  • RM 向 TC 注冊分支事務,接着執行這個分支事務并送出(重點:RM在第一階段就已經執行了本地事務的送出/復原),最後将執行結果彙報給TC
  • TM 根據 TC 中所有的分支事務的執行情況,發起全局送出或復原決議。
  • TC 排程 XID 下管轄的全部分支事務完成送出或復原請求。

Seata 中有三大子產品,分别是 TM、RM 和 TC。其中 TM 和 RM 是作為 Seata 的用戶端與業務系統內建在一起,TC 作為 Seata 的服務端獨立部署。

4.2.3.2 MT 模式

Seata還支援MT模式。MT模式本質上是一種TCC方案,業務邏輯需要被拆分為 Prepare/Commit/Rollback 3 部分,形成一個 MT 分支,加入全局事務。如圖所示:

一篇文章帶你了解分布式事務

MT 模式一方面是 AT 模式的補充。另外,更重要的價值在于,通過 MT 模式可以把衆多非事務性資源納入全局事務的管理中。

5 總結

在企業開發中,資料安全問題一定是排在第一位的。随着分布式架構的流行,軟體結構越來越複雜;資料的同步、如何保證資料的強一緻性已經成為了一個越來越常見的問題。使用分布式事務,能夠解決多個微服務協同工作時引發的資料不一緻問題。是以我們的精力僅僅集中在開發業務邏輯是遠遠不夠的,還需要為系統的資料安全建立起可靠的保障。一般能使用本地事務解決的問題盡量使用本地事務,因為架構越來越複雜,新的技術在解決問題的同時,同時也會引發新技術所帶來的問題。我們在解決這些新問題的同時,意味着會引進更多的資源,消耗更多的性能,而且維護起來也會越來越複雜。無論是資料庫層的XA、還是應用層TCC、最大努力通知等方案,都沒有完美解決分布式事務問題,它們不過是各自在性能、一緻性、可用性等方面做取舍,尋求某些場景偏好下的權衡。

繼續閱讀