微服務架構下的分散式事務

一提到分散式事務,就不得不說XA規範、TCC事務模型。

XA規範

分散式事務中耳熟能詳的全域性事務管理器(Transaction Manager)以及資源管理器(Resource Manager)。就是XA規範定義的。

XA協議提供了資源管理器(資料庫)和事務管理器之間的介面標準。XA介面是雙向的系統介面,在事務管理器和一個或者多個資源管理器之間建成通訊橋樑,事務管理器充當全域性事務中的協調者,控制著全域性事務,管理事務的生命週期,協調資源, 一般常見的事務管理器(TM)有JDBC、hibernate框架中的TransacManager等。資源管理器控制和管理實際的資源,比如資料庫。目前各大資料庫產家都支援這種標準。

XA 事務的執行流程 XA

兩階段提交的分散式事務,XA將一次事務分割成兩個階段,Prepare準備階段和Commit提交階段。

Prepare階段,TM向所有的RM傳送prepare指令,RM接收到指令以後,就會執行資料的修改和日誌記錄操作(binlog和redo log)。然後返回能不能提交的訊息給TM。TM假如接收到所有的參與者返回的訊息,會通知所有的RM進行提交,於是進入第二階段。

Commit階段,TM接收到所有事務參與者RM返回的Prepare結果,如果RM全部返回提交,則事務進行提交,假如存在某個RM返回不能提交或者超時的情況,則所有的事務進行回滾操作。

MySQL如何實現XA規範

大多數生產環境中的Mysql都會開啟Bin log日誌,於是為了為了兩套日誌的資料一致性,Mysql就是使用了XA事務。

當事務提交的時候,Binlog中會新增一個XID_EVENT作為事務的結束,該事件記錄了事務的ID就是XID。

Binlog事務的提交過程,是先寫redo log,再寫binlog,以binlog寫成功為事務提交成功的標誌。

微服務架構下的分散式事務

事務提交:

第一步,prepare階段,寫入redo log。binlog不做任何操作。

第二步,寫入binlog。

第三步,事務提交。

第一步和第二步失敗,整個事務會回滾,第三步失敗,事務會進行再次提交。

Spring事務

眾所周知,Spring事務採用AOP的方式實現,Spring中的事務機制和業務程式碼幾乎不存在耦合關係。

獲取事務的屬性(@Transactional註解中的配置,比如spring的事務傳播機制)

載入spring 配置中的事務管理器PlatformTransactionManager。

微服務架構下的分散式事務

獲取事務的資訊TransactionInfo

微服務架構下的分散式事務

執行目標方法

提交或者回滾

Spring 提供了超類PlatformTransactionManager,Hibernate自定義實現了自身框架的事務管理器HibernateTransactionManager。HibernateTransactionManager中會開啟sessionFactory,建立Session,執行事務,關閉session。session在開啟過程中還存在Hibernate的一級快取,查詢資料的時候會先進行session一級快取的查詢,然後在進行DB的查詢。

spring事務的傳播級別

PROPAGATION_REQUIRED 支援當前事務,如果當前沒有事務,就新建一個事務。這是最常見的選擇,也是 Spring 預設的事務的傳播。 PROPAGATION_REQUIRES_NEW 新建事務,如果當前存在事務,把當前事務掛起。新建的事務將和被掛起的事務沒有任何關係,是兩個獨立的事務,外層事務失敗回滾之後,不能回滾內層事務執行的結果,內層事務失敗丟擲異常,外層事務捕獲,也可以不處理回滾操作。

PROPAGATION_NOT_SUPPORTED 以非事務方式執行操作,如果當前存在事務,就把當前事務掛起。

spring事務不生效的場景

private、final、static方法,事務不生效,入口方法必須是public ,spring的AOp特性決定的,spring認為private自己用的方法應該自己控制,不應該用事務切進去

在同一個類中一個無事務的方法呼叫另一個有事務的方法,事務是不會起作用的

如果使用的是rollbakfor的預設,已檢查的異常(所有派生自Error和RuntimeException的類,都是未檢查異常。其餘的是已檢查異常, 比如nullPointException是未檢查的,IllegalAccessException 是已檢查的)不回滾, 可設為rollbackFor={Exception。class}

TCC事務模型

