MySQL分布式事務(wù)支持

MySQL分布式事務(wù)介紹

InnoDB存儲(chǔ)引擎提供了對(duì)XA事務(wù)的支持在岂,并通過XA事務(wù)來支持分布式事務(wù)的實(shí)現(xiàn)豫缨。分布式事務(wù)指的是允許多個(gè)獨(dú)立的事務(wù)資源參與到一個(gè)全局的事務(wù)中缩举。事務(wù)資源通常是關(guān)系型數(shù)據(jù)庫系統(tǒng)洁奈,但也可以是其他類型的資源串慰。全局事務(wù)要求在其中的所有參與的事務(wù)要么都提交井濒,要么都回滾灶似,這對(duì)于事務(wù)原有的ACID要求又有了提高。另外瑞你,在使用分布式事務(wù)時(shí)酪惭,InnoDB存儲(chǔ)引擎的事務(wù)隔離級(jí)別必須設(shè)置為SERIALIZABLE。

XA事務(wù)語允許不同數(shù)據(jù)庫之間的分布式事務(wù)者甲,如一臺(tái)服務(wù)器是MySQL數(shù)據(jù)庫的春感,另一臺(tái)是Oracle數(shù)據(jù)庫的,又可能還有一臺(tái)服務(wù)器是SQL Server數(shù)據(jù)庫的虏缸,只要參與在全局事務(wù)中的每個(gè)節(jié)點(diǎn)都支持XA事務(wù)鲫懒。分布式事務(wù)可能在分布式架構(gòu)或銀行系統(tǒng)的轉(zhuǎn)賬中比較常見,入用戶David需要從上海轉(zhuǎn)10000元到北京的用戶Mariah的銀行卡中刽辙。

# Bank@Shanghai

update account set money = money - 10000 where user='David';

# Bank@Beijing

update account set money = money + 10000 where user='Mariah';

在這種情況下窥岩,一定需要使用分布式事務(wù)來保證數(shù)據(jù)的安全。如果發(fā)生的操作不能全部提交或回滾宰缤,那么任何一個(gè)節(jié)點(diǎn)出現(xiàn)問題都會(huì)導(dǎo)致嚴(yán)重的結(jié)果颂翼。要么是David的賬號(hào)被扣了款晃洒,但Mariah沒收到,又或者是David的賬戶沒有扣款朦乏,Mariah卻收到錢了球及。

XA事務(wù)由一個(gè)或多個(gè)資源管理器(resource managers)、一個(gè)事務(wù)管理器(transaction manager)以及一個(gè)應(yīng)用程序(application program)組成呻疹。

資源管理器:提供訪問事務(wù)資源的方法吃引,通常一個(gè)數(shù)據(jù)庫就是一個(gè)資源管理器。

事務(wù)管理器:協(xié)調(diào)參與全局事務(wù)中的各個(gè)事務(wù)刽锤,需要和參與全局事務(wù)的所有資源管理器進(jìn)行通信镊尺。

應(yīng)用程序:定義事務(wù)的邊界,指定全局事務(wù)中的操作并思。

在MySQL數(shù)據(jù)庫的分布式事務(wù)中鹅心,資源管理器就是MySQL數(shù)據(jù)庫,事務(wù)管理器為連接MySQL服務(wù)器的客戶端纺荧。下圖顯示了一個(gè)分布式事務(wù)的模型。

分布式事務(wù)通常采用2PC協(xié)議颅筋,全稱Two Phase Commitment Protocol宙暇。該協(xié)議主要為了解決在分布式數(shù)據(jù)庫場景下,所有節(jié)點(diǎn)間數(shù)據(jù)一致性的問題议泵。在分布式事務(wù)環(huán)境下占贫,事務(wù)的提交會(huì)變得相對(duì)比較復(fù)雜,因?yàn)槎鄠€(gè)節(jié)點(diǎn)的存在先口,可能存在部分節(jié)點(diǎn)提交失敗的情況型奥,即事務(wù)的ACID特性需要在各個(gè)數(shù)據(jù)庫實(shí)例中保證〉锞總而言之厢汹,在分布式提交時(shí),只要發(fā)生一個(gè)節(jié)點(diǎn)提交失敗谐宙,則所有的節(jié)點(diǎn)都不能提交烫葬,只有當(dāng)所有節(jié)點(diǎn)都能提交時(shí),整個(gè)分布式事務(wù)才允許被提交凡蜻。

