天天看點

幹貨|一篇文章帶你學習分布式事務

小螞蟻說:

分布式事務是企業內建中的一個技術難點,也是每一個分布式系統架構中都會涉及到的一個東西,特别是在這幾年越來越火的微服務架構中,幾乎可以說是無法避免,本文就圍繞分布式事務各方面與大家進行介紹。

一. 事務 1.1 什麼是事務 資料庫事務(簡稱:事務,Transaction)是指資料庫執行過程中的一個邏輯機關,由一個有限的資料庫操作序列構成。 事務擁有以下四個特性,習慣上被稱為ACID特性:

  1. 原子性( Atomicity):事務作為一個整體被執行,包含在其中的對資料庫的操作要麼全部被執行,要麼都不執行。
  2. 一緻性 (Consistency):事務應確定資料庫的狀态從一個一緻狀态轉變為另一個一緻狀态。一緻狀态是指資料庫中的資料應滿足完整性限制。除此之外,一緻性還有另外一層語義,就是事務的中間狀态不能被觀察到(這層語義也有說應該屬于原子性)。
  3. 隔離性 (Isolation):多個事務并發執行時,一個事務的執行不應影響其他事務的執行,如同隻有這一個操作在被資料庫所執行一樣。
  4. 持久性 (Durability):已被送出的事務對資料庫的修改應該永久儲存在資料庫中。在事務結束時,此操作将不可逆轉。
1.2 本地事務

起初,事務僅限于對單一資料庫資源的通路控制:

幹貨|一篇文章帶你學習分布式事務

架構服務化以後,事務的概念延伸到了服務中。倘若将一個單一的服務操作作為一個事務,那麼整個服務操作隻能涉及一個單一的資料庫資源:

幹貨|一篇文章帶你學習分布式事務

這類基于單個服務單一資料庫資源通路的事務,被稱為本地事務(Local Transaction)。

二. 分布式事務應用架構

本地事務主要限制在單個會話内,不涉及多個資料庫資源。但是在基于SOA(Service-Oriented Architecture,面向服務架構)的分布式應用環境下,越來越多的應用要求對多個資料庫資源,多個服務的通路都能納入到同一個事務當中,分布式事務應運而生。

最早的分布式事務應用架構很簡單,不涉及服務間的通路調用,僅僅是服務内操作涉及到對多個資料庫資源的通路。

幹貨|一篇文章帶你學習分布式事務

當一個服務操作通路不同的資料庫資源,又希望對它們的通路具有事務特性時,就需要采用分布式事務來協調所有的事務參與者。

對于上面介紹的分布式事務應用架構,盡管一個服務操作會通路多個資料庫資源,但是畢竟整個事務還是控制在單一服務的内部。如果一個服務操作需要調用另外一個服務,這時的事務就需要跨越多個服務了。在這種情況下,起始于某個服務的事務在調用另外一個服務的時候,需要以某種機制流轉到另外一個服務,進而使被調用的服務通路的資源也自動加入到該事務當中來。下圖反映了這樣一個跨越多個服務的分布式事務:

幹貨|一篇文章帶你學習分布式事務

如果将上面這兩種場景(一個服務可以調用多個資料庫資源,也可以調用其他服務)結合在一起,對此進行延伸,整個分布式事務的參與者将會組成如下圖所示的樹形拓撲結構。在一個跨服務的分布式事務中,事務的發起者和送出均系同一個,它可以是整個調用的用戶端,也可以是用戶端最先調用的那個服務。

幹貨|一篇文章帶你學習分布式事務

較之基于單一資料庫資源通路的本地事務,分布式事務的應用架構更為複雜。在不同的分布式應用架構下,實作一個分布式事務要考慮的問題并不完全一樣,比如對多資源的協調、事務的跨服務傳播等,實作機制也是複雜多變。盡管有這麼多工程細節需要考慮,但分布式事務最核心的還是其 ACID 特性。是以,想要了解一個分布式事務,就先從了解它是怎麼實作事務 ACID 特性開始。

下文将從兩個最常見的分布式事務模型入手,着重分析分布式事務的基礎共通點,即如何保證分布式事務的 ACID 特性。

三. 常見分布式事務模型 ACID 實作分析 3.1 X/Open XA 協定

最早的分布式事務模型是 X/Open 國際聯盟提出的 X/Open Distributed Transaction Processing(DTP)模型,也就是大家常說的 X/Open XA 協定,簡稱XA 協定。

幹貨|一篇文章帶你學習分布式事務

DTP 模型中包含一個全局事務管理器(TM,Transaction Manager)和多個資料總管(RM,Resource Manager)。全局事務管理器負責管理全局事務狀态與參與的資源,協同資源一起送出或復原;資料總管則負責具體的資源操作。

XA 協定描述了 TM 與 RM 之間的接口,允許多個資源在同一分布式事務中通路。

基于 DTP 模型的分布式事務流程大緻如下:
幹貨|一篇文章帶你學習分布式事務
  1. 應用程式(AP,Application)向 TM 申請開始一個全局事務。
  2. 針對要操作的 RM,AP 會先向 TM 注冊(TM 負責記錄 AP 操作過哪些 RM,即分支事務),TM 通過 XA 接口函數通知相應 RM 開啟分布式事務的子事務,接着 AP 就可以對該 RM 管理的資源進行操作。
  3. 當 AP 對所有 RM 操作完畢後,AP 根據執行情況通知 TM 送出或復原該全局事務,TM 通過 XA 接口函數通知各 RM 完成操作。TM 會先要求各個 RM 做預送出,所有 RM 傳回成功後,再要求各 RM 做正式送出,XA 協定要求,一旦 RM 預送出成功,則後續的正式送出也必須能成功;如果任意一個 RM 預送出失敗,則 TM 通知各 RM 復原。
  4. 所有 RM 送出或復原完成後,全局事務結束。
3.1.1 原子性

XA 協定使用 2PC(Two Phase Commit,兩階段送出)原子送出協定來保證分布式事務原子性。

兩階段送出是指将送出過程分為兩個階段,即準備階段(投票階段)和送出階段(執行階段):

幹貨|一篇文章帶你學習分布式事務
準備階段:

TM 向每個 RM 發送準備消息。如果 RM 的本地事務操作執行成功,則傳回成功;如果 RM 的本地事務操作執行失敗,則傳回失敗。

送出階段

如果 TM 收到了所有 RM 回複的成功消息,則向每個 RM 發送送出消息;否則發送復原消息;RM 根據 TM 的指令執行送出或者復原本地事務操作,釋放所有事務處理過程中使用的鎖資源。

3.1.2 隔離性

XA 協定中沒有描述如何實作分布式事務的隔離性,但是 XA 協定要求DTP 模型中的每個 RM 都要實作本地事務,也就是說,基于 XA 協定實作的分布式事務的隔離性是由每個 RM 本地事務的隔離性來保證的,當一個分布式事務的所有子事務都是隔離的,那麼這個分布式事務天然的就實作了隔離性。

以 MySQL 來舉例,MySQL 使用 2PL(Two-Phase Locking,兩階段鎖)機制來控制本地事務的并發,保證隔離性。2PL 與 2PC 類似,也是将鎖操作分為加鎖和解鎖兩個階段,并且保證兩個階段完全不相交。加鎖階段,隻加鎖,不放鎖。解鎖階段,隻放鎖,不加鎖。

幹貨|一篇文章帶你學習分布式事務

如上圖所示,在一個本地事務中,每執行一條更新操作之前,都會先擷取對應的鎖資源,隻有擷取鎖資源成功才會執行該操作,并且一旦擷取了鎖資源就會持有該鎖資源直到本事務執行結束。

MySQL 通過這種 2PL 機制,可以保證在本地事務執行過程中,其他并發事務不能操作相同資源,進而實作了事務隔離。

3.1.3 一緻性

前面提到一緻性有兩層語義,一層是確定事務執行結束後,資料庫從一個一緻狀态轉變為另一個一緻狀态。另一層語義是事務執行過程中的中間狀态不能被觀察到。

前一層語義的實作很簡單,通過原子性、隔離性以及 RM 自身一緻性的實作就可以保證。至于後一層語義,我們先來看看單個 RM 上的本地事務是怎麼實作的。還是以 MySQL 舉例,MySQL 通過 MVCC(Multi Version Concurrency Control,多版本并發控制)機制,為每個一緻性狀态生成快照(Snapshot),每個事務看到的都是各Snapshot對應的一緻性狀态,進而也就保證了本地事務的中間狀态不會被觀察到。

雖然單個 RM 上實作了Snapshot,但是在分布式應用架構下,會遇到什麼問題呢?

幹貨|一篇文章帶你學習分布式事務

如上圖所示,在 RM1 的本地子事務送出完畢到 RM2 的本地子事務送出完畢之間,隻能讀到 RM1 上子事務執行的内容,讀不到 RM2 上的子事務。也就是說,雖然在單個 RM 上的本地事務是一緻的,但是從全局來看,一個全局事務執行過程的中間狀态被觀察到了,全局一緻性就被破壞了。

XA 協定并沒有定義怎麼實作全局的 Snapshot,像 MySQL 官方文檔裡就建議使用串行化的隔離級别來保證分布式事務一緻性: “As with nondistributed transactions, SERIALIZABLE may be preferred if your applications are sensitive to read phenomena. REPEATABLE READ may not be sufficient for distributed transactions.”(對于分布式事務來說,可重複讀隔離級别不足以保證事務一緻性,如果你的程式有全局一緻性讀要求,可以考慮串行化隔離級别.)

當然,由于串行化隔離級别的性能較差,是以很多分布式資料庫都自己實作了分布式 MVCC 機制來提供全局的一緻性讀。一個基本思路是用一個集中式或者邏輯上單調遞增的東西來控制生成全局 Snapshot,每個事務或者每條 SQL 執行時都去擷取一次,進而實作不同隔離級别下的一緻性。比如 Google 的 Spanner 就是用 TrueTime 來控制通路全局 Snapshot。

3.1.4 小結

XA 協定通常實作在資料庫資源層,直接作用于資料總管上。是以,基于 XA 協定實作的分布式事務産品,無論是分布式資料庫,還是分布式事務架構,對業務幾乎都沒有侵入,就像使用普通資料庫一樣。

XA 協定嚴格保障事務 ACID 特性,能夠滿足所有業務領域的功能需求,但是,這同樣是一把雙刃劍。

由于隔離性的互斥要求,在事務執行過程中,所有的資源都被鎖定,隻适用于執行時間确定的短事務。同時,整個事務期間都是獨占資料,對于熱點資料的并發性能可能會很低,實作了分布式 MVCC 或樂觀鎖(optimistic locking)以後,性能可能會有所提升。

同時,為了保障一緻性,要求所有 RM 同等可信、可靠,要求故障恢複機制可靠、快速,在網絡故障隔離的情況下,服務基本不可用。

3.2 TCC 模型

TCC(Try-Confirm-Cancel)分布式事務模型相對于 XA 等傳統模型,其特征在于它不依賴資料總管(RM)對分布式事務的支援,而是通過對業務邏輯的分解來實作分布式事務。

TCC 模型認為對于業務系統中一個特定的業務邏輯,其對外提供服務時,必須接受一些不确定性,即對業務邏輯初步操作的調用僅是一個臨時性操作,調用它的主業務服務保留了後續的取消權。如果主業務服務認為全局事務應該復原,它會要求取消之前的臨時性操作,這就對應從業務服務的取消操作。而當主業務服務認為全局事務應該送出時,它會放棄之前臨時性操作的取消權,這對應從業務服務的确認操作。每一個初步操作,最終都會被确認或取消。

是以,針對一個具體的業務服務,TCC 分布式事務模型需要業務系統提供三段業務邏輯:
  1. 初步操作 Try:完成所有業務檢查,預留必須的業務資源。
  2. 确認操作 Confirm:真正執行的業務邏輯,不作任何業務檢查,隻使用 Try 階段預留的業務資源。是以,隻要 Try 操作成功,Confirm 必須能成功。另外,Confirm 操作需滿足幂等性,保證一筆分布式事務有且隻能成功一次。
  3. 取消操作 Cancel:釋放 Try 階段預留的業務資源。同樣的,Cancel 操作也需要滿足幂等性。
幹貨|一篇文章帶你學習分布式事務
TCC 分布式事務模型包括三部分:
  1. 主業務服務: 主業務服務為整個業務活動的發起方,服務的編排者,負責發起并完成整個業務活動。
  2. 從業務服務: 從業務服務是整個業務活動的參與方,負責提供 TCC 業務操作,實作初步操作(Try)、确認操作(Confirm)、取消操作(Cancel)三個接口,供主業務服務調用。
  3. 業務活動管理器: 業務活動管理器管理控制整個業務活動,包括記錄維護 TCC 全局事務的事務狀态和每個從業務服務的子事務狀态,并在業務活動送出時調用所有從業務服務的 Confirm 操作,在業務活動取消時調用所有從業務服務的 Cancel 操作。
一個完整的 TCC 分布式事務流程如下:
  1. 主業務服務首先開啟本地事務;
  2. 主業務服務向業務活動管理器申請啟動分布式事務主業務活動;
  3. 然後針對要調用的從業務服務,主業務活動先向業務活動管理器注冊從業務活動,然後調用從業務服務的 Try 接口;
  4. 當所有從業務服務的 Try 接口調用成功,主業務服務送出本地事務;若調用失敗,主業務服務復原本地事務;
  5. 若主業務服務送出本地事務,則 TCC 模型分别調用所有從業務服務的 Confirm 接口;若主業務服務復原本地事務,則分别調用 Cancel 接口;
  6. 所有從業務服務的 Confirm 或 Cancel 操作完成後,全局事務結束。
3.2.1 原子性

TCC 模型也使用 2PC 原子送出協定來保證事務原子性。Try 操作對應2PC 的一階段準備(Prepare);Confirm 對應 2PC 的二階段送出(Commit),Cancel 對應 2PC 的二階段復原(Rollback),可以說 TCC 就是應用層的 2PC。

3.2.2 隔離性

TCC 分布式事務模型僅提供兩階段原子送出協定,保證分布式事務原子性。事務的隔離交給業務邏輯來實作。

隔離的本質是控制并發,防止并發事務操作相同資源而引起的結果錯亂。

舉個例子,比如金融行業裡管理使用者資金,當使用者發起交易時,一般會先檢查使用者資金,如果資金充足,則扣除相應交易金額,增加賣家資金,完成交易。如果沒有事務隔離,使用者同時發起兩筆交易,兩筆交易的檢查都認為資金充足,實際上卻隻夠支付一筆交易,結果兩筆交易都支付成功,導緻資損。

可以發現,并發控制是業務邏輯執行正确的保證,但是像兩階段鎖這樣的并發通路控制技術要求一直持有資料庫資源鎖直到整個事務執行結束,特别是在分布式事務架構下,要求持有鎖到分布式事務第二階段執行結束,也就是說,分布式事務會加長資源鎖的持有時間,導緻并發性能進一步下降。

是以,TCC 模型的隔離性思想就是通過業務的改造,在第一階段結束之後,從底層資料庫資源層面的加鎖過渡為上層業務層面的加鎖,進而釋放底層資料庫鎖資源,放寬分布式事務鎖協定,提高業務并發性能。

還是以上面的例子舉例:

  1. 第一階段:檢查使用者資金,如果資金充足,當機使用者本次交易資金,這筆資金被業務隔離,不允許除本事務之外的其它并發事務動用。
  2. 第二階段:扣除第一階段預當機的使用者資金,增加賣家資金,完成交易。 采用業務加鎖的方式,隔離使用者當機資金,在第一階段結束後直接釋放底層資源鎖,該使用者和賣家的其他交易都可以立刻并發執行,而不用等到整個分布式事務結束,可以獲得更高的并發交易能力。
3.2.3 一緻性

再來看看 TCC 分布式事務模型下的一緻性實作。與 XA 協定實作一緻性第一層語義類似,通過原子性保證事務的原子送出、業務隔離性控制事務的并發通路,實作分布式事務的一緻性狀态轉變。

