XA 分布式事務原理


在開發中,為了降低單點壓力,通常會根據業務情況進行分表分庫,將表分布在不同的庫中(庫可能分布在不同的機器上)。在這種場景下,事務的提交會變得相對復雜,因為多個節點(庫)的存在,可能存在部分節點提交失敗的情況,即事務的ACID特性需要在各個不同的數據庫實例中保證。比如更新db1庫的A表時,必須同步更新db2庫的B表,兩個更新形成一個事務,要么都成功,要么都失敗。 
那么我們如何利用MySQL實現分布式數據庫的事務呢?

Mysql 5.7為我們提供了分布式事務解決方案(https://dev.mysql.com/doc/refman/5.7/en/xa.html) 
這里先聲明兩個概念:

  • 資源管理器(resource manager:用來管理系統資源,是通向事務資源的途徑。數據庫就是一種資源管理器。資源管理還應該具有管理事務提交或回滾的能力。
  • 事務管理器(transaction manager):事務管理器是分布式事務的核心管理者。事務管理器與每個資源管理器(resource 
    manager)進行通信,協調並完成事務的處理。事務的各個分支由唯一命名進行標識。

mysql在執行分布式事務(外部XA)的時候,mysql服務器相當於xa事務資源管理器,與mysql鏈接的客戶端相當於事務管理器。

分布式事務原理:分段式提交

分布式事務通常采用2PC協議,全稱Two Phase Commitment Protocol。該協議主要為了解決在分布式數據庫場景下,所有節點間數據一致性的問題。分布式事務通過2PC協議將提交分成兩個階段:

  • prepare
  • commit/rollback

階段一為准備(prepare)階段。即所有的參與者准備執行事務並鎖住需要的資源。參與者ready時,向transaction manager報告已准備就緒。 
階段二為提交階段(commit。當transaction manager確認所有參與者都ready后,向所有參與者發送commit命令。 
如下圖所示: 

事務協調者transaction manager

因為XA 事務是基於兩階段提交協議的,所以需要有一個事務協調者(transaction manager)來保證所有的事務參與者都完成了准備工作(第一階段)。如果事務協調者(transaction manager)收到所有參與者都准備好的消息,就會通知所有的事務都可以提交了(第二階段)。MySQL 在這個XA事務中扮演的是參與者的角色,而不是事務協調者(transaction manager)。

Mysql的XA事務分為外部XA和內部XA

  • 外部XA用於跨多MySQL實例的分布式事務,需要應用層作為協調者,通俗的說就是比如我們在PHP中寫代碼,那么PHP書寫的邏輯就是協調者。應用層負責決定提交還是回滾,崩潰時的懸掛事務。MySQL數據庫外部XA可以用在分布式數據庫代理層,實現對MySQL數據庫的分布式事務支持,例如開源的代理工具:網易的DDB,淘寶的TDDL等等。
  • 內部XA事務用於同一實例下跨多引擎事務,由Binlog作為協調者,比如在一個存儲引擎提交時,需要將提交信息寫入二進制日志,這就是一個分布式內部XA事務,只不過二進制日志的參與者是MySQL本身。Binlog作為內部XA的協調者,在binlog中出現的內部xid,在crash recover時,由binlog負責提交。(這是因為,binlog不進行prepare,只進行commit,因此在binlog中出現的內部xid,一定能夠保證其在底層各存儲引擎中已經完成prepare)

MySQL XA事務基本語法

XA {START|BEGIN} xid [JOIN|RESUME] 啟動xid事務 (xid 必須是一個唯一值不支持[JOIN|RESUME]子句
XA END xid [SUSPEND [FOR MIGRATE]] 結束xid事務 不支持[SUSPEND [FOR MIGRATE]] 子句
XA PREPARE xid 准備、預提交xid事務 
XA COMMIT xid [ONE PHASE] 提交xid事務 
XA ROLLBACK xid 回滾xid事務 
XA RECOVER 查看處於PREPARE 階段的所有事務

PHP調用MYSQL XA事務示例

1、首先要確保mysql開啟XA事務支持

SHOW VARIABLES LIKE '%xa%'

  • 1
  • 1

如果innodb_support_xa的值是ON就說明mysql已經開啟對XA事務的支持了。 
如果不是就執行:

SET innodb_support_xa = ON

  • 1
  • 1

開啟

2、代碼如下:

<?PHP$dbtest1 = new mysqli("172.20.101.17","public","public","dbtest1")or die("dbtest1 連接失敗");$dbtest2     = new mysqli("172.20.101.18","public","public","dbtest2")or die("dbtest2 連接失敗");

//為XA事務指定一個id,xid 必須是一個唯一值。$xid = uniqid("");

//兩個庫指定同一個事務id,表明這兩個庫的操作處於同一事務中$dbtest1->query("XA START '$xid'");//准備事務1$dbtest2->query("XA START '$xid'");//准備事務2

 

try {

    //$dbtest1

    $return = $dbtest1->query("UPDATE member SET name='twm' WHERE id=1") ;

    if($return == false) {

       throw new Exception("庫dbtest1@172.20.101.17執行update member操作失敗!");

    }

 

    //$dbtest2

    $return = $dbtest2->query("UPDATE memberpoints SET point=point+10 WHERE memberid=1") ;

    if($return == false) {

       throw new Exception("庫dbtest1@172.20.101.18執行update memberpoints操作失敗!");

    }

 

    //階段1:$dbtest1提交准備就緒

    $dbtest1->query("XA END '$xid'");

    $dbtest1->query("XA PREPARE '$xid'");

    //階段1:$dbtest2提交准備就緒

    $dbtest2->query("XA END '$xid'");

    $dbtest2->query("XA PREPARE '$xid'");

 

    //階段2:提交兩個庫

    $dbtest1->query("XA COMMIT '$xid'");

    $dbtest2->query("XA COMMIT '$xid'");

catch (Exception $e) {

    //階段2:回滾

    $dbtest1->query("XA ROLLBACK '$xid'");

    $dbtest2->query("XA ROLLBACK '$xid'");

    die($e->getMessage());

}

$dbtest1->close();$dbtest2->close();

?>

XA的性能問題

XA的性能很低。一個數據庫的事務和多個數據庫間的XA事務性能對比可發現,性能差10倍左右。因此要盡量避免XA事務,例如可以將數據寫入本地,用高性能的消息系統分發數據。或使用數據庫復制等技術。只有在這些都無法實現,且性能不是瓶頸時才應該使用XA

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM