MySQL XA 介紹

引言

在MySQL 5.7.7版本中,Oracle 官方將MySQL XA 一直存在的一個“bug” 進(jìn)行了修復(fù),使得MySQL XA 的實(shí)現(xiàn)符合了分布式事務(wù)的標(biāo)準(zhǔn)。那是否可以使用MySQL XA 讓MySQL 具有分布式擴(kuò)展的能力呢?在回答這個問題前,我們先看下MySQL XA 涉及到的相關(guān)概念。

相關(guān)概念介紹

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

分布式事務(wù):根據(jù) Open Group 關(guān)于分布式事務(wù)的處理規(guī)范,定義了三種組件,如下圖:

其中

AP

是指應(yīng)用程序。

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

TM是事務(wù)管理器,創(chuàng)建分布式事務(wù)并協(xié)調(diào)分布式事務(wù)中的各個子事務(wù)的執(zhí)行和狀態(tài)。子事務(wù)是指分布式事務(wù)中在RM上執(zhí)行的具體操作。

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

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

MySQL XA 是什么?

MySQL XA 是基于Open Group 的<<Distributed Transaction Processing:The XA Specification>> 標(biāo)準(zhǔn)實(shí)現(xiàn)的,支持分布式事務(wù),允許多個數(shù)據(jù)庫實(shí)例參與一個全局的事務(wù)。MySQl XA 從MySQL 5.0 開始引入,僅innodb存儲引擎支持MySQL XA事務(wù)。

MySQL XA 的命令集合如下:

XA START xid: 開啟一個事務(wù),并將事務(wù)置于ACTIVE狀態(tài),此后執(zhí)行的SQL語句都將置于該是事務(wù)中。

XA END xid: 將事務(wù)置于IDLE狀態(tài),表示事務(wù)內(nèi)的SQL操作完成。

XA PREPARE xid: 實(shí)現(xiàn)事務(wù)提交的準(zhǔn)備工作,事務(wù)狀態(tài)置于PREPARED狀態(tài)。事務(wù)如果無法完成提交前的準(zhǔn)備操作,該語句會執(zhí)行失敗。

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

XA ROLLBACK xid: 事務(wù)回滾終止。

XA RECOVER: 查看MySQL中存在的PREPARED狀態(tài)的xa事務(wù)。

下圖是XA事務(wù)狀態(tài)變遷圖:

從分布式事務(wù)的變遷中可以看出,有兩條路徑可以使事務(wù)達(dá)到提交狀態(tài),有兩條路徑是回滾并結(jié)束事務(wù)。我們將這四條路徑進(jìn)行橫向?qū)Ρ?,看看每個階段是如何實(shí)現(xiàn)分布式事務(wù)的ACID特性的(相關(guān)分析是在RR隔離級別下進(jìn)行的,暫不考慮RC隔離級別)。

如上圖可以看出,

1. 當(dāng)xa start開啟事務(wù)后,DML也會在對應(yīng)的RM上創(chuàng)建undo以及read view(該read view是instance級別的)。

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

3. 當(dāng)xa commit 時會在binlog中記錄xa commit xid, 并將innodb中PREPARED狀態(tài)轉(zhuǎn)化為COMMITED狀態(tài)。

4. 當(dāng)xa commit one phase 時會同時進(jìn)行prepare和commit 兩種操作,是在TM發(fā)現(xiàn)全局的分布式事務(wù)只涉及一個RM時進(jìn)行的(因?yàn)椴恍枰却渌鸕M的反饋結(jié)果)。

5. 當(dāng)xa rollback在xa prepare前時,因?yàn)闆]有寫binlog和redo,只會釋放undo, read view以及l(fā)ock。

6. 當(dāng)xa rollback 在xa prepare之后時,除了需要釋放undo, read view以及l(fā)ock,還需要binlog中記錄xa rollback xid(使得從庫不會提交該事務(wù))以及innodb中將PREPARED狀態(tài)轉(zhuǎn)化為ROLLBACK狀態(tài)。

MySQL XA 的例子

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

例子1,兩階段XA事務(wù)提交:

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)

對應(yīng)的Binlog 中的記錄如下:

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

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)

對應(yīng)的Binlog 中的記錄如下:

例子3,在xa prepare后,執(zhí)行xa rollback 回滾事務(wù)。

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)

對應(yīng)的Binlog 中的記錄如下:

MySQL XA 的限制

在MySQL 5.7.7 之前,MySQL一直存在一個"bug"。在事務(wù)達(dá)到PREPARED狀態(tài)后,客戶端斷開與MySQL的連接,MySQL 會自動回滾該事務(wù),這個行為不符合分布式事務(wù)的規(guī)范,MySQL將PREPARED的事務(wù)丟失了。之所以MySQL這么實(shí)現(xiàn)是因?yàn)镸ySQL 5.7.7 之前PREPARED的事務(wù)并不會記錄到binlog中??蛻舳送顺龊髸G失該信息,如果允許再提交,那么binlog缺少事務(wù)信息,會造成主從不一致。

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

MySQL XA 的實(shí)踐

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

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

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

友情鏈接更多精彩內(nèi)容