Seata


Seata

  Seata 是 Simple Extensible Autonomous Transaction Architecture 的簡寫,阿里開源的分布式事務框架,屬於二階段提交模式,致力於提供高性能和簡單易用的分布式事務服務。Seata 將為用戶提供了 AT、TCC、SAGA 和 XA 事務模式,為用戶打造一站式的分布式解決方案。

Seata術語 

  TC (Transaction Coordinator) - 事務協調者

  維護全局和分支事務的狀態,驅動全局事務提交或回滾。

  TM (Transaction Manager) - 事務管理器

  定義全局事務的范圍:開始全局事務、提交或回滾全局事務。

  RM (Resource Manager) - 資源管理器

  管理分支事務處理的資源,與TC交談以注冊分支事務和報告分支事務的狀態,並驅動分支事務提交或回滾。

Seata重要機制

  1 全局事務的回滾是如何實現的呢?

  Seata有一個重要的機制:回滾日志。每個分支事務對應的數據庫中都需要有一個回滾日志表 UNDO_LOG,在真正修改數據庫記錄之前,都會先記錄修改前的記錄值,以便之后回滾。在收到回滾請求后,就會根據 UNDO_LOG 生成回滾操作的 SQL 語句來執行。如果收到的是提交請求,就把 UNDO_LOG 中的相應記錄刪除掉。

  2 RM是怎么自動和TC交互的?

  是通過監控攔截JDBC實現的,例如監控到開啟本地事務了,就會自動向 TC 注冊、生成回滾日志、向 TC 匯報執行結果。

  3 二階段回滾失敗怎么辦?

  例如TC命令各個RM回滾的時候,有一個微服務掛掉了,那么所有正常的微服務也都不會執行回滾,當這個微服務重新正常運行后,TC會重新執行全局回滾。

  4 undo_log表log_status=1的記錄是做什么用的?

  • 場景 : 分支事務a注冊TC后,a的本地事務提交前發生了全局事務回滾
  • 后果 : 全局事務回滾成功,a資源被占用掉,產生了資源懸掛問題
  • 防懸掛措施: a回滾時發現回滾undo還未插入,則插入一條log_status=1的undo記錄,a本地事務(業務寫操作sql和對應undo為一個本地事務)提交時會因為undo表唯一索引沖突而提交失敗。

 

AT 模式

前提

  • 基於支持本地 ACID 事務的關系型數據庫。
  • Java 應用,通過 JDBC 訪問數據庫。

整體機制

  兩階段提交協議的演變:

  • 一階段:業務數據和回滾日志記錄在同一個本地事務中提交,釋放本地鎖和連接資源。

  • 二階段:

    • 提交異步化,非常快速地完成。
    • 回滾通過一階段的回滾日志進行反向補償。

具體工作過程

  • TM 請求 TC,開始一個新的全局事務,TC 會為這個全局事務生成一個 XID。
  • XID 通過微服務的調用鏈傳遞到其他微服務。
  • RM 把本地事務作為這個XID的分支事務注冊到TC。
  • TM 請求 TC 對這個 XID 進行提交或回滾。
  • TC 指揮這個 XID 下面的所有分支事務進行提交、回滾

寫隔離

  • 一階段本地事務提交前,需要確保先拿到 全局鎖
  • 拿不到 全局鎖 ,不能提交本地事務。
  • 全局鎖 的嘗試被限制在一定范圍內,超出范圍將放棄,並回滾本地事務,釋放本地鎖。

  以一個示例來說明:

  兩個全局事務 tx1 和 tx2,分別對 a 表的 m 字段進行更新操作,m 的初始值 1000。

  tx1 先開始,開啟本地事務,拿到本地鎖,更新操作 m = 1000 - 100 = 900。本地事務提交前,先拿到該記錄的 全局鎖 ,本地提交釋放本地鎖。 tx2 后開始,開啟本地事務,拿到本地鎖,更新操作 m = 900 - 100 = 800。本地事務提交前,嘗試拿該記錄的 全局鎖 ,tx1 全局提交前,該記錄的全局鎖被 tx1 持有,tx2 需要重試等待 全局鎖

tx1 二階段全局提交,釋放 全局鎖 。tx2 拿到 全局鎖 提交本地事務。

  如果 tx1 的二階段全局回滾,則 tx1 需要重新獲取該數據的本地鎖,進行反向補償的更新操作,實現分支的回滾。

  此時,如果 tx2 仍在等待該數據的 全局鎖,同時持有本地鎖,則 tx1 的分支回滾會失敗。分支的回滾會一直重試,直到 tx2 的 全局鎖 等鎖超時,放棄 全局鎖 並回滾本地事務釋放本地鎖,tx1 的分支回滾最終成功。

  因為整個過程 全局鎖 在 tx1 結束前一直是被 tx1 持有的,所以不會發生 臟寫 的問題

  SELECT FOR UPDATE 語句的執行會申請 全局鎖 ,如果 全局鎖 被其他事務持有,則釋放本地鎖(回滾 SELECT FOR UPDATE 語句的本地執行)並重試。這個過程中,查詢是被 block 住的,直到 全局鎖 拿到,即讀取的相關數據是 已提交 的,才返回。

  出於總體性能上的考慮,Seata 目前的方案並沒有對所有 SELECT 語句都進行代理,僅針對 FOR UPDATE 的 SELECT 語句。

讀隔離

  在數據庫本地事務隔離級別 讀已提交(Read Committed) 或以上的基礎上,Seata(AT 模式)的默認全局隔離級別是 讀未提交(Read Uncommitted)

