MySQL XA 介紹

引言

在MySQL 5.7.7版本中,Oracle 官方將MySQL XA 一直存在的一個“bug” 進行了修復,使得MySQL XA 的實現符合了分布式事務的標準。那是否可以使用MySQL XA 讓MySQL 具有分布式擴展的能力呢?在回答這個問題前,我們先看下MySQL XA 涉及到的相關概念。

相關概念介紹

事務:由一個有限的數據庫操作序列構成,這些操作需要滿足四個特性,即原子性、一致性、隔離性、持久性,簡稱ACID。

分布式事務:根據 Open Group 關于分布式事務的處理規范,定義了三種組件,如下圖:

其中

AP

是指應用程序。

RM是資源管理器,事務的參與者,通常是數據庫,比如MySQL Server。一個分布式事務通常涉及多個資源管理器。

TM是事務管理器,創建分布式事務并協調分布式事務中的各個子事務的執行和狀態。子事務是指分布式事務中在RM上執行的具體操作。

兩階段提交 (Two-Phase Commit, 簡稱2PC) ,是為了使基于分布式系統架構下的所有節點在進行事務提交時保持一致性而設計的一種算法。分布式事務通常采用2PC,二階段提交的算法思路可以概括為: 參與者將操作成敗通知協調者,再由協調者根據所有參與者的反饋情報決定各參與者是否要提交操作還是中止操作,這里的參與者可以理解為RM,協調者可以理解為TM。如下圖所示:

在第一階段,TM會發送 Prepare 到所有參與分布式事務的RM詢問是否可以提交操作,參與分布式事務的所有RM接收到請求,實現自身事務提交前的準備工作并返回結果。在第二階段,根據RM返回的結果,如果涉及分布式事務的所有RM都返回可以提交,則TM給RM發送commit的命令,每個RM實現自己的提交,同時釋放鎖和資源,然后RM反饋提交成功,TM完成整個分布式事務;如果任何一個RM返回不能提交,則涉及分布式事務的所有RM都被告知需要回滾。MySQL XA 也是基于這個規范實現的,接下來我們介紹下MySQL XA。

MySQL XA 是什么?

MySQL XA 是基于Open Group 的<<Distributed Transaction Processing:The XA Specification>> 標準實現的,支持分布式事務,允許多個數據庫實例參與一個全局的事務。MySQl XA 從MySQL 5.0 開始引入,僅innodb存儲引擎支持MySQL XA事務。

MySQL XA 的命令集合如下:

XA START xid: 開啟一個事務,并將事務置于ACTIVE狀態,此后執行的SQL語句都將置于該是事務中。

XA END xid: 將事務置于IDLE狀態,表示事務內的SQL操作完成。

XA PREPARE xid: 實現事務提交的準備工作,事務狀態置于PREPARED狀態。事務如果無法完成提交前的準備操作,該語句會執行失敗。

XA COMMIT xid:? 事務最終提交,完成持久化。

XA ROLLBACK xid: 事務回滾終止。

XA RECOVER: 查看MySQL中存在的PREPARED狀態的xa事務。

下圖是XA事務狀態變遷圖:

從分布式事務的變遷中可以看出,有兩條路徑可以使事務達到提交狀態,有兩條路徑是回滾并結束事務。我們將這四條路徑進行橫向對比,看看每個階段是如何實現分布式事務的ACID特性的(相關分析是在RR隔離級別下進行的,暫不考慮RC隔離級別)。

如上圖可以看出,

1. 當xa start開啟事務后,DML也會在對應的RM上創建undo以及read view(該read view是instance級別的)。

2. 當xa prepare 時會將子事務置于PREPARED狀態,此時子事務已經完成事務提交前的所有準備工作(獲得鎖,并將PREPARED狀態記錄到共享表空間中,會將xa start到xa end之間操作記錄在binlog中)。

3. 當xa commit 時會在binlog中記錄xa commit xid, 并將innodb中PREPARED狀態轉化為COMMITED狀態。

4. 當xa commit one phase 時會同時進行prepare和commit 兩種操作,是在TM發現全局的分布式事務只涉及一個RM時進行的(因為不需要等待其他RM的反饋結果)。

5. 當xa rollback在xa prepare前時,因為沒有寫binlog和redo,只會釋放undo, read view以及lock。

6. 當xa rollback 在xa prepare之后時,除了需要釋放undo, read view以及lock,還需要binlog中記錄xa rollback xid(使得從庫不會提交該事務)以及innodb中將PREPARED狀態轉化為ROLLBACK狀態。

MySQL XA 的例子

上面介紹了MySQL XA 的原理,我們現在舉幾個簡單的例子。

例子1,兩階段XA事務提交:

mysql> xa start 'mysql57xa';

Query OK, 0 rows affected (0.00 sec)

mysql> insert into t1(id) values(1);