分布式事務(wù)通過2PC協(xié)議將提交分成兩個(gè)階段搭综,在第一階段,所有參與全局事務(wù)的節(jié)點(diǎn)都開始準(zhǔn)備(PREPARE)划栓,告訴事務(wù)管理器它們準(zhǔn)備好提交了兑巾。在第二階段,事務(wù)管理器告訴資源管理器執(zhí)行ROLLBACK還是COMMIT忠荞。如果任何一個(gè)節(jié)點(diǎn)顯示不能提交蒋歌,則所有的節(jié)點(diǎn)都被告知需要回滾帅掘。可見與本地事務(wù)不同的是奋姿,分布式事務(wù)需要多一次的PREPARE操作锄开,待收到所有節(jié)點(diǎn)的同意信息后,再進(jìn)行COMMIT或是ROLLBACK操作称诗。

MySQL分布式事務(wù)操作

1. MySQL XA事務(wù)的語法

主要有:

# 在mysql實(shí)例中開啟一個(gè)XA事務(wù),指定一個(gè)全局唯一標(biāo)識(shí)萍悴;

mysql> XA START 'any_unique_id';

# XA事務(wù)的操作結(jié)束;

mysql> XA END 'any_unique_id?';

# 告知mysql準(zhǔn)備提交這個(gè)xa事務(wù)寓免;

mysql> XA PREPARE 'any_unique_id';

# 告知mysql提交這個(gè)xa事務(wù)癣诱;

mysql> XA COMMIT 'any_unique_id';

# 告知mysql回滾這個(gè)xa事務(wù);

mysql> XA ROLLBACK 'any_unique_id';

# 查看本機(jī)mysql目前有哪些xa事務(wù)處于prepare狀態(tài)袜香;

mysql> XA RECOVER;

2. XA事務(wù)恢復(fù)

如果執(zhí)行分布式事務(wù)的mysql crash了撕予,MySQL按照如下邏輯進(jìn)行恢復(fù):

a. 如果這個(gè)xa事務(wù)commit了,那么什么也不用做蜈首。

b. 如果這個(gè)xa事務(wù)還沒有prepare实抡,那么直接回滾它。

c. 如果這個(gè)xa事務(wù)prepare了欢策,還沒commit吆寨, 那么把它恢復(fù)到prepare的狀態(tài),由用戶去決定commit或rollback踩寇。

當(dāng)mysql crash后重新啟動(dòng)之后啄清,執(zhí)行“XA RECOVER;”查看當(dāng)前處于prepare狀態(tài)的xa事務(wù)俺孙,然后commit或rollback它們辣卒。

MySQL分布式事務(wù)限制

a. XA事務(wù)和本地事務(wù)以及鎖表操作是互斥的

開啟了xa事務(wù)就無法使用本地事務(wù)和鎖表操作

mysql> xa start 't1xa';

Query OK, 0 rows affected (0.04 sec)

mysql> begin;

ERROR 1399 (XAE07): XAER_RMFAIL: The command cannot be executed when global transaction is in theACTIVE state

mysql> lock table t read;

ERROR 1399 (XAE07): XAER_RMFAIL: The command cannot be executed when global transaction is in theACTIVE state

開啟了本地事務(wù)就無法使用xa事務(wù)

mysql> begin;

Query OK, 0 rows affected (0.00 sec)

mysql> xa start 'rrrr';

