MySQL程序员系统运维专家

MySQL XA 介绍

2017-08-17  本文已影响159人  foreversunyao

引言

在MySQL 5.7.7版本中,Oracle 官方将MySQL XA 一直存在的一个“bug” 进行了修复,使得MySQL XA 的实现符合了分布式事务的标准。那是否可以使用MySQL XA 让MySQL 具有分布式扩展的能力呢?在回答这个问题前,我们先看下MySQL XA 涉及到的相关概念。

相关概念介绍

事务:由一个有限的数据库操作序列构成,这些操作需要满足四个特性,即原子性、一致性、隔离性、持久性,简称ACID。

分布式事务:根据 Open Group 关于分布式事务的处理规范,定义了三种组件,如下图:

其中

AP

是指应用程序。

RM是资源管理器,事务的参与者,通常是数据库,比如MySQL Server。一个分布式事务通常涉及多个资源管理器。

TM是事务管理器,创建分布式事务并协调分布式事务中的各个子事务的执行和状态。子事务是指分布式事务中在RM上执行的具体操作。

两阶段提交 (Two-Phase Commit, 简称2PC) ,是为了使基于分布式系统架构下的所有节点在进行事务提交时保持一致性而设计的一种算法。分布式事务通常采用2PC,二阶段提交的算法思路可以概括为: 参与者将操作成败通知协调者,再由协调者根据所有参与者的反馈情报决定各参与者是否要提交操作还是中止操作,这里的参与者可以理解为RM,协调者可以理解为TM。如下图所示:

在第一阶段,TM会发送 Prepare 到所有参与分布式事务的RM询问是否可以提交操作,参与分布式事务的所有RM接收到请求,实现自身事务提交前的准备工作并返回结果。在第二阶段,根据RM返回的结果,如果涉及分布式事务的所有RM都返回可以提交,则TM给RM发送commit的命令,每个RM实现自己的提交,同时释放锁和资源,然后RM反馈提交成功,TM完成整个分布式事务;如果任何一个RM返回不能提交,则涉及分布式事务的所有RM都被告知需要回滚。MySQL XA 也是基于这个规范实现的,接下来我们介绍下MySQL XA。

MySQL XA 是什么?

MySQL XA 是基于Open Group 的<<Distributed Transaction Processing:The XA Specification>> 标准实现的,支持分布式事务,允许多个数据库实例参与一个全局的事务。MySQl XA 从MySQL 5.0 开始引入,仅innodb存储引擎支持MySQL XA事务。

MySQL XA 的命令集合如下:

XA START xid: 开启一个事务,并将事务置于ACTIVE状态,此后执行的SQL语句都将置于该是事务中。

XA END xid: 将事务置于IDLE状态,表示事务内的SQL操作完成。

XA PREPARE xid: 实现事务提交的准备工作,事务状态置于PREPARED状态。事务如果无法完成提交前的准备操作,该语句会执行失败。

XA COMMIT xid:  事务最终提交,完成持久化。

XA ROLLBACK xid: 事务回滚终止。

XA RECOVER: 查看MySQL中存在的PREPARED状态的xa事务。

下图是XA事务状态变迁图:

从分布式事务的变迁中可以看出,有两条路径可以使事务达到提交状态,有两条路径是回滚并结束事务。我们将这四条路径进行横向对比,看看每个阶段是如何实现分布式事务的ACID特性的(相关分析是在RR隔离级别下进行的,暂不考虑RC隔离级别)。

如上图可以看出,

1. 当xa start开启事务后,DML也会在对应的RM上创建undo以及read view(该read view是instance级别的)。

2. 当xa prepare 时会将子事务置于PREPARED状态,此时子事务已经完成事务提交前的所有准备工作(获得锁,并将PREPARED状态记录到共享表空间中,会将xa start到xa end之间操作记录在binlog中)。

3. 当xa commit 时会在binlog中记录xa commit xid, 并将innodb中PREPARED状态转化为COMMITED状态。

4. 当xa commit one phase 时会同时进行prepare和commit 两种操作,是在TM发现全局的分布式事务只涉及一个RM时进行的(因为不需要等待其他RM的反馈结果)。

5. 当xa rollback在xa prepare前时,因为没有写binlog和redo,只会释放undo, read view以及lock。

