3. 分散式事務解決方案之2PC(兩階段提交) 針對不同的分散式場景業界常見的解決方案有2PC、TCC、可靠消息最終一致性、最大努力通知這幾種。 3.1. 什麼是2PC 2PC即兩階段提交協議,是將整個事務流程分為兩個階段,準備階段(Prepare phase)、提交階段(commit phase) ...
3. 分散式事務解決方案之2PC(兩階段提交)
針對不同的分散式場景業界常見的解決方案有2PC、TCC、可靠消息最終一致性、最大努力通知這幾種。
3.1. 什麼是2PC
2PC即兩階段提交協議,是將整個事務流程分為兩個階段,準備階段(Prepare phase)、提交階段(commit phase),2是指兩階段,P是指準備階段,C是提交階段。
舉例 :張三和李四好久不見,老友約起聚餐,飯店老闆要求先買單,才能出票。這時張三和李四分別抱怨近況不如意,囊腫羞澀,都不願意請客,這時只能AA。只有張三和李四都付款,老闆才能出票安排就餐。但由於張三和李四都是鐵公雞,形成兩尷尬的一幕 :
準備階段 :老闆要求張三付款,張三付款。老闆要求李四付款,李四付款。
提交階段 :老闆出票,兩人拿票紛紛落座就餐。
例子中形成兩一個事務,若張三或李四其中一個拒絕付款,或錢不夠,店老闆都不會給出票,並且會把已收款退回。
整個事務過程由事務管理器和參與者組成,店老闆就是事務管理器,張三、李四就是事務參與者,事務管理器負責決策整個分散式事務的提交和回滾,事務參與者負責自己本地事務的提交和回滾。
在電腦中部分關係資料庫如Oracle、MySQL支持兩階段提交協議,如下圖 :
1. 準備階段(Prepare phase):事務管理器給每個參與者發送Prepare消息,每個資料庫參與者在本地執行事務,並寫本地的Undo/Redo日誌,此時事務沒有提交。
(Undo日誌是記錄修改前的數據,用於資料庫回滾,Redo日誌是記錄修改後的數據,用於提交事務後寫入數據文件)
2. 提交階段(commit phase):如果事務管理器收到兩參與者的執行失敗或者超時消息時,直接給每個參與者發送回滾(Rollback)消息;否則,發送提交(Commit)消息;參與者根據事務管理器的指令執行提交或者回滾操作,並釋放事務處理過程中使用的鎖資源。註意 :必須在最後階段釋放鎖資源。
下圖展示兩2PC的兩個階段,分成功和失敗兩個情況說明 :
成功情況 :
失敗情況 :
3.2. 解決方案
3.2.1 XA方案
2PC的傳統方案是在資料庫層面實現的,如Oracle、MySQL都支持2PC協議,為了統一標準減少行業內不必要的對接成本,需要制定標準化的處理模型及介面標準,國際開放標準組織Open Group定義分散式事務處理模型DTP(Distributed Transaction Processing Reference Model)。
為了讓大家更明確XA方案的內容,下麵新用戶註冊送積分為例來說明 :
執行流程如下 :
1、應用程式(AP)持有用戶庫和積分庫兩個數據源。
2、應用程式(AP)通過TM通知用戶庫RM新增用戶,同時通知積分庫RM為該用戶新增積分,RM此時並未提交事務,此時用戶和積分資源鎖定。
3、TM收到執行回覆,只要有一方失敗則分別向其他RM發起回滾事務,回滾完畢,資源鎖釋放。
4、TM收到執行回覆,全部成功,此時向所有RM發起提交事務,提交完畢,資源鎖釋放。
DTP模型定義如下角色 :
- AP(Application Program) : 既應用程式,可以理解為使用DTP分散式事務的程式。
- RM(Resource Manager) : 即資源管理器,可以理解為事務的參與者,一般情況下是指一個資料庫實例,通過資源管理器對該資料庫進行控制,資源管理器控制著分支事務。
- TM(Transaction Manager) : 事務管理器,負責協調和管理事務,事務管理器控制著全局事務,管理事務生命周期,並協調各個RM。全局事務是指分散式事務處理環境中,需要操作多個資料庫共同完成一個工作,這個工作即是一個全局事務。
- DTP模型定義TM和RM之間通訊的介面規範叫XA,簡單理解為資料庫提供的2PC介面協議,基於資料庫的XA協議來實現2PC又稱為XA方案。
- 以上三個角色之間的交互方式如下 :
1)TM向AP提供應用程式編程介面,AP通過TM提交及回滾事務。
2)TM交易中間件通過XA介面來通知RM資料庫事務的開始、結束以及提交、回滾等。
總結 :
整個2PC的事務流程涉及到三個角色AP、RM、TM。AP指的是使用2PC分散式事務的應用程式;RM指的是資源管理器,它控制著分支事務;TM指的是事務管理器,它控制著整個全局事務。
1)在準備階段RM執行實際的業務操作,但不提交事務,資源鎖定;
2)在提交階段TM會接收RM在準備階段的執行回覆,只要有任一個RM執行失敗,TM會通知所有RM執行回滾操作,否則,TM將會通知所有RM提交該事務。提交階段結束資源鎖釋放。
XA方案的問題 :
1、需要本地資料庫支持XA協議。
2、資源鎖需要等到兩個階段結束才釋放,性能較差。
3.2.2 Seata方案
Seata是阿裡中間件團隊發起的開源項目Fescar,後更名Seata,它是一個是開源的分散式事務框架。傳統2PC的問題在Seata中得到瞭解決,它通過對本地關係資料庫的分支事務的協調來驅動完成全局事務,是工作在應用層的中間件。主要優點是性能較好,且不長時間占用連接資源,它以高效並且對業務0入侵的方式解決微服務場景下麵臨的分散式事務問題,它目前提供AT模式(即2PC)及TCC模式的分散式事務解決方案。
Seata的設計思想如下 :
Seata的設計目標其一是對業務無入侵,因此從業務無入侵的2PC方案著手,在傳統2PC的基礎上演進,並解決2PC方案面臨的問題。
Seata把一個分散式事務理解成一個包含來若幹分支事務的全局事務。全局事務的職責是協調其下管轄的分支事務達成一致,要麼一起成功提交,要麼一起失敗回滾。此外,通常分支事務本身就是一個關係資料庫的本地事務,下圖是全局事務與分支事務的關係圖 :
與傳統2PC的模型類似,Seata定義了三個組件來協議分散式事務的處理過程 :
- Transaction Coordinator(TC):事務協調器,它是獨立的中間件,需要獨立部署運行,它維護全局事務的運行狀態,接收TM指令發起全局事務的提交與回滾,負責與RM通信協調各個分支事務的提交或回滾。
- Transaction Manager(TM):事務管理器,TM需要嵌入應用程式中工作,它負責開啟一個全局事務,並最終向TC發起全局提交或全局回滾的指令。
- Resource Manager(RM):控制分支事務,負責分支註冊、狀態彙報,並接收事務協調器TC的指令,驅動分支(本地)事務的提交和回滾。
還拿新用戶註冊送積分舉例Seata的分散式事務過程 :
具體的執行流程如下 :
- 用戶服務的TM向TC申請開啟一個全局事務,全局事務創建成功並生成一個全局唯一的XID。
- 用戶服務的RM向TC註冊分支事務,該分支事務在用戶服務執行新增用戶邏輯,並將其納入XID對應全局事務的管轄。
- 用戶服務執行分支事務,向用戶表插入一條記錄。
- 邏輯執行到遠程調用積分服務時(XID在微服務調用鏈路的上下文中傳播)。積分服務的RM向TC註冊分支事務,該分支事務執行增加積分的邏輯,並將其納入XID對應全局事務的管轄。
- 積分服務執行分支事務,向積分記錄表插入一條記錄,執行完畢後,返回用戶服務。
- 用戶服務分支事務執行完畢。
- TM向TC發起針對XID的全局提交或回滾決議。
- TC調度XID下管轄的全部分支事務完成提交或回滾請求。
Seata實現2PC與傳統2PC的差別 :
架構層次方面,傳統2PC方案的RM實際上是在資料庫層,RM本質上就是資料庫自身,通過XA協議實現,而Seata的RM是以jar包的形式作為中間件層部署在應用程式的這一側的。
兩階段提交方面,傳統2PC無論第二階段的決議是commit還是rollbcak,事務性資源的鎖都要保持到Phase2完成才釋放。而Seata的做法是在Phase1就將本地事務提交,這樣就可以省去Phase2持鎖的時間,整體提高效率。
3.3. Seata實現2PC事務
3.3.1. 業務說明
本實例通過Seata中間件實現分散式事務,模擬兩個賬戶的轉賬交易過程。兩個賬戶在兩個不同的銀行(張三在bank1、李四在bank2),bank1和bank2是兩個微服務。交易過程中,張三給李四轉賬制定金額。
上述交易步驟,要麼一起成功,要麼一起失敗,必須是一個整體性的事務。
3.3.2.程式組成部分
本實常式序組成 部分如下 :
資料庫 :MySQL-5.7.25
包括bank1和bank2兩個資料庫。
JDK:1.8
微服務框架 :spring-boot-2.1.3、spring-cloud-Greenwich.RELEASE
seata客戶端(RM、TM):spring-cloud-alibaba-seata-2.1.0RELEASE
seata服務端(TC):seata-server-0.7.1
微服務及資料庫的關係 :
dtx/dtx-seata-demo/seata-demo-bank1 銀行1,操作張三賬戶,鏈接資料庫bank1
dtx/dtx-seata-demo/seata-demo-bank2 銀行2,操作李四賬戶,鏈接資料庫bank2
服務註冊中興 :dtx/discover-server
本實常式序技術架構如下 :
交互流程如下 :
1、請求bank1進行轉賬,傳入轉賬金額。
2、bank1減少轉賬金額,調用bank2,傳入轉賬金額。
3.3.3.創建資料庫
bank1庫,包含張三賬戶
CREATE DATABASE /*!32312 IF NOT EXISTS*/`bank1` /*!40100 DEFAULT CHARACTER SET utf8 */;
USE `bank1`;
/*Table structure for table `account_info` */
DROP TABLE IF EXISTS `account_info`;
CREATE TABLE `account_info` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`account_name` varchar(100) COLLATE utf8_bin DEFAULT NULL COMMENT '戶主姓名',
`account_no` varchar(100) COLLATE utf8_bin DEFAULT NULL COMMENT '銀行卡號',
`account_password` varchar(100) COLLATE utf8_bin DEFAULT NULL COMMENT '帳戶密碼',
`account_balance` double DEFAULT NULL COMMENT '帳戶餘額',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_bin ROW_FORMAT=DYNAMIC;
/*Data for the table `account_info` */
insert into `account_info`(`id`,`account_name`,`account_no`,`account_password`,`account_balance`) values (2,'張三','1',NULL,1000);
/*Table structure for table `de_duplication` */
DROP TABLE IF