Seata學習筆記一:初識Seata(持續更新中)

什麼是Seata?

官網定義:Seata 是一款開源的分佈式事務解決方案,致力於提供高性能簡單易用的分佈式事務服務。Seata 爲用戶提供了 AT、TCC、SAGA 和 XA 事務模式,爲用戶打造一站式的分佈式解決方案。

Seata三要素

TC - 事務協調者:維護全局和分支事務的狀態,驅動全局事務提交或回滾。

TM - 事務管理器:定義全局事務的範圍 ---- 開始全局事務、提交或回滾全局事務。

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

什麼是Seata AT模式?

定義

總結一下:Seata AT模式是兩階段提交協議的演變。第一階段,基於JDBC數據源代理通過對業務SQL進行解析,把業務數據在更新前後的數據鏡像組織成回滾日誌,將業務數據的更新和回滾日誌的寫入通過同一本地事務進行提交,利用本地事務ACID特性保證任何業務數據的更新一定有對應的回滾日誌。第二階段異步清理回滾日誌或者根據回滾日誌進行業務數據的回滾操作。

第一階段的重點就是完成分支事務的註冊與提交,保證業務數據的更新與對應回滾日誌的一致性,其根本目的是爲第二階段可能的回滾操作提供數據支撐。實質上,第一階段結束,本地事務已經完成提交,業務數據的更新已經寫入數據庫,因此在第二階段,如果全局事務結果是提交,那麼對於分支事務系統而言,僅僅只需要根據全局事務ID和分支事務ID找到對應undo_log並刪除即可:

如果第二階段全局決議結果是回滾,則需要根據XID和branch ID找到對應的undo_log記錄,並根據記錄生成反向的更新SQL並執行,完成分支事務的回滾:

AT模式的隔離性實現

從上面的流程可以看出,AT模式在第一階段完成後實質上已經完成了本地事務的提交,那麼當數據庫事務隔離級別爲讀已提交及以上時,在全局事務完成提交前,無法保證分支事務提交數據的不可讀及不可寫。那麼,AT模式是如何解決隔離性的問題呢?

Seata AT模式引入了全局鎖機制來實現隔離。

寫隔離:

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

以一個示例來說明:

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

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

要重試等待 全局鎖 。

Write-Isolation: Commit

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

Write-Isolation: Rollback

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

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

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

讀隔離:

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

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

Read Isolation: SELECT FOR UPDATE

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

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

Seata 的全局鎖是由 TC 也就是 server 來集中維護,而不是在數據庫維護的

這樣做有兩點好處:

  • 一方面:鎖的釋放非常快,尤其是在全局提交的情況下,收到全局提交的請求,鎖馬上就釋放掉了,不需要與 RM 或數據庫進行一輪交互。
  • 另外一方面:因爲鎖不是數據庫維護的,從數據庫層面看,數據沒有鎖定。這也就是給極端情況下,業務 降級 提供了方便,事務協調器異常導致的一部分異常事務,不會 block 後面業務的繼續進行。

然而,壞處是,Seata無法保證數據不被全局事務之外的其他操作進行修改。

AT模式相對於2PC的改進

傳統的2PC模式中,所有參與節點都是事務阻塞型的,事務節點對資源的佔有持續於整個全局事務期間,嚴重影響系統性能,並且一旦事務協調者出現問題問題,事務參與者會一直阻塞下去,導致整個業務系統出現問題。

AT模式下所有分支事務系統選擇在第一階段執行完畢立即釋放本地鎖和鏈接資源,以本地事務的形式實現分支系統業務數據的更新,避免長時間佔有公共數據資源,保證了系統的吞吐量。同時,通過TC維護全局鎖,避免了當事務協調器出現問題時導致的整個業務系統的阻塞。

AT模式的優缺點

優點

1、低成本,業務無侵入,實現簡單

2、高性能,協議不阻塞,資源釋放快,保障業務的吞吐

3、高可用:極端的異常情況下,可以暫時 跳過異常事務,保證整個業務系統的高可用