ERROR 1400 (XAE09): XAER_OUTSIDE: Some work is done outside global transaction

b. xa start之后必須xa end,否則不能執(zhí)行xa commit和xa rollback

所以如果在執(zhí)行xa事務(wù)過程中有語句出錯(cuò)了睛榄,你也需要先xa end一下荣茫,然后才能xa rollback。

mysql> xa start 'tt';

Query OK, 0 rows affected (0.00 sec)

mysql> xa rollback 'tt';

ERROR 1399 (XAE07): XAER_RMFAIL: The command cannot be executed when global transaction is in the??ACTIVE state

mysql> xa end 'tt';

Query OK, 0 rows affected (0.00 sec)

mysql> xa rollback 'tt';

Query OK, 0 rows affected (0.00 sec)

MySQL 5.7對(duì)分布式事務(wù)的支持

一直以來场靴,MySQL數(shù)據(jù)庫是支持分布式事務(wù)的计露,但是只能說是有限的支持,具體表現(xiàn)在:

已經(jīng)prepare的事務(wù)憎乙,在客戶端退出或者服務(wù)宕機(jī)的時(shí)候票罐,2PC的事務(wù)會(huì)被回滾。

在服務(wù)器故障重啟提交后泞边,相應(yīng)的Binlog被丟失该押。

上述問題存在于MySQL數(shù)據(jù)庫長達(dá)數(shù)十年的時(shí)間,直到MySQL-5.7.7版本阵谚,官方才修復(fù)了該問題蚕礼。下面將會(huì)詳細(xì)介紹下該問題的具體表現(xiàn)和官方修復(fù)方法烟具,這里分別采用官方MySQL-5.6.27版本(未修復(fù))和MySQL-5.7.9版本(已修復(fù))進(jìn)行驗(yàn)證。

先來看下存在的問題奠蹬,我們先創(chuàng)建一個(gè)表如下:

CREATE TABLE t(

id INT AUTO_INCREMENT PRIMARY KEY,

a INT

)ENGINE=InnoDB;

對(duì)于上述表朝聋,通過如下操作進(jìn)行數(shù)據(jù)插入:

mysql> XA START 'mysql56';

mysql> INSERT INTO t VALUES(1,1);

mysql> XA END 'mysql56';

mysql> XA PREPARE 'mysql56';

通過上面的操作,用戶創(chuàng)建了一個(gè)分布式事務(wù)囤躁,并且prepare沒有返回錯(cuò)誤冀痕,說明該分布式事務(wù)可以被提交。通過命令XA?RECOVER查看顯示如下結(jié)果:

mysql> XA RECOVER;

+----------+--------------+--------------+---------+

| formatID | gtrid_length | bqual_length | data? |

+----------+--------------+--------------+---------+

| 1? ? | 7? ? ? | 0? ? ? | mysql56 |

+----------+--------------+--------------+---------+

若這時(shí)候用戶退出客戶端后重連狸演,通過命令xa recover會(huì)發(fā)現(xiàn)剛才創(chuàng)建的2PC事務(wù)不見了言蛇。即prepare成功的事務(wù)丟失了,不符合2PC協(xié)議規(guī)范O唷@吧小!

產(chǎn)生上述問題的主要原因在于:MySQL 5.6版本在客戶端退出的時(shí)候满哪,自動(dòng)把已經(jīng)prepare的事務(wù)回滾了婿斥,那么MySQL為什么要這樣做?這主要取決于MySQL的內(nèi)部實(shí)現(xiàn)哨鸭,MySQL 5.7以前的版本受扳,對(duì)于prepare的事務(wù),MySQL是不會(huì)記錄binlog的(官方說是減少fsync兔跌,起到了優(yōu)化的作用)。只有當(dāng)分布式事務(wù)提交的時(shí)候才會(huì)把前面的操作寫入binlog信息峡蟋,所以對(duì)于binlog來說坟桅,分布式事務(wù)與普通的事務(wù)沒有區(qū)別,而prepare以前的操作信息都保存在連接的IO_CACHE中蕊蝗,如果這個(gè)時(shí)候客戶端退出了仅乓,以前的binlog信息都會(huì)被丟失,再次重連后允許提交的話蓬戚,會(huì)造成Binlog丟失夸楣,從而造成主從數(shù)據(jù)的不一致,所以官方在客戶端退出的時(shí)候直接把已經(jīng)prepare的事務(wù)都回滾了子漩!

官方的做法豫喧,貌似干得很漂亮,犧牲了一點(diǎn)標(biāo)準(zhǔn)化的東西幢泼,至少保證了主從數(shù)據(jù)的一致性紧显。但其實(shí)不然,若用戶已經(jīng)prepare后在客戶端退出之前缕棵,MySQL發(fā)生了宕機(jī)孵班,這個(gè)時(shí)候又會(huì)怎樣涉兽?

MySQL在某個(gè)分布式事務(wù)prepare成功后宕機(jī),宕機(jī)前操作該事務(wù)的連接并沒有斷開篙程,這個(gè)時(shí)候已經(jīng)prepare的事務(wù)并不會(huì)被回滾枷畏,所以在MySQL重新啟動(dòng)后,引擎層通過recover機(jī)制能恢復(fù)該事務(wù)虱饿。當(dāng)然該事務(wù)的Binlog已經(jīng)在宕機(jī)過程中被丟失拥诡,這個(gè)時(shí)候,如果去提交郭厌,則會(huì)造成主從數(shù)據(jù)的不一致袋倔,即提交沒有記錄Binlog,從上丟失該條數(shù)據(jù)折柠。所以對(duì)于這種情況宾娜,官方一般建議直接回滾已經(jīng)prepare的事務(wù)。

以上是MySQL 5.7以前版本MySQL在分布式事務(wù)上的各種問題扇售,那么MySQL 5.7版本官方做了哪些改進(jìn)前塔?這個(gè)可以從官方的WL#6860描述上得到一些信息,我們還是本著沒有實(shí)踐就沒有發(fā)言權(quán)的態(tài)度承冰,從具體的操作上來分析下MySQL 5.7的改進(jìn)方法华弓。還是以上面同樣的表結(jié)構(gòu)進(jìn)行同樣的操作如下:

mysql> XA START 'mysql57';

mysql> INSERT INTO t VALUES(1,1);

mysql> XA END 'mysql57';

mysql> XA PREPARE 'mysql57'

這個(gè)時(shí)候,我們通過mysqlbinlog來查看下Master上的Binlog困乒,結(jié)果如下:

同時(shí)也對(duì)比下Slave上的Relay log寂屏,如下:

通過上面的操作,明顯發(fā)現(xiàn)在prepare以后娜搂,從XA START到XA PREPARE之間的操作都被記錄到了Master的Binlog中迁霎,然后通過復(fù)制關(guān)系傳到了Slave上。也就是說MySQL 5.7開始百宇,MySQL對(duì)于分布式事務(wù)考廉,在prepare的時(shí)候就完成了寫B(tài)inlog的操作,通過新增一種叫XA_prepare_log_event的event類型來實(shí)現(xiàn)携御,這是與以前版本的主要區(qū)別(以前版本prepare時(shí)不寫B(tài)inlog)昌粤。

當(dāng)然僅靠這一點(diǎn)是不夠的,因?yàn)槲覀冎繱lave通過SQL thread來回放Relay log信息啄刹,由于prepare的事務(wù)能阻塞整個(gè)session涮坐,而回放的SQL thread只有一個(gè)(不考慮并行回放),那么SQL thread會(huì)不會(huì)因?yàn)楸环植际绞聞?wù)的prepare階段所阻塞誓军,從而造成整個(gè)SQL thread回放出現(xiàn)問題膊升?這也正是官方要解決的第二個(gè)問題:怎么樣能使SQL thread在回放到分布式事務(wù)的prepare階段時(shí),不阻塞后面event的回放谭企?其實(shí)這個(gè)實(shí)現(xiàn)也很簡單(在xa.cc::applier_reset_xa_trans)廓译,只要在SQL thread回放到prepare的時(shí)候评肆,進(jìn)行類似于客戶端斷開連接的處理即可(把相關(guān)cache與SQL thread的連接句柄脫離)。最后在Slave服務(wù)器上非区,用戶通過命令XA RECOVER可以查到如下信息:

mysql> XA RECOVER;

+----------+--------------+--------------+---------+

| formatID | gtrid_length | bqual_length | data? |

+----------+--------------+--------------+---------+

| 1? ? | 7? ? ? | 0? ? ? | mysql57 |

+----------+--------------+--------------+---------+

至于上面的事務(wù)什么時(shí)候提交瓜挽,一般等到Master上進(jìn)行XA COMMIT ?‘mysql57’后,slave上也同時(shí)會(huì)被提交征绸。

總結(jié)

綜上所述久橙,MySQL 5.7對(duì)于分布式事務(wù)的支持變得完美了,一個(gè)長達(dá)數(shù)十年的bug又被修復(fù)了管怠,因而又多了一個(gè)升級(jí)到MySQL 5.7版本的理由淆衷。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市渤弛,隨后出現(xiàn)的幾起案子祝拯,更是在濱河造成了極大的恐慌,老刑警劉巖她肯,帶你破解...
    沈念sama閱讀 212,454評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件佳头,死亡現(xiàn)場離奇詭異,居然都是意外死亡晴氨,警方通過查閱死者的電腦和手機(jī)康嘉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來籽前,“玉大人亭珍,你說我怎么就攤上這事≈澹” “怎么了肄梨?”我有些...
    開封第一講書人閱讀 157,921評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長膘格。 經(jīng)常有香客問我,道長财松,這世上最難降的妖魔是什么瘪贱? 我笑而不...
    開封第一講書人閱讀 56,648評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮辆毡,結(jié)果婚禮上菜秦,老公的妹妹穿的比我還像新娘。我一直安慰自己舶掖,他們只是感情好球昨,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,770評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著眨攘,像睡著了一般主慰。 火紅的嫁衣襯著肌膚如雪嚣州。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,950評(píng)論 1 291
  • 那天共螺,我揣著相機(jī)與錄音该肴,去河邊找鬼。 笑死藐不,一個(gè)胖子當(dāng)著我的面吹牛匀哄,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播雏蛮,決...
    沈念sama閱讀 39,090評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼涎嚼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了挑秉?” 一聲冷哼從身側(cè)響起法梯,我...
    開封第一講書人閱讀 37,817評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎衷模,沒想到半個(gè)月后鹊汛,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,275評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡阱冶,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,592評(píng)論 2 327
  • 正文 我和宋清朗相戀三年刁憋,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片木蹬。...
    茶點(diǎn)故事閱讀 38,724評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡至耻,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出镊叁,到底是詐尸還是另有隱情尘颓,我是刑警寧澤,帶...
    沈念sama閱讀 34,409評(píng)論 4 333
  • 正文 年R本政府宣布晦譬,位于F島的核電站疤苹,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏敛腌。R本人自食惡果不足惜卧土,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,052評(píng)論 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望像樊。 院中可真熱鬧尤莺,春花似錦、人聲如沸生棍。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至友酱,卻和暖如春晴音,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背粹污。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評(píng)論 1 266
  • 我被黑心中介騙來泰國打工段多, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人壮吩。 一個(gè)月前我還...
    沈念sama閱讀 46,503評(píng)論 2 361
  • 正文 我出身青樓进苍,卻偏偏與公主長得像,于是被迫代替她去往敵國和親鸭叙。 傳聞我的和親對(duì)象是個(gè)殘疾皇子觉啊,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,627評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容