至于第二層語義:事務的中間狀态不能被觀察到。我們來看看,在 SOA分布式應用環境下是否是必須的。

還是以賬務服務舉例。轉賬業務(使用者 A  使用者 B),由交易服務和賬務服務組成分布式事務,交易服務作為主業務服務,賬務服務作為從業務服務,賬務服務的 Try 操作預當機使用者 A 的資金;Commit 操作扣除使用者 A 的預當機資金,增加使用者 B 的可用資金;Cancel 操作解凍使用者 A 的預當機資金。

當賬務服務執行完 Try 階段後,交易主業務就可以 Commit 了,然後由TCC 架構調用賬務的 Commit 階段。在賬務 Commit 階段還沒執行結束的時候,使用者 A 可以查詢到自己的餘額已扣除,但是,此時使用者 B 的可用資金還沒增加。

從系統的角度來看,确實有問題與不确定性。在第一階段執行結束到第二階段執行結束之間,有一段時間的延時,在這段時間内,看似任何使用者都不享有這筆資産。

但是,從使用者的角度來考慮這個問題的話,這個時間間隔可能就無所謂或者根本就不存在。特别是當這個時間間隔僅僅是幾秒鐘,對于具體溝通資産轉移的使用者來講,這個過程是隐蔽的或确實可以接受的,且保證了結果的最終一緻性。

當然,對于這樣的系統,如果确實需要檢視系統的某個一緻性狀态,可以采用額外的方法實作。

一般來講,服務之間的一緻性比服務内部的一緻性要更加容易弱化,這也是為什麼 XA 等直接在資源層面上實作通用分布式事務的模型會注重一緻性的保證,而當上升到服務層面,服務與服務之間已經實作了功能的劃分,邏輯的解耦,也就更容易弱化一緻性,這就是 SOA 架構下 BASE 理論的最終一緻性思想。

BASE 理論是指 BA(Basic Availability,基本業務可用性);S(Soft state,柔性狀态);E(Eventual consistency,最終一緻性)。

該理論認為為了可用性、性能與降級服務的需要,可以适當降低一點一緻性的要求,即“基本可用,最終一緻”。

業内通常把嚴格遵循 ACID 的事務稱為剛性事務;而基于 BASE 思想實作的事務稱為柔性事務。柔性事務并不是完全放棄了 ACID,僅僅是放寬了一緻性要求:事務完成後的一緻性嚴格遵循,事務中的一緻性可适當放寬;

3.2.4 小結

TCC 分布式事務模型的業務實作特性決定了其可以跨 DB、跨服務實作資源管理,将對不同的 DB 通路、不同的業務操作通過 TCC 模型協調為一個原子操作,解決了分布式應用架構場景下的事務問題。

TCC 模型通過 2PC 原子送出協定保證分布式事務的的原子性,把資源層的隔離性上升到業務層,交給業務邏輯來實作。TCC 的每個操作對于資源層來說,就是單個本地事務的使用,操作結束則本地事務結束,規避了資源層在 2PC 和 2PL 下對資源占用導緻的性能低下問題。

同時,TCC 模型也可以根據業務需要,做一些定制化的功能,比如交易異步化實作削峰填谷等。

但是,業務接入 TCC 模型需要拆分業務邏輯成兩個階段,并實作 Try、Confirm、Cancel 三個接口,定制化程度高,開發成本高。

四. 總結

本文首先介紹了典型的分布式事務的架構場景。分布式事務剛開始是為解決單服務多資料庫資源的場景而誕生的。随着技術的發展,特别是 SOA 分布式應用架構以及微服務時代的到來,服務變成了基本業務單元。是以,又産生了跨服務的分布式事務需求。然後從 XA 和 TCC 兩種常用的分布式事務模型入手,介紹了其實作機制,着重分析了各模型是如何實作分布式事務 ACID 特性的。

— END —

螞蟻金服科技,隻為分享幹貨, 您的轉發是對我們最大的支援

歡迎在文章下方留言與我們進行交流哦~