缺點

1、SDK依賴較重,需要搭建TC服務器,可用性受限於TC集羣

2、嚴重依賴本地事務,數據庫本身必須支持本地事務

3、隔離性依賴TC,無法保證高隔離性

官網示例:

以一個示例來說明整個 AT 分支的工作過程。

業務表:product

Field Type Key
id bigint(20) PRI
name varchar(100)  
since varchar(100)  

AT 分支事務的業務邏輯:

update product set name = 'GTS' where name = 'TXC';

一階段

過程:

  1. 解析 SQL:得到 SQL 的類型(UPDATE),表(product),條件(where name = 'TXC')等相關的信息。
  2. 查詢前鏡像:根據解析得到的條件信息,生成查詢語句,定位數據。
select id, name, since from product where name = 'TXC';

得到前鏡像:

id name since
1 TXC 2014
  1. 執行業務 SQL:更新這條記錄的 name 爲 'GTS'。
  2. 查詢後鏡像:根據前鏡像的結果,通過 主鍵 定位數據。
select id, name, since from product where id = 1`;

得到後鏡像:

id name since
1 GTS 2014
  1. 插入回滾日誌:把前後鏡像數據以及業務 SQL 相關的信息組成一條回滾日誌記錄,插入到 UNDO_LOG表中。
{
	"branchId": 641789253,
	"undoItems": [{
		"afterImage": {
			"rows": [{
				"fields": [{
					"name": "id",
					"type": 4,
					"value": 1
				}, {
					"name": "name",
					"type": 12,
					"value": "GTS"
				}, {
					"name": "since",
					"type": 12,
					"value": "2014"
				}]
			}],
			"tableName": "product"
		},
		"beforeImage": {
			"rows": [{
				"fields": [{
					"name": "id",
					"type": 4,
					"value": 1
				}, {
					"name": "name",
					"type": 12,
					"value": "TXC"
				}, {
					"name": "since",
					"type": 12,
					"value": "2014"
				}]
			}],
			"tableName": "product"
		},
		"sqlType": "UPDATE"
	}],
	"xid": "xid:xxx"
}
  1. 提交前,向 TC 註冊分支:申請 product 表中,主鍵值等於 1 的記錄的 全局鎖 。
  2. 本地事務提交:業務數據的更新和前面步驟中生成的 UNDO LOG 一併提交。
  3. 將本地事務提交的結果上報給 TC。

二階段-回滾

  1. 收到 TC 的分支回滾請求,開啓一個本地事務,執行如下操作。
  2. 通過 XID 和 Branch ID 查找到相應的 UNDO LOG 記錄。
  3. 數據校驗:拿 UNDO LOG 中的後鏡與當前數據進行比較,如果有不同,說明數據被當前全局事務之外的動作做了修改。這種情況,需要根據配置策略來做處理,詳細的說明在另外的文檔中介紹。
  4. 根據 UNDO LOG 中的前鏡像和業務 SQL 的相關信息生成並執行回滾的語句:
update product set name = 'TXC' where id = 1;
  1. 提交本地事務。並把本地事務的執行結果(即分支事務回滾的結果)上報給 TC。

二階段-提交

  1. 收到 TC 的分支提交請求,把請求放入一個異步任務的隊列中,馬上返回提交成功的結果給 TC。
  2. 異步任務階段的分支提交請求將異步和批量地刪除相應 UNDO LOG 記錄。

什麼是Seata TCC模式?

TCC模式是指支持把 自定義 的分支事務納入到全局事務的管理中的事務處理模式,是與AT模式相對的,不依賴於底層數據資源的事務支持:

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

後續更新。。。

什麼是Seata Saga模式?

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

後續更新。。。

 

 

 

 

 

 

參考資料:

http://seata.io/zh-cn/docs/overview/what-is-seata.html

https://blog.csdn.net/hosaos/article/details/89403552

https://www.jianshu.com/p/0ed828c2019a

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章