详解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。

相关文章
相关标签/搜索