TCC提出了一種新的事務模型,是基於業務層面的事務定義,鎖粒度完全由業務自己控制,目的是解決複雜業務中,跨表跨庫等大顆粒度資源鎖定的問題。TCC把事務執行過程分成Try、Confirm/Cancel兩個階段,每個階段的邏輯由業務程式碼控制,避免了長事務,可以獲取更高的效能。

微服務架構下的分散式事務

Try 階段: 呼叫 Try 介面,嘗試執行業務,完成所有業務檢查,預留業務資源。Confirm 或 Cancel 階段: 兩者是互斥的,只能進入其中一個,並且都滿足冪等性,允許失敗重試。

Confirm 操作: 對業務系統做確認提交,確認執行業務操作,不做其他業務檢查,只使用 Try 階段預留的業務資源。Cancel 操作: 在業務執行錯誤,需要回滾的狀態下執行業務取消,釋放預留資源。

Try 階段失敗可以 Cancel,如果 Confirm 和 Cancel 階段失敗了怎麼辦?

TCC 中會新增事務日誌,如果 Confirm 或者 Cancel 階段出錯,則會進行重試,所以這兩個階段需要支援冪等;如果重試失敗,則需要人工介入進行恢復和處理等。

實際開發中,TCC 的本質是把資料庫的二階段提交上升到微服務來實現,從而避免資料庫二階段中長事務引起的低效能風險。

所以說,TCC 解決了跨服務的業務操作原子性問題,比如下訂單減庫存,多渠道組合支付等場景,透過 TCC 對業務進行拆解,可以讓應用自己定義資料庫操作的粒度,可以降低鎖衝突,提高系統的業務吞吐量。

TCC 的不足主要體現在對微服務的侵入性強,TCC 需要對業務系統進行改造,業務邏輯的每個分支都需要實現 try、Confirm、Cancel 三個操作,並且 Confirm、Cancel 必須保證冪等。

為了應用 TCC 事務模型,需要對業務程式碼改造,抽象 Try、Confirm 和 Cancel 階段。

Try 操作一般都是鎖定某個資源,設定一個預備的狀態,凍結部分資料。比如,訂單服務新增一個預備狀態,修改為 UPDATING,也就是更新中的意思,凍結當前訂單的操作,而不是直接修改為支付成功。庫存服務設定凍結庫存,可以擴充套件欄位,也可以額外新增新的庫存凍結表。積分服務和庫存一樣,新增一個預增加積分,比如本次訂單積分是 100,新增一個額外的儲存表示等待增加的積分,賬戶餘額服務等也是一樣的操作。

Confirm 操作就是把前邊的 Try 操作鎖定的資源提交,類比資料庫事務中的 Commit 操作。在支付的場景中,包括訂單狀態從準備中更新為支付成功;庫存資料扣減凍結庫存,積分資料增加預增加積分。

Cancel 操作執行的是業務上的回滾處理,類比資料庫事務中的 Rollback 操作。首先訂單服務,撤銷預備狀態,還原為待支付狀態或者已取消狀態,庫存服務刪除凍結庫存,新增到可銷售庫存中,積分服務也是一樣,將預增加積分扣減掉。

業務請求過來,開始執行 Try 操作,如果 TCC 分散式事務框架感知到各個服務的 Try 階段都成功了以後,就會執行各個服務的 Confirm 邏輯。

如果 Try 階段有操作不能正確執行,比如訂單失效、庫存不足等,就會執行 Cancel 的邏輯,取消事務提交。

關於TCC的事務模型,國記憶體在很多的開源框架。如ByteTCC,TCC-transaction,Himly等。

比較下XA事務和TCC事務的區別

微服務架構下的分散式事務

2PC/XA 是資料庫或者儲存資源層面的事務,實現的是強一致性,在兩階段提交的整個過程中,一直會持有資料庫的鎖。

TCC 關注業務層的正確提交和回滾,在 Try 階段不涉及加鎖,是業務層的分散式事務,關注最終一致性,不會一直持有各個業務資源的鎖。

業內還存在一種3PC的分散式事務,與2PC不同的地方是引入了超時機制,在第一階段和第二階段中新增了一個準備階段。這裡的Can-Commit是嘗試獲取資料庫鎖,第二階段和第三階段同2PC中一致,

微服務架構下的分散式事務

我是凱騰凱,網際網路浪潮下一枚苟且偷生的程式設計師

微服務架構下的分散式事務