XA的意思为“eXtended Architecture”,是The Open Group组织为分布式事务处理创建的标准。虽然MySQL 5.0是第一个支持XA的版本,但MySQL 5.7提升了XA支持的可靠性,修复了许多错误,并增加了整体测试用例覆盖率。
XA解决了在分布式资源集中的单个事务中保留ACID属性的问题。资源本身可以是其他MySQL服务器,甚至是不同的数据库技术。XA的标准描述了全局事务管理器和本地资源管理器之间的交互。
正如在上面提到的,MySQL 5.0引入了XA支持,从而增加了参与全局事务的能力。XA需要一个资源管理器,它提供了对事务资源的访问,还需要一个事务管理器来协调全局事务中的事务。MySQL的XA实现使MySQL服务器能够充当资源管理器,而连接到MySQL服务器的客户端则执行事务管理器的任务。
XA使用两阶段提交协议,其中第一阶段是提交请求,然后是实际提交。一旦全局事务的各个分支完成执行,两阶段提交协议就会启动:
事务管理器与多个资源管理器交互以处理全局事务中的各个事务/分支。该图描绘了涉及一个资源管理器的XA事务。XA事务的语句以XA关键字,要执行的操作和唯一标识符开头。在下面的示例中,字符串’xatest’表示全局事务标识符。除了全局事务标识符之外,还可以为XA事务指定分支标识符和格式ID。分支标识符用于标识本地事务,格式ID指定前两个组件使用的格式。
让我们看一下上述XA事务的状态转换。
XA START将事务置于ACTIVE状态。一旦所有语句都由活动事务执行,就会发出XA_END语句,使事务处于IDLE状态。
对于IDLE事务,可以发出XA PREPARE或XA COMMIT ONE PHASE。
XA PREPARE将事务置于PREPARED状态。而XA COMMIT ONE PHASE准备并提交交易。
对于PREPARED XA事务,发出XA COMMIT以提交结束事务。
在5.7.7之前,如果客户端连接已终止或服务器正常退出,则回滚PREPARED事务。当客户端被Kill时,所有事务都被回滚。因此,即使XA事务处于PREPARED状态,它也无法在XA RECOVER期间恢复事务。理想情况下,当事务是PREPARED时,应该可以COMMIT或ROLLBACK事务。对于这种情况,可参看下列示例:
mysql> CREATE TABLE t1(fld1 INT);
Query OK, 0 rows affected (0.01 sec)
mysql> COMMIT;
Query OK, 0 rows affected (0.00 sec)
mysql> XA START 'test';
Query OK, 0 rows affected (0.00 sec)
mysql> INSERT INTO t1 VALUES (1);
Query OK, 1 row affected (0.00 sec)
mysql> XA END 'test';
Query OK, 0 rows affected (0.00 sec)
mysql> XA PREPARE 'test';
Query OK, 0 rows affected (0.00 sec)
mysql> Killed
Now start another client session.
mysql> XA COMMIT 'test';
ERROR 1397 (XAE04): XAER_NOTA: Unknown XID
mysql> XA RECOVER;
Empty set (0.00 sec)
同样在5.7.7之前,如果XA事务处于PREPARED状态并且服务器异常退出,则可以在重新启动服务器后恢复事务 - 但是它没有被复制。 服务器重启后,XA事务仍然存在于PREPARED状态,但内容无法记录在二进制日志中。因此,二进制日志不同步导致数据漂移。因此,XA无法安全地用于复制。
ql> CREATE TABLE t1(fld1 INT);
Query OK, 0 rows affected (0.01 sec)
mysql> COMMIT;
Query OK, 0 rows affected (0.00 sec)
mysql> XA START 'test';
Query OK, 0 rows affected (0.00 sec)
mysql> INSERT INTO t1 VALUES (1);
Query OK, 1 row affected (0.00 sec)
mysql> XA END 'test';
Query OK, 0 rows affected (0.00 sec)
mysql> XA PREPARE 'test';
Query OK, 0 rows affected (0.00 sec)
Now kill the server.
mysql> XA RECOVER;
ERROR 2006 (HY000): MySQL server has gone away
No connection. Trying to reconnect...
Connection id: 1
Current database: test
+----------+--------------+--------------+------+
| formatID | gtrid_length | bqual_length | data |
+----------+--------------+--------------+------+
| 1 | 4 | 0 | test |
+----------+--------------+--------------+------+
1 row in set (0.02 sec)
mysql> XA COMMIT 'test';
Query OK, 0 rows affected (0.02 sec)
mysql> SHOW BINLOG EVENS\G;
*************************** 1. row ***************************
Log_name: nisha-PORTEGE-Z30-A-bin.000001
Pos: 4
Event_type: Format_desc
Server_id: 1
End_log_pos: 120
Info: Server ver: 5.6.29-debug-log, Binlog ver: 4
1 row in set (0.00 sec)
mysql> SELECT * FROM t1;
+------+
| fld1 |
+------+
| 1 |
+------+
1 row in set (0.00 sec)
克服上述限制需要更改XA事务恢复机制和二进制日志记录机制。在5.7.7中进行了这种改进。
让我们看看5.7.7之后的上述例子的输出:
客户端断开连接后:
l> CREATE TABLE t1(fld1 INT);
Query OK, 0 rows affected (0.01 sec)
mysql> COMMIT;
Query OK, 0 rows affected (0.00 sec)
mysql> XA START 'test';
Query OK, 0 rows affected (0.00 sec)
mysql> INSERT INTO t1 VALUES (1);
Query OK, 1 row affected (0.00 sec)
mysql> XA END 'test';
Query OK, 0 rows affected (0.00 sec)
mysql> XA PREPARE 'test';
Query OK, 0 rows affected (0.00 sec)
mysql> Killed
Now start another client session.
mysql> XA RECOVER;
+----------+--------------+--------------+------+
| formatID | gtrid_length | bqual_length | data |
+----------+--------------+--------------+------+
| 1 | 4 | 0 | test |
+----------+--------------+--------------+------+
1 row in set (0.00 sec)
mysql> XA COMMIT 'test';
Query OK, 0 rows affected (0.02 sec)
服务器重启后:
ql> CREATE TABLE t1(fld1 INT);
Query OK, 0 rows affected (0.01 sec)
mysql> COMMIT;
Query OK, 0 rows affected (0.00 sec)
mysql> XA START 'test';
Query OK, 0 rows affected (0.00 sec)
mysql> INSERT INTO t1 VALUES (1);
Query OK, 1 row affected (0.00 sec)
mysql> XA END 'test';
Query OK, 0 rows affected (0.00 sec)
mysql> XA PREPARE 'test';
Query OK, 0 rows affected (0.00 sec)
Now kill the server.
mysql> XA RECOVER;
ERROR 2006 (HY000): MySQL server has gone away
No connection. Trying to reconnect...
Connection id: 1
Current database: test
+----------+--------------+--------------+------+
| formatID | gtrid_length | bqual_length | data |
+----------+--------------+--------------+------+
| 1 | 4 | 0 | test |
+----------+--------------+--------------+------+
1 row in set (0.02 sec)
mysql> XA COMMIT 'test';
Query OK, 0 rows affected (0.02 sec)
mysql> SHOW BINLOG events\G;
*************************** 3. row ***************************
Log_name: nisha-PORTEGE-Z30-A-bin.000001
Pos: 154
Event_type: Anonymous_Gtid
Server_id: 0
End_log_pos: 219
Info: SET @@SESSION.GTID_NEXT= 'ANONYMOUS'
*************************** 4. row ***************************
Log_name: nisha-PORTEGE-Z30-A-bin.000001
Pos: 219
Event_type: Query
Server_id: 0
End_log_pos: 319
Info: XA START X'74657374',X'',1
*************************** 5. row ***************************
Log_name: nisha-PORTEGE-Z30-A-bin.000001
Pos: 319
Event_type: Query
Server_id: 0
End_log_pos: 418
Info: use `test`; INSERT INTO t1 VALUES (1)
*************************** 6. row ***************************
Log_name: nisha-PORTEGE-Z30-A-bin.000001
Pos: 418
Event_type: Query
Server_id: 0
End_log_pos: 509
Info: XA END X'74657374',X'',1
*************************** 7. row ***************************
Log_name: nisha-PORTEGE-Z30-A-bin.000001
Pos: 509
Event_type: XA_prepare
Server_id: 0
End_log_pos: 549
Info: XA PREPARE X'74657374',X'',1
*************************** 8. row ***************************
Log_name: nisha-PORTEGE-Z30-A-bin.000002
Pos: 219
Event_type: Query
Server_id: 0
End_log_pos: 313
Info: XA COMMIT X'74657374',X'',1
8 rows in set (0.00 sec)
MySQL 5.7中极大地改进了对XA的支持,并使实现XA JOIN / XA RESUME操作成为可能。期待看到它在分布式系统中的使用。