日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

mysql平台workb_MySQL分布式事务

發(fā)布時間:2025/3/20 数据库 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 mysql平台workb_MySQL分布式事务 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

一、分布式事務(wù)

在說分布式事務(wù)(XA)之前,可以先看一下“以交易系統(tǒng)為例,看分布式事務(wù)架構(gòu)的五大演進(jìn)”,闡述了分布式事務(wù)解決了什么問題?

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

XA事務(wù)語允許不同數(shù)據(jù)庫之間的分布式事務(wù),如一臺服務(wù)器是MySQL數(shù)據(jù)庫的,另一臺是Oracle數(shù)據(jù)庫的,又可能還有一臺服務(wù)器是SQL Server數(shù)據(jù)庫的,只要參與在全局事務(wù)中的每個節(jié)點都支持XA事務(wù)。

考慮下面一種場景:當(dāng)你發(fā)了工資之后,把你的當(dāng)月工資¥10000從支付寶轉(zhuǎn)到了余額寶。如果在支付寶賬戶扣除¥10000之后,余額寶系統(tǒng)掛掉了,余額寶的賬戶并沒有增加¥10000,這時候就出現(xiàn)了數(shù)據(jù)不一致的情況。

# 支付寶

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

# 余額寶

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

1

2

3

4

5

# 支付寶

updateaccountsetmoney=money-10000whereuser='dkey';

# 余額寶

updateaccountsetmoney=money+10000whereuser='dkey';

在很多系統(tǒng)中都能找到上述情況的影子:

在下單的時候,需要在訂單表中插入一條數(shù)據(jù),然后把庫存減去一。

在搜索的時候如果點擊了廣告,需要先記錄該點擊事件,然后通知商家系統(tǒng)扣除廣告費。

在這種情況下,一定需要使用分布式事務(wù)來保證數(shù)據(jù)的安全。如果發(fā)生的操作不能全部提交或回滾,那么任何一個節(jié)點出現(xiàn)問題都會導(dǎo)致嚴(yán)重的結(jié)果。

在一個分布式事務(wù)結(jié)束的時候,事務(wù)的原子特性要求所有參與該事務(wù)的服務(wù)器必須全部提交或全部放棄該事務(wù)。為了實現(xiàn)這一點,其中一個服務(wù)器承擔(dān)了協(xié)調(diào)者(coordinater)的角色,由它來保證所有的服務(wù)器獲得相同的結(jié)果。

協(xié)調(diào)者(coordinater)的工作方式取決于它選用的協(xié)議,“兩階段提交”是分布式事務(wù)最常用的協(xié)議。

兩階段提交協(xié)議(Two-phase Commit,2PC)經(jīng)常被用來實現(xiàn)分布式事務(wù)。一般由一個或多個資源管理器(resource managers)、一個事務(wù)協(xié)調(diào)器(transaction coordinater)以及一個應(yīng)用程序(application program)組成。事務(wù)協(xié)調(diào)器可以和資源管理器在一臺機(jī)器上。

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

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

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

在MySQL數(shù)據(jù)庫的分布式事務(wù)中,資源管理器就是MySQL數(shù)據(jù)庫,事務(wù)協(xié)調(diào)器為連接MySQL服務(wù)器的客戶端(支持分布式事務(wù)的客戶端)。下圖顯示了一個分布式事務(wù)的模型。

分布式事務(wù)通常采用2PC協(xié)議,全稱Two Phase Commitment Protocol。該協(xié)議主要為了解決在分布式數(shù)據(jù)庫場景下,所有節(jié)點間數(shù)據(jù)一致性的問題。在分布式事務(wù)環(huán)境下,事務(wù)的提交會變得相對比較復(fù)雜,因為多個節(jié)點的存在,可能存在部分節(jié)點提交失敗的情況,即事務(wù)的ACID特性需要在各個數(shù)據(jù)庫實例中保證??偠灾?#xff0c;在分布式提交時,只要發(fā)生一個節(jié)點提交失敗,則所有的節(jié)點都不能提交,只有當(dāng)所有節(jié)點都能提交時,整個分布式事務(wù)才允許被提交。

在該協(xié)議的第一個階段,每個參與者投票表決該事務(wù)是放棄還是提交,一旦參與者要求提交事務(wù),那么就不允許放棄該事務(wù)。因此,在一個參與者要求提交事務(wù)之前,它必須保證最終能夠執(zhí)行分布式事務(wù)中自己的那部分,即使該參與者出現(xiàn)故障而被中途替換掉。

一個事務(wù)的參與者如果最終能提交事務(wù),那么可以說參與者處于事務(wù)的準(zhǔn)備好(prepared)狀態(tài)。為了保證能夠提交,每個參與者必須將事務(wù)中所有發(fā)生改變的對象以及自身的狀態(tài)(prepared)保存到持久性存儲中。

在該協(xié)議的第二個階段,事務(wù)的每個參與者執(zhí)行最終統(tǒng)一的決定。如果任何一個參與者投票放棄事務(wù),那么最終的決定是放棄事務(wù),則所有的節(jié)點都被告知需要回滾。如果所有的參與者都投票提交事務(wù),那么最終的決定是提交事務(wù)。

1. 我們的應(yīng)用程序(client)發(fā)起一個開始請求到TC;

2. TC先將消息寫到本地日志,之后向所有的RM發(fā)起消息。以支付寶轉(zhuǎn)賬到余額寶為例,TC給A的prepare消息是通知支付寶數(shù)據(jù)庫相應(yīng)賬目扣款1萬,TC給B的prepare消息是通知余額寶數(shù)據(jù)庫相應(yīng)賬目增加1w。為什么在執(zhí)行任務(wù)前需要先寫本地日志,主要是為了故障后恢復(fù)用,本地日志起到現(xiàn)實生活中憑證的效果,如果沒有本地日志(憑證),出問題容易死無對證;

3. RM收到消息后,執(zhí)行具體本機(jī)事務(wù),但不會進(jìn)行commit,如果成功返回,不成功返回。同理,返回前都應(yīng)把要返回的消息寫到日志里,當(dāng)作憑證。

4. TC收集所有執(zhí)行器返回的消息,如果所有執(zhí)行器都返回yes,那么給所有執(zhí)行器發(fā)生送commit消息,執(zhí)行器收到commit后執(zhí)行本地事務(wù)的commit操作;如果有任一個執(zhí)行器返回no,那么給所有執(zhí)行器發(fā)送rollback消息,執(zhí)行器收到rollback消息后執(zhí)行事務(wù)rollback操作。

注:TC或RM把發(fā)送或接收到的消息先寫到日志里,主要是為了故障后恢復(fù)用。如某一RM從故障中恢復(fù)后,先檢查本機(jī)的日志,如果已收到,則提交,如果則回滾。如果是,則再向TC詢問一下,確定下一步。如果什么都沒有,則很可能在階段RM就崩潰了,因此需要回滾。

可見與本地事務(wù)不同的是,分布式事務(wù)需要多一次的PREPARE操作,待收到所有節(jié)點的同意信息后,再進(jìn)行COMMIT或是ROLLBACK操作。

現(xiàn)如今實現(xiàn)基于兩階段提交的分布式事務(wù)也沒那么困難了,如果使用java,那么可以使用開源軟件atomikos(http://www.atomikos.com/)來快速實現(xiàn)。

不過但凡使用過的上述兩階段提交的同學(xué)都可以發(fā)現(xiàn)性能實在是太差,根本不適合高并發(fā)的系統(tǒng)。為什么?

兩階段提交涉及多次節(jié)點間的網(wǎng)絡(luò)通信,通信時間太長!

事務(wù)時間相對于變長了,鎖定的資源的時間也變長了,造成資源等待時間也增加好多!

正是由于分布式事務(wù)存在很嚴(yán)重的性能問題,大部分高并發(fā)服務(wù)都在避免使用,往往通過其他途徑來解決數(shù)據(jù)一致性問題。比如使用消息隊列來避免分布式事務(wù)。

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

XA事務(wù)語法

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

mysql> XA START 'any_unique_id';

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

mysql> XA END 'any_unique_id';

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

mysql> XA PREPARE 'any_unique_id';

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

mysql> XA COMMIT 'any_unique_id';

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

mysql> XA ROLLBACK 'any_unique_id';

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

mysql> XA RECOVER;

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

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

mysql>XASTART'any_unique_id';

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

mysql>XAEND'any_unique_id';

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

mysql>XAPREPARE'any_unique_id';

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

mysql>XACOMMIT'any_unique_id';

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

mysql>XAROLLBACK'any_unique_id';

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

mysql>XARECOVER;

XA事務(wù)演示

在單個節(jié)點上運行分布式事務(wù)是沒有意義的,起碼兩個節(jié)點才有意義。但是要在MySQL數(shù)據(jù)庫的命令行下演示多個節(jié)點參與的分布式事務(wù)也是行不通的。通常來說,都是通過編程語言來完成分布式事務(wù)操作的。當(dāng)前Java的JTA可以很好地支持MySQL的分布式事務(wù)。下面用一個簡單的例子來演示:

public class XaDemo {

public static MysqlXADataSource getDataSource(String connStr, String user, String pwd) {

try {

MysqlXADataSource ds = new MysqlXADataSource();

ds.setUrl(connStr);

ds.setUser(user);

ds.setPassword(pwd);

return ds;

} catch (Exception e) {

e.printStackTrace();

}

return null;

}

public static void main(String[] arg) {

String connStr1 = "jdbc:mysql://192.168.0.1:3306/test";

String connStr2 = "jdbc:mysql://192.168.0.2:3306/test";

try {

//從不同數(shù)據(jù)庫獲取數(shù)據(jù)庫數(shù)據(jù)源

MysqlXADataSource ds1 = getDataSource(connStr1, "root", "123456");

MysqlXADataSource ds2 = getDataSource(connStr2, "root", "123456");

//數(shù)據(jù)庫1獲取連接

XAConnection xaConnection1 = ds1.getXAConnection();

XAResource xaResource1 = xaConnection1.getXAResource();

Connection connection1 = xaConnection1.getConnection();

Statement statement1 = connection1.createStatement();

//數(shù)據(jù)庫2獲取連接

XAConnection xaConnection2 = ds2.getXAConnection();

XAResource xaResource2 = xaConnection2.getXAResource();

Connection connection2 = xaConnection2.getConnection();

Statement statement2 = connection2.createStatement();

//創(chuàng)建事務(wù)分支的xid

Xid xid1 = new MysqlXid(new byte[] { 0x01 }, new byte[] { 0x02 }, 100);

Xid xid2 = new MysqlXid(new byte[] { 0x011 }, new byte[] { 0x012 }, 100);

try {

//事務(wù)分支1關(guān)聯(lián)分支事務(wù)sql語句

xaResource1.start(xid1, XAResource.TMNOFLAGS);

int update1Result = statement1.executeUpdate("update account_from set money=money - 50 where id=1");

xaResource1.end(xid1, XAResource.TMSUCCESS);

//事務(wù)分支2關(guān)聯(lián)分支事務(wù)sql語句

xaResource2.start(xid2, XAResource.TMNOFLAGS);

int update2Result = statement2.executeUpdate("update account_to set money= money + 50 where id=1");

xaResource2.end(xid2, XAResource.TMSUCCESS);

// 兩階段提交協(xié)議第一階段

int ret1 = xaResource1.prepare(xid1);

int ret2 = xaResource2.prepare(xid2);

// 兩階段提交協(xié)議第二階段

if (XAResource.XA_OK == ret1 && XAResource.XA_OK == ret2) {

xaResource1.commit(xid1, false);

xaResource2.commit(xid2, false);

System.out.println("reslut1:" + update1Result + ", result2:" + update2Result);

}

} catch (Exception e) {

e.printStackTrace();

}

} catch (Exception e) {

e.printStackTrace();

}

}

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

publicclassXaDemo{

publicstaticMysqlXADataSourcegetDataSource(StringconnStr,Stringuser,Stringpwd){

try{

MysqlXADataSourceds=newMysqlXADataSource();

ds.setUrl(connStr);

ds.setUser(user);

ds.setPassword(pwd);

returnds;

}catch(Exceptione){

e.printStackTrace();

}

returnnull;

}

publicstaticvoidmain(String[]arg){

StringconnStr1="jdbc:mysql://192.168.0.1:3306/test";

StringconnStr2="jdbc:mysql://192.168.0.2:3306/test";

try{

//從不同數(shù)據(jù)庫獲取數(shù)據(jù)庫數(shù)據(jù)源

MysqlXADataSourceds1=getDataSource(connStr1,"root","123456");

MysqlXADataSourceds2=getDataSource(connStr2,"root","123456");

//數(shù)據(jù)庫1獲取連接

XAConnectionxaConnection1=ds1.getXAConnection();

XAResourcexaResource1=xaConnection1.getXAResource();

Connectionconnection1=xaConnection1.getConnection();

Statementstatement1=connection1.createStatement();

//數(shù)據(jù)庫2獲取連接

XAConnectionxaConnection2=ds2.getXAConnection();

XAResourcexaResource2=xaConnection2.getXAResource();

Connectionconnection2=xaConnection2.getConnection();

Statementstatement2=connection2.createStatement();

//創(chuàng)建事務(wù)分支的xid

Xidxid1=newMysqlXid(newbyte[]{0x01},newbyte[]{0x02},100);

Xidxid2=newMysqlXid(newbyte[]{0x011},newbyte[]{0x012},100);

try{

//事務(wù)分支1關(guān)聯(lián)分支事務(wù)sql語句

xaResource1.start(xid1,XAResource.TMNOFLAGS);

intupdate1Result=statement1.executeUpdate("update account_from set money=money - 50 where id=1");

xaResource1.end(xid1,XAResource.TMSUCCESS);

//事務(wù)分支2關(guān)聯(lián)分支事務(wù)sql語句

xaResource2.start(xid2,XAResource.TMNOFLAGS);

intupdate2Result=statement2.executeUpdate("update account_to set money= money + 50 where id=1");

xaResource2.end(xid2,XAResource.TMSUCCESS);

//兩階段提交協(xié)議第一階段

intret1=xaResource1.prepare(xid1);

intret2=xaResource2.prepare(xid2);

//兩階段提交協(xié)議第二階段

if(XAResource.XA_OK==ret1&&XAResource.XA_OK==ret2){

xaResource1.commit(xid1,false);

xaResource2.commit(xid2,false);

System.out.println("reslut1:"+update1Result+", result2:"+update2Result);

}

}catch(Exceptione){

e.printStackTrace();

}

}catch(Exceptione){

e.printStackTrace();

}

}

}

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

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

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

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

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

當(dāng)mysql crash后重新啟動之后,執(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 the??ACTIVE state

mysql> lock table t read;

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

1

2

3

4

5

6

7

8

mysql>xastart't1xa';

QueryOK,0rowsaffected(0.04sec)

mysql>begin;

ERROR1399(XAE07):XAER_RMFAIL:Thecommandcannotbeexecutedwhenglobaltransactionisinthe??ACTIVEstate

mysql>locktabletread;

ERROR1399(XAE07):XAER_RMFAIL:Thecommandcannotbeexecutedwhenglobaltransactionisinthe??ACTIVEstate

開啟了本地事務(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

1

2

3

4

5

mysql>begin;

QueryOK,0rowsaffected(0.00sec)

mysql>xastart'rrrr';

ERROR1400(XAE09):XAER_OUTSIDE:Someworkisdoneoutsideglobaltransaction

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

所以如果在執(zhí)行xa事務(wù)過程中有語句出錯了,你也需要先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)

1

2

3

4

5

6

7

8

9

10

11

mysql>xastart'tt';

QueryOK,0rowsaffected(0.00sec)

mysql>xarollback'tt';

ERROR1399(XAE07):XAER_RMFAIL:ThecommandcannotbeexecutedwhenglobaltransactionisintheACTIVEstate

mysql>xaend'tt';

QueryOK,0rowsaffected(0.00sec)

mysql>xarollback'tt';

QueryOK,0rowsaffected(0.00sec)

四、MySQL 5.7對分布式事務(wù)的支持

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

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

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

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

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

CREATE TABLE t(

id INT AUTO_INCREMENT PRIMARY KEY,

a INT

) ENGINE=InnoDB;

1

2

3

4

CREATETABLEt(

idINTAUTO_INCREMENTPRIMARYKEY,

aINT

)ENGINE=InnoDB;

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

mysql> XA START 'mysql56';

Query OK, 0 rows affected (0.00 sec)

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

Query OK, 1 row affected (0.02 sec)

mysql> XA END 'mysql56';

Query OK, 0 rows affected (0.00 sec)

mysql> XA PREPARE 'mysql56';

Query OK, 0 rows affected (0.00 sec)

1

2

3

4

5

6

7

8

9

10

11

mysql>XASTART'mysql56';

QueryOK,0rowsaffected(0.00sec)

mysql>INSERTINTOtVALUES(1,1);

QueryOK,1rowaffected(0.02sec)

mysql>XAEND'mysql56';

QueryOK,0rowsaffected(0.00sec)

mysql>XAPREPARE'mysql56';

QueryOK,0rowsaffected(0.00sec)

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

mysql> XA RECOVER;

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

| formatID | gtrid_length | bqual_length | data ? ?|

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

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

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

1

2

3

4

5

6

mysql>XARECOVER;

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

|formatID|gtrid_length|bqual_length|data??|

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

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

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

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

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

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

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

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

mysql> XA START 'mysql57';

Query OK, 0 rows affected (0.00 sec)

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

Query OK, 1 row affected (0.02 sec)

mysql> XA END 'mysql57';

Query OK, 0 rows affected (0.00 sec)

mysql> XA PREPARE 'mysql57';

Query OK, 0 rows affected (0.00 sec)

1

2

3

4

5

6

7

8

9

10

11

mysql>XASTART'mysql57';

QueryOK,0rowsaffected(0.00sec)

mysql>INSERTINTOtVALUES(1,1);

QueryOK,1rowaffected(0.02sec)

mysql>XAEND'mysql57';

QueryOK,0rowsaffected(0.00sec)

mysql>XAPREPARE'mysql57';

QueryOK,0rowsaffected(0.00sec)

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

同時也對比下Slave上的Relay log,如下:

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

當(dāng)然僅靠這一點是不夠的,因為我們知道Slave通過SQL thread來回放Relay log信息,由于prepare的事務(wù)能阻塞整個session,而回放的SQL thread只有一個(不考慮并行回放),那么SQL thread會不會因為被分布式事務(wù)的prepare階段所阻塞,從而造成整個SQL thread回放出現(xiàn)問題?這也正是官方要解決的第二個問題:怎么樣能使SQL thread在回放到分布式事務(wù)的prepare階段時,不阻塞后面event的回放?其實這個實現(xiàn)也很簡單(在xa.cc::applier_reset_xa_trans),只要在SQL thread回放到prepare的時候,進(jìn)行類似于客戶端斷開連接的處理即可(把相關(guān)cache與SQL thread的連接句柄脫離)。最后在Slave服務(wù)器上,用戶通過命令XA RECOVER可以查到如下信息:

mysql> XA RECOVER;

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

| formatID | gtrid_length | bqual_length | data ? ?|

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

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

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

1

2

3

4

5

6

mysql>XARECOVER;

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

|formatID|gtrid_length|bqual_length|data??|

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

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

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

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

總結(jié)

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

如果您覺得本站對你有幫助,那么可以支付寶掃碼捐助以幫助本站更好地發(fā)展,在此謝過。

《新程序員》:云原生和全面數(shù)字化實踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀

總結(jié)

以上是生活随笔為你收集整理的mysql平台workb_MySQL分布式事务的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。