閱讀147 返回首頁    go 阿裏雲 go 技術社區[雲棲]


MySQL · 特性分析 · 淺談 MySQL 5.7 XA 事務改進

關於MySQL XA 事務

MySQL XA 事務通常用於分布式事務處理當中。比如在分庫分表的場景下,當遇到一個用戶事務跨了多個分區,需要使用XA事務 來完成整個事務的正確的提交和回滾,即保證全局事務的一致性。

XA 事務在分庫分表場景的使用

下圖是個典型的分庫分表場景,前端是一個Proxy後麵帶若幹個MySQL實例,每個實例是一個分區。

XA-sharding.png

假設一個表test定義如下,Proxy根據主鍵”a”算Hash決定一條記錄應該分布在哪個節點上:

create table test(a int primay key, b int) engine = innodb;

應用發到Proxy的一個事務如下:

begin;
insert into test values (1, 1);
update test set b = 1 where a = 10;
commit;

Proxy收到這個事務需要將它轉成XA事務發送到後端的數據庫以保證這個事務能夠安全的提交或回滾,一般的Proxy的處理步驟 如下:

  1. Proxy先收到begin,它隻需要設置一下自己的狀態不需要向後端數據庫發送
  2. 當收到 insert 語句時Proxy會解析語句,根據“a”的值計算出該條記錄應該位於哪個節點上,這裏假設是“分庫1”
  3. Proxy就會向分庫1上發送語句xa start ‘xid1’,開啟一個XA事務,這裏xid1是Proxy自動生成的一個全局事務ID;同時原來 的insert語句insert into values(1,1)也會一並發送到分庫1上。
  4. 這時Proxy遇到了update語句,Proxy會解析 where條件主鍵的值來決定該條語句會被發送到哪個節點上,這裏假設是“分庫2”
  5. Proxy就會向分庫2上發送語句xa start ‘xid1’,開啟一個XA事務,這裏xid1是Proxy之前已經生成的一個全局事務ID;同時原來 的update語句update test set b = 1 where a = 10也會一並發送到分庫2上。
  6. 最後當Proxy解析到commit語句時,就知道一個用戶事務已經結束了,就開啟提交流程
  7. Proxy會向分庫1和分庫2發送 xa end ‘xid1’;xa prepare ‘xid1’語句,當收到執行都成功回複後,則繼續進行到下一步,如果任何一個分 庫返回失敗,則向分庫1和分庫2 發送 xa rollback ‘xid1’,回滾整個事務
  8. 當 xa prepare ‘xid1’都返回成功,那麼 proxy會向分庫1和分庫2上發送 xa commit ‘xid1’,來最終提交事務。

這裏有一個可能的優化,即在步驟4時如果Proxy計算出update語句發送的節點仍然是“分庫1”時,在遇到commit時,由於隻涉 及到一個分庫,它可以直接向“分庫1”發送 xa end ‘xid1’; xa commit ‘xid1’ one phase來直接提交該事務,避免走 prepare階段來提高效率。

XA對事務安全的影響分析

從以上分庫分表場景下分布式事務的處理過程來看,整個分布式事務的安全性依賴是XA Prepare了的事務的可靠性,也就是在 數據庫節點上 XA Prepare了的事務必須是持久化了的,這樣當XA Commit發來時才可以提交。設想如下場景:

  1. Proxy已經向分庫1和分庫2上發送完了 xa prepare ‘xid1’語句,並得到了成功的回複
  2. Proxy向分庫1上發送了 ‘xa commit ‘xid1’語句,並已經成功返回
  3. 當 Proxy向分庫2上發送 ‘xa commit ‘xid1’時,網絡斷開了,或者分庫2的數據庫實例被kill了
  4. 當網絡恢複(這時相關的Session已經退出了)或數據庫實例再啟動後(或切換到備庫),XA prepare了的事務已經回滾了, 當Proxy XA commit ‘xid1’發過來後數據庫實例根本找不到xid1這個xa事務

上麵的過程就導致了分布式事務的不一致:分庫1提交了事務,分庫2回滾了事務,整個事務提交了一半,回滾了一半。

在MySQL 5.6中以上過程是可能發生的,因為xa prepare並沒有嚴格的持久化,當Session斷開,數據庫崩潰等情況下這些事務 會被回滾掉,而且的當一個主庫配置了SemiSync的備庫時xa prepare了的事務也不會被發送的備庫,如果主庫切換到備庫這些 事務也會丟失。

MySQL 5.7 XA可靠性改進

MySQL 5.7解決了 xa prepare了的事務的嚴格持久化問題,也就是在session斷開和實例崩潰重啟情況下這些事務不丟,同時在 xa prepare ‘xid1’返回之前XA事務也會同步到備庫。下麵將通過在5.6和5.7上分別執行xa prepare並對binlog event進行分析 來演示這個改進。

斷開連接對xa prepare的事務影響

在5.6和5.7上分別執行如下sql然後斷開連接,再重新連接使用的xa recover驗證 XA 事務是否回滾了。

xa start 'xid1';
insert into test values(1, 1);
xa end 'xid1';
xa prepare 'xid1';
-- 這裏斷開再連上新連接執行 xa recover

在 5.6 的版本上將返回空的結果,在 5.7 的版本上返回:

mysql> xa recover;
+----------+--------------+--------------+------+
| formatID | gtrid_length | bqual_length | data |
+----------+--------------+--------------+------+
|        1 |            4 |            0 | xid1 |
+----------+--------------+--------------+------+
1 row in set (0.00 sec)

說明斷開連接後 5.7的prepare了的xa事務沒有丟失。

XA 事務的 Binlog events 異同

在5.6和5.7上分別執行如下事務,然後用 show binlog events 查看兩者binlog的不同:

xa start 'xid1';
insert into test values(1, 1);
xa end 'xid1';
xa prepare 'xid1';
xa commit 'xid1';

5.6的結果:

mysql-bin.000001 | 304 | Gtid           |      3706 |         352 | SET @@SESSION.GTID_NEXT= 'uuid:2'
mysql-bin.000001 | 352 | Query          |      3706 |         424 | BEGIN
mysql-bin.000001 | 424 | Table_map      |      3706 |         472 | table_id: 71 (test.test)
mysql-bin.000001 | 472 | Write_rows     |      3706 |         516 | table_id: 71 flags: STMT_END_F
mysql-bin.000001 | 516 | Query          |      3706 |         589 | COMMIT

5.7的結果:

mysql-bin.000001 |  544 | Gtid           |      3707 |         592 | SET @@SESSION.GTID_NEXT= 'uuid:3'
mysql-bin.000001 |  592 | Query          |      3707 |         685 | XA START X'78696431',X'',1
mysql-bin.000001 |  685 | Table_map      |      3707 |         730 | table_id: 74 (test.t) 
mysql-bin.000001 |  730 | Write_rows     |      3707 |         774 | table_id: 74 flags: STMT_END_F
mysql-bin.000001 |  774 | Query          |      3707 |         865 | XA END X'78696431',X'',1 
mysql-bin.000001 |  865 | XA_prepare     |      3707 |         905 | XA PREPARE X'78696431',X'',1
mysql-bin.000001 |  905 | Gtid           |      3707 |         953 | SET @@SESSION.GTID_NEXT= 'uuid:4' |
mysql-bin.000001 |  953 | Query          |      3707 |        1047 | XA COMMIT X'78696431',X'',1

可以看到 MySQL 5.6 XA 事務和普通事務的binlog是一樣的,並沒有體現 xa prepare。而到了 MySQL 5.7 XA 事務的binlog和 普通的事務是完全不同的,XA Prepare有單獨的Log event類型,有自己的Gtid,當開啟semi-sync的情況下,MySQL 5.7 執行 XA prepare 時會等備庫回複後才返回結果給客戶端,這樣XA prepare執行完就是安全的。

通過以上分析可以看出 MySQL 5.7在XA事務安全性方麵做了很大的改進,後續月報文章將會對它的實現做分析。

最後更新:2017-09-21 09:03:39

  上一篇:go  MySQL · 特性分析 · 利用gdb跟蹤MDL加鎖過程
  下一篇:go  MySQL · myrocks · myrocks之Bloom filter