一,什么是事務(本地事務)?
指作為單個邏輯作業單元執行的一系列操作,要么完全地執行,要么完全地不執行,
簡單的說,事務就是并發控制的單位,是用戶定義的一個操作序列,
而一個邏輯作業單元要成為事務,就必須滿足ACID屬性,
A:原子性(Atomicity)
事務中的操作要么都不做,要么就全做,
C:一致性(Consistency)
事務執行的結果必須是從資料庫從一個一致性狀態轉換到另一個一致性狀態,
I:隔離性(Isolation)
一個事務的執行不能被其他事務干擾
D:持久性(Durability)
一個事務一旦提交,它對資料庫中資料的改變就應該是永久性的,
注:如果對事務不是很懂可以參考這篇文章:什么是事務?事務的四個特性以及事務的隔離級別 - Kevin.ZhangCG - 博客園 (cnblogs.com)
二, 分布式事務
? 隨著互聯網的快速發展,軟體系統由原來的單體應用轉變為分布式應用,下圖描述了單體應用向微服務的演變:

? 分布式系統會把一個應用系統拆分為可獨立部署的多個服務,因此需要服務與服務之間遠程協作才能完成事務操作,這種分布式系統環境下由不同的服務之間通過網路遠程協作完成事務稱之為分布式事務,
三,分布式事務產生的情景
- 跨JVM行程產生分布式事務
典型的場景就是微服務架構:微服務之間通過遠程呼叫完成事務操作,比如:訂單微服務和庫存微服務,下單的同時訂單微服務請求庫存微服務減少庫存,

- 跨資料庫實體產生分布式事務
單體系統訪問多個資料庫實體當單體系統需要訪問多個資料庫(實體)時就會產生分布式事務,比如:用戶資訊和訂單資訊分別在兩個MySQL實體存盤,用戶管理系統洗掉用戶資訊,需要分別洗掉用戶資訊及用戶的訂單資訊,由于資料分布在不同的資料實體,需要通過不同的資料庫鏈接去操作資料,此時產生分布式事務,

- 多服務訪問同一個資料庫實體
訂單微服務和庫存微服務即使訪問同一個資料庫也會產生分布式事務,原因就是跨JVM行程,兩個微服務持有了不同的資料庫鏈接進行資料庫操作,此時產生分布式事務,

四, 分布式事務解決方案之 2PC
常見分布式事務解決方案:
1.seata 阿里分布式事務框架
2. 訊息佇列
3.sage
4,XA
但是他們都有一個共同點:都是兩個階段(2PC),其實這四種解決方案對應的是分布式事務的四種模式:AT,TCC,Sage,XA
4.1 什么是 2PC
2PC 即兩階段提交協議,是將整個事務流程分為兩個階段,準備階段(Prepare phase)、提交階段(commit phase),2 是指兩個階段,P 是指準備階段,C 是指提交階段,
舉例:張三和李四好久不見,老友約起聚餐,飯店老板要求先買單,才能出票,這時張三和李四分別抱怨近況不如意,囊中羞澀,都不愿意請客,這時只能AA,只有張三和李四都付款,老板才能出票安排就餐,但由于張三和李四都是鐵公雞,形成了尷尬的一幕:
準備階段:老板要求張三付款,張三付款,老板要求李四付款,李四付款,
提交階段:老板出票,兩人拿票紛紛落座就餐,
例子中形成了一個事務,若張三或李四其中一人拒絕付款,或錢不夠,店老板都不會給出票,并且會把已收款退回,
整個事務程序由事務管理器和參與者組成,店老板就是事務管理器,張三、李四就是事務參與者,事務管理器負責決策整個分布式事務的提交和回滾,事務參與者負責自己本地事務的提交和回滾,
在計算機中部分關系資料庫如 Oracle、MySQL 支持兩階段提交協議,如下圖:
- 準備階段(Prepare phase):事務管理器給每個參與者發送 Prepare 訊息,每個資料庫參與者在本地執行事務,并寫本地的 Undo/Redo 日志,此時事務沒有提交,(Undo 日志是記錄修改前的資料,用于資料庫回滾,Redo 日志是記錄修改后的資料,用于提交事務后寫入資料檔案)
- 提交階段(commit phase):如果事務管理器收到了參與者的執行失敗或者超時訊息時,直接給每個參與者發送回滾(Rollback)訊息;否則,發送提交(Commit)訊息;參與者根據事務管理器的指令執行提交或者回滾操作,并釋放事務處理程序中使用的鎖資源,注意:必須在最后階段釋放鎖資源,
下圖展示了2PC的兩個階段,分成功和失敗兩個情況說明:
成功情況

失敗情況

五,Seata 方案(AT模式)
Seata 是由阿里中間件團隊發起的開源專案 Fescar,后更名為 Seata,它是一個是開源的分布式事務框架,
傳統 2PC 的問題在 Seata 中得到了解決,它通過對本地關系資料庫的分支事務的協調來驅動完成全域事務,是作業在應用層的中間件,主要優點是性能較好,且不長時間占用連接資源,它以高效并且對業務 0 侵入的方式解決微服務場景下面臨的分布式事務問題,它目前提供 AT 模式(即 2PC)及 TCC 模式的分布式事務解決方案,
Seata 的設計思想如下:
Seata 的設計目標其一是對業務無侵入,因此從業務無侵入的 2PC 方案著手,在傳統 2PC的基礎上演進,并解決 2PC 方案面臨的問題,
Seata 把一個分布式事務理解成一個包含了若干分支事務的全域事務,全域事務的職責是協調其下管轄的分支事務達成一致,要么一起成功提交,要么一起失敗回滾,此外,通常分支事務本身就是一個關系資料庫的本地事務,下圖是全域事務與分支事務的關系圖:

與傳統 2PC 的模型類似,Seata 定義了 3 個組件來協議分布式事務的處理程序:

- Transaction Coordinator(TC):事務協調器,它是獨立的中間件,需要獨立部署運行,它維護全域事務的運行狀態,接收 TM 指令發起全域事務的提交與回滾,負責與 RM 通信協調各各分支事務的提交或回滾,
- Transaction Manager(TM): 事務管理器,TM 需要嵌入應用程式中作業,它負責開啟一個全域事務,并最終向 TC 發起全域提交或全域回滾的指令,
- Resource Manager(RM):控制分支事務,負責分支注冊、狀態匯報,并接收事務協調器 TC 的指令,驅動分支(本地)事務的提交和回滾,
舉例Seata的分布式事務程序:
簡單業務邏輯圖:

第一階段:

第二階段:提交,回滾
提交:
回滾:

Seata實作2PC與傳統2PC的差別
架構層次方面:傳統 2PC 方案的 RM 實際上是在資料庫層,RM 本質上就是資料庫自身,通過 XA 協議實作,而 Seata 的 RM 是以 jar 包的形式作為中間件層部署在應用程式這一側的,
兩階段提交方面:傳統 2PC無論第二階段的決議是 commit 還是 rollback ,事務性資源的鎖都要保持到 Phase2 完成才釋放,而 Seata 的做法是在 Phase1 就將本地事務提交,這樣就可以省去 Phase2 持鎖的時間,整體提高效率,
六,TCC模式
缺點:代碼侵入性較強,需要自己手寫處理代碼
優點:沒有鎖的概念,效率高一點,由于是自己手寫,定制化程度比較高,
Tcc 框架:
概念圖:

簡單業務圖:

七,Seata 服務端的自帶表說明
UNDO_LOG(回滾日志)表:記錄參與者的xid和相關的操作,用于對事務的回滾
表結構:
`CREATE TABLE `undo_log` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `branch_id` bigint(20) NOT NULL, `xid` varchar(100) NOT NULL, `context` varchar(128) NOT NULL, `rollback_info` longblob NOT NULL, `log_status` int(11) NOT NULL, `log_created` datetime NOT NULL, `log_modified` datetime NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
在 db 模式下,我們需要針對全域事務的會話資訊創建以下 3 張資料庫表,
全域事務表,對應的表為:global_table
分支事務表,對應的表為:branch_table
全域鎖表,對應的表為:lock_table
---資料庫server 要的表 -- -------------------------------- The script used when storeMode is 'db' -------------------------------- -- the table to store GlobalSession data CREATE TABLE IF NOT EXISTS ?"??global_table??"? ( `xid` VARCHAR(128) NOT NULL, `transaction_id` BIGINT, `status` TINYINT NOT NULL, `application_id` VARCHAR(32), `transaction_service_group` VARCHAR(32), `transaction_name` VARCHAR(128), `timeout` INT, `begin_time` BIGINT, `application_data` VARCHAR(2000), `gmt_create` DATETIME, `gmt_modified` DATETIME, PRIMARY KEY (`xid`), KEY `idx_status_gmt_modified` (`status` , `gmt_modified`), KEY `idx_transaction_id` (`transaction_id`) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4; -- the table to store BranchSession data CREATE TABLE IF NOT EXISTS `branch_table` ( `branch_id` BIGINT NOT NULL, `xid` VARCHAR(128) NOT NULL, `transaction_id` BIGINT, `resource_group_id` VARCHAR(32), `resource_id` VARCHAR(256), `branch_type` VARCHAR(8), `status` TINYINT, `client_id` VARCHAR(64), `application_data` VARCHAR(2000), `gmt_create` DATETIME(6), `gmt_modified` DATETIME(6), PRIMARY KEY (`branch_id`), KEY `idx_xid` (`xid`) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4; -- the table to store lock data CREATE TABLE IF NOT EXISTS `lock_table` ( `row_key` VARCHAR(128) NOT NULL, `xid` VARCHAR(128), `transaction_id` BIGINT, `branch_id` BIGINT NOT NULL, `resource_id` VARCHAR(256), `table_name` VARCHAR(32), `pk` VARCHAR(36), `status` TINYINT NOT NULL DEFAULT '0' COMMENT '0:locked ,1:rollbacking', `gmt_create` DATETIME, `gmt_modified` DATETIME, PRIMARY KEY (`row_key`), KEY `idx_status` (`status`), KEY `idx_branch_id` (`branch_id`) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4; CREATE TABLE IF NOT EXISTS `distributed_lock` ( `lock_key` CHAR(20) NOT NULL, `lock_value` VARCHAR(20) NOT NULL, `expire` BIGINT, primary key (`lock_key`) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;
注:看到這里了,感覺有用,就關注一下吧,或者贊助一下吧!你們的贊助就是我創作的最大動力,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/449043.html
標籤:Java