如果應用在特定場景下,必需要求全局的 讀已提交 ,目前 Seata 的方式是通過 SELECT FOR UPDATE 語句的代理。

 

TCC 模式

  回顧總覽中的描述:一個分布式的全局事務,整體是 兩階段提交 的模型。全局事務是由若干分支事務組成的,分支事務要滿足 兩階段提交 的模型要求,即需要每個分支事務都具備自己的:

  • 一階段 prepare 行為
  • 二階段 commit 或 rollback 行為

 

  根據兩階段行為模式的不同,我們將分支事務划分為 Automatic (Branch) Transaction ModeManual (Branch) Transaction Mode.

AT 模式基於 支持本地 ACID 事務關系型數據庫

  • 一階段 prepare 行為:在本地事務中,一並提交業務數據更新和相應回滾日志記錄。
  • 二階段 commit 行為:馬上成功結束,自動 異步批量清理回滾日志。
  • 二階段 rollback 行為:通過回滾日志,自動 生成補償操作,完成數據回滾。

相應的,TCC 模式,不依賴於底層數據資源的事務支持:

  • 一階段 prepare 行為:調用 自定義 的 prepare 邏輯。
  • 二階段 commit 行為:調用 自定義 的 commit 邏輯。
  • 二階段 rollback 行為:調用 自定義 的 rollback 邏輯。

所謂 TCC 模式,是指支持把 自定義 的分支事務納入到全局事務的管理中。

 

Saga 模式

  Saga模式是SEATA提供的長事務解決方案,在Saga模式中,業務流程中每個參與者都提交本地事務,當出現某一個參與者失敗則補償前面已經成功的參與者,一階段正向服務和二階段補償服務都由業務開發實現

 理論基礎:Hector & Kenneth 發表論⽂ Sagas (1987)

 

XA 模式

前提

  • 支持XA 事務的數據庫。
  • Java 應用,通過 JDBC 訪問數據庫

整體機制

  在 Seata 定義的分布式事務框架內,利用事務資源(數據庫、消息服務等)對 XA 協議的支持,以 XA 協議的機制來管理分支事務的一種 事務模式。

  • 執行階段:

    • 可回滾:業務 SQL 操作放在 XA 分支中進行,由資源對 XA 協議的支持來保證 可回滾
    • 持久化:XA 分支完成后,執行 XA prepare,同樣,由資源對 XA 協議的支持來保證 持久化(即,之后任何意外都不會造成無法回滾的情況)
  • 完成階段:

    • 分支提交:執行 XA 分支的 commit
    • 分支回滾:執行 XA 分支的 rollback

工作機制

1. 整體運行機制

XA 模式 運行在 Seata 定義的事務框架內:

 

  • 執行階段(E xecute):

    • XA start/XA end/XA prepare + SQL + 注冊分支
  • 完成階段(F inish):

    • XA commit/XA rollback

2. 數據源代理

  XA 模式需要 XAConnection。

  獲取 XAConnection 兩種方式:

  • 方式一:要求開發者配置 XADataSource
  • 方式二:根據開發者的普通 DataSource 來創建

  第一種方式,給開發者增加了認知負擔,需要為 XA 模式專門去學習和使用 XA 數據源,與 透明化 XA 編程模型的設計目標相違背。

  第二種方式,對開發者比較友好,和 AT 模式使用一樣,開發者完全不必關心 XA 層面的任何問題,保持本地編程模型即可。

  我們優先設計實現第二種方式:數據源代理根據普通數據源中獲取的普通 JDBC 連接創建出相應的 XAConnection。類比 AT 模式的數據源代理機制,如下:

  但是,第二種方法有局限:無法保證兼容的正確性。

  實際上,這種方法是在做數據庫驅動程序要做的事情。不同的廠商、不同版本的數據庫驅動實現機制是廠商私有的,我們只能保證在充分測試過的驅動程序上是正確的,開發者使用的驅動程序版本差異很可能造成機制的失效。

  這點在 Oracle 上體現非常明顯。參見 Druid issue:https://github.com/alibaba/druid/issues/3707

  綜合考慮,XA 模式的數據源代理設計需要同時支持第一種方式:基於 XA 數據源進行代理。類比 AT 模式的數據源代理機制,如下:

3. 分支注冊

  XA start 需要 Xid 參數。這個 Xid 需要和 Seata 全局事務的 XID 和 BranchId 關聯起來,以便由 TC 驅動 XA 分支的提交或回滾。

  目前 Seata 的 BranchId 是在分支注冊過程,由 TC 統一生成的,所以 XA 模式分支注冊的時機需要在 XA start 之前。

  將來一個可能的優化方向:

  把分支注冊盡量延后。類似 AT 模式在本地事務提交之前才注冊分支,避免分支執行失敗情況下,沒有意義的分支注冊。這個優化方向需要 BranchId 生成機制的變化來配合。BranchId 不通過分支注冊過程生成,而是生成后再帶着 BranchId 去注冊分支。

XA 模式的使用

  從編程模型上,XA 模式與 AT 模式保持完全一致。可以參考 Seata 官網的樣例:seata-xa

  樣例場景是 Seata 經典的,涉及庫存、訂單、賬戶 3 個微服務的商品訂購業務。在樣例中,上層編程模型與 AT 模式完全相同。只需要修改數據源代理,即可實現 XA 模式與 AT 模式之間的切換。

 

 

 更多信息參見:https://seata.io/zh-cn/docs/overview/what-is-seata.html

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM