詳解Mysql分佈式事務XA(跨數據庫事務)

在開發中,爲了下降單點壓力,一般會根據業務狀況進行分表分庫,將表分佈在不一樣的庫中(庫可能分佈在不一樣的機器上)。在這種場景下,事務的提交會變得相對複雜,由於多個節點(庫)的存在,可能存在部分節點提交失敗的狀況,即事務的ACID特性須要在各個不一樣的數據庫實例中保證。好比更新db1庫的A表時,必須同步更新db2庫的B表,兩個更新造成一個事務,要麼都成功,要麼都失敗。 
那麼咱們如何利用mysql實現分佈式數據庫的事務呢?php

Mysql 爲咱們提供了分佈式事務解決方案(https://dev.mysql.com/doc/refman/5.7/en/xa.html 這是mysql5.7的文檔) 
這裏先聲明兩個概念:html

資源管理器(resource manager):用來管理系統資源,是通向事務資源的途徑。數據庫就是一種資源管理器。資源管理還應該具備管理事務提交或回滾的能力。
事務管理器(transaction manager):事務管理器是分佈式事務的核心管理者。事務管理器與每一個資源管理器(resource 
manager)進行通訊,協調並完成事務的處理。事務的各個分支由惟一命名進行標識。
mysql在執行分佈式事務(外部XA)的時候,mysql服務器至關於xa事務的資源管理器,與mysql連接的客戶端至關於事務管理器。mysql

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

分佈式事務一般採用2PC協議,全稱Two Phase Commitment Protocol。該協議主要爲了解決在分佈式數據庫場景下,全部節點間數據一致性的問題。分佈式事務經過2PC協議將提交分紅兩個階段:sql

prepare;
commit/rollback;

階段一爲準備(prepare)階段。即全部的參與者準備執行事務並鎖住須要的資源。參與者ready時,向transaction manager報告已準備就緒。 
階段二爲提交階段(commit)。當transaction manager確認全部參與者都ready後,向全部參與者發送commit命令。 
以下圖所示: 數據庫

事務協調者transaction manager

由於XA 事務是基於兩階段提交協議的,因此須要有一個事務協調者(transaction manager)來保證全部的事務參與者都完成了準備工做(第一階段)。若是事務協調者(transaction manager)收到全部參與者都準備好的消息,就會通知全部的事務均可以提交了(第二階段)。MySQL 在這個XA事務中扮演的是參與者的角色,而不是事務協調者(transaction manager)。bash

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事務示例

一、首先要確保mysql開啓XA事務支持工具

SHOW VARIABLES LIKE '%xa%'

若是innodb_support_xa的值是ON就說明mysql已經開啓對XA事務的支持了。 
若是不是就執行:性能

SET innodb_support_xa = ON

開啓.

二、代碼以下:

<?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='abc' 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。

相關文章
相關標籤/搜索