6. 当xa rollback 在xa prepare之后时,除了需要释放undo, read view以及lock,还需要binlog中记录xa rollback xid(使得从库不会提交该事务)以及innodb中将PREPARED状态转化为ROLLBACK状态。

MySQL XA 的例子

上面介绍了MySQL XA 的原理,我们现在举几个简单的例子。

例子1,两阶段XA事务提交:

mysql> xa start 'mysql57xa';

Query OK, 0 rows affected (0.00 sec)

mysql> insert into t1(id) values(1);

Query OK, 1 row affected (0.00 sec)

mysql> xa end 'mysql57xa';

Query OK, 0 rows affected (0.00 sec)

mysql> xa prepare 'mysql57xa';

Query OK, 0 rows affected (0.00 sec)

mysql> xa recover\G

formatID: 1

gtrid_length: 7

bqual_length: 0

data: mysql57

1 row in set (0.00 sec)

mysql> xa commit 'mysql57xa';

Query OK, 0 rows affected (0.00 sec)

对应的Binlog 中的记录如下:

例子2,xa commit one phase ,不需要等待其他RM反馈prepare的结果。

mysql> xa start 'mysql57xa';

Query OK, 0 rows affected (0.00 sec)

mysql> insert into t1(id) values(2);

Query OK, 1 row affected (0.00 sec)

mysql> xa end 'mysql57xa';

Query OK, 0 rows affected (0.00 sec)

mysql> xa commit 'mysql57xa' one phase;

Query OK, 0 rows affected (0.00 sec)

对应的Binlog 中的记录如下:

例子3,在xa prepare后,执行xa rollback 回滚事务。

mysql> xa start 'mysql57xa';

Query OK, 0 rows affected (0.00 sec)

mysql> insert into t1(id) values(3);

Query OK, 1 row affected (0.00 sec)

mysql> xa end 'mysql57xa';

Query OK, 0 rows affected (0.00 sec)

mysql> xa prepare 'mysql57xa';

Query OK, 0 rows affected (0.00 sec)

mysql> xa rollback 'mysql57xa';

Query OK, 0 rows affected (0.00 sec)

对应的Binlog 中的记录如下:

MySQL XA 的限制

在MySQL 5.7.7 之前,MySQL一直存在一个"bug"。在事务达到PREPARED状态后,客户端断开与MySQL的连接,MySQL 会自动回滚该事务,这个行为不符合分布式事务的规范,MySQL将PREPARED的事务丢失了。之所以MySQL这么实现是因为MySQL 5.7.7 之前PREPARED的事务并不会记录到binlog中。客户端退出后会丢失该信息,如果允许再提交,那么binlog缺少事务信息,会造成主从不一致。

在MySQL 5.7.7 之后,MySQL 新增了一个XA_prepare_log_event的事件,会把xa start到xa prepare中间的操作记录到Binlog中。Slave读取Relay log 进行回放,当SQL Thread读取到PREPARED的事务后,在读取xa commit或者xa rollback前,会进行一个类似客户端断开的操作,继续读取后续的事务信息,不会阻塞SQL Thread的执行。从以上的结果看,Oracle在MySQL 5.7.7 上确实完美的解决了MySQL XA一直存在的一个"bug"。

MySQL XA 的实践

本人曾在某公司的分布式数据库项目组中实践过基于MySQL XA的分布式事务。MySQL XA 要满足线上高并发的访问要求,在使用时还需要解决两个问题:分布式死锁问题和分布式读一致性问题。分布式死锁问题是指MySQL Server 是可以检测和解决单个MySQL实例中的死锁问题,但涉及到跨越多个MySQL 实例的分布式事务时候,需要程序层面实现死锁的检测和解决。分布式读一致性问题是指MySQL的read view 也是实例级别的,对于全局分布式事务来说无法实现读一致,只能通过select ... lock in share mode在读请求上加锁的串行化隔离级别来实现,这必然会带来并发性能的下降。这就需要在程序层面构建全局的read view来实现全局的MVCC 。当然这两个问题,当时团队的大牛们都已经解决了,我也很有幸参与其中。

本人水平有限,描述有误或是不准确的地方,请大家多多指教。

上一篇下一篇

猜你喜欢

热点阅读