Query OK, 1 row affected (0.00 sec)

mysql> xa end 'mysql57xa';

Query OK, 0 rows affected (0.00 sec)

mysql> xa prepare 'mysql57xa';

Query OK, 0 rows affected (0.00 sec)

mysql> xa recover\G

formatID: 1

gtrid_length: 7

bqual_length: 0

data: mysql57

1 row in set (0.00 sec)

mysql> xa commit 'mysql57xa';

Query OK, 0 rows affected (0.00 sec)

對應的Binlog 中的記錄如下:

例子2,xa commit one phase ,不需要等待其他RM反饋prepare的結果。

mysql> xa start 'mysql57xa';

Query OK, 0 rows affected (0.00 sec)

mysql> insert into t1(id) values(2);

Query OK, 1 row affected (0.00 sec)

mysql> xa end 'mysql57xa';

Query OK, 0 rows affected (0.00 sec)

mysql> xa commit 'mysql57xa' one phase;

Query OK, 0 rows affected (0.00 sec)

對應的Binlog 中的記錄如下:

例子3,在xa prepare后,執行xa rollback 回滾事務。

mysql> xa start 'mysql57xa';

Query OK, 0 rows affected (0.00 sec)

mysql> insert into t1(id) values(3);

Query OK, 1 row affected (0.00 sec)

mysql> xa end 'mysql57xa';

Query OK, 0 rows affected (0.00 sec)

mysql> xa prepare 'mysql57xa';

Query OK, 0 rows affected (0.00 sec)

mysql> xa rollback 'mysql57xa';

Query OK, 0 rows affected (0.00 sec)

對應的Binlog 中的記錄如下:

MySQL XA 的限制

在MySQL 5.7.7 之前,MySQL一直存在一個"bug"。在事務達到PREPARED狀態后,客戶端斷開與MySQL的連接,MySQL 會自動回滾該事務,這個行為不符合分布式事務的規范,MySQL將PREPARED的事務丟失了。之所以MySQL這么實現是因為MySQL 5.7.7 之前PREPARED的事務并不會記錄到binlog中。客戶端退出后會丟失該信息,如果允許再提交,那么binlog缺少事務信息,會造成主從不一致。

在MySQL 5.7.7 之后,MySQL 新增了一個XA_prepare_log_event的事件,會把xa start到xa prepare中間的操作記錄到Binlog中。Slave讀取Relay log 進行回放,當SQL Thread讀取到PREPARED的事務后,在讀取xa commit或者xa rollback前,會進行一個類似客戶端斷開的操作,繼續讀取后續的事務信息,不會阻塞SQL Thread的執行。從以上的結果看,Oracle在MySQL 5.7.7 上確實完美的解決了MySQL XA一直存在的一個"bug"。

MySQL XA 的實踐

本人曾在某公司的分布式數據庫項目組中實踐過基于MySQL XA的分布式事務。MySQL XA 要滿足線上高并發的訪問要求,在使用時還需要解決兩個問題:分布式死鎖問題和分布式讀一致性問題。分布式死鎖問題是指MySQL Server 是可以檢測和解決單個MySQL實例中的死鎖問題,但涉及到跨越多個MySQL 實例的分布式事務時候,需要程序層面實現死鎖的檢測和解決。分布式讀一致性問題是指MySQL的read view 也是實例級別的,對于全局分布式事務來說無法實現讀一致,只能通過select ... lock in share mode在讀請求上加鎖的串行化隔離級別來實現,這必然會帶來并發性能的下降。這就需要在程序層面構建全局的read view來實現全局的MVCC 。當然這兩個問題,當時團隊的大牛們都已經解決了,我也很有幸參與其中。

本人水平有限,描述有誤或是不準確的地方,請大家多多指教。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 什么是數據庫? 數據庫是存儲數據的集合的單獨的應用程序。每個數據庫具有一個或多個不同的API,用于創建,訪問,管理...
    chen_000閱讀 4,053評論 0 19
  • 線上一個5.7從庫復制中斷: 查詢具體報錯: 第一感覺很奇怪,為什么會rollback失敗呢?于是根據gtid去對...
    小盧二閱讀 4,836評論 1 1
  • Mysql 有4種類型的日志:Error Log、Genaral Query Log、 Binary Log 和 ...
    人在碼途閱讀 16,440評論 2 11
  • 最近在一群辣眼睛玄幻劇當道的晚暑期檔中,我突然發現了一股清流——簡單不做作的《小別離》。 第一次聽朋友說在看《小別...
    念念云耳閱讀 14,636評論 10 40
  • 今天是正月十五,元宵節,本來對這個節日沒有什么感覺,可就在剛才,看見家庭群里公公發的在外面散步的視頻,忽然有些傷感...
    我的春夏秋冬閱讀 109評論 0 1