在開發中,爲了下降單點壓力,一般會根據業務狀況進行分表分庫,將表分佈在不一樣的庫中(庫可能分佈在不一樣的機器上)。在這種場景下,事務的提交會變得相對複雜,由於多個節點(庫)的存在,可能存在部分節點提交失敗的狀況,即事務的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命令。
以下圖所示: 數據庫
由於XA 事務是基於兩階段提交協議的,因此須要有一個事務協調者(transaction manager)來保證全部的事務參與者都完成了準備工做(第一階段)。若是事務協調者(transaction manager)收到全部參與者都準備好的消息,就會通知全部的事務均可以提交了(第二階段)。MySQL 在這個XA事務中扮演的是參與者的角色,而不是事務協調者(transaction manager)。bash
外部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 階段的全部事務
一、首先要確保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事務性能對比可發現,性能差10倍左右。所以要儘可能避免XA事務,例如能夠將數據寫入本地,用高性能的消息系統分發數據。或使用數據庫複製等技術。只有在這些都沒法實現,且性能不是瓶頸時才應該使用XA。