MySQL 5.7中对XA支持的改进

介绍

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使用两阶段提交协议,其中第一阶段是提交请求,然后是实际提交。一旦全局事务的各个分支完成执行,两阶段提交协议就会启动:

  1. 在第一阶段,事务管理器发布准备提交消息给全局事务中涉及的所有分支。在资源管理器确认它已准备好提交之前,它会将操作结果记录在日志中,以便在第二阶段执行实际提交。
  2. 在第二阶段,事务管理器如果收到来自所有相关分支的肯定响应,则通知他们提交。但是,如果任何分支机构以失败方式回复,则会通知所有分支回滚。

事务管理器与多个资源管理器交互以处理全局事务中的各个事务/分支。该图描绘了涉及一个资源管理器的XA事务。XA事务的语句以XA关键字,要执行的操作和唯一标识符开头。在下面的示例中,字符串’xatest’表示全局事务标识符。除了全局事务标识符之外,还可以为XA事务指定分支标识符和格式ID。分支标识符用于标识本地事务,格式ID指定前两个组件使用的格式。

MySQL 5.7中对XA支持的改进_第1张图片

  • XA START / BEGIN 启动一个事务并定义其全局事务标识符。
  • XA END 指定活动事务的结束。
  • XA PREPARE 准备事务以进行提交。
  • XA COMMIT [ONE PHASE] 提交并终止PREPARED的事务。
  • 如果是ONE PHASE选项,则在单步结束事务中执行准备和提交。
  • XA ROLLBACK 回滚并终止事务。
  • XA RECOVER 显示有关所有PREPARED事务的信息。

让我们看一下上述XA事务的状态转换。

MySQL 5.7中对XA支持的改进_第2张图片

XA START将事务置于ACTIVE状态。一旦所有语句都由活动事务执行,就会发出XA_END语句,使事务处于IDLE状态。

对于IDLE事务,可以发出XA PREPAREXA 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中进行了这种改进。

  • XA恢复机制已经扩展,当连接终止时,PREPARED XA事务将保留在事务高速缓存中,并在InnoDB中进行特殊标记。这允许客户端恢复PREPARED XA事务,然后是COMMIT / ROLLBACK。
  • XA事务现在使用两个不同的GTID分两个阶段进行二进制记录,这两个GTID允许事务交错。在第一阶段,当发布XA PREPARE时,直到该点的事务被记录在二进制日志中并且可以通过XA_prepare_log_event. 在第二阶段识别在发出XA COMMIT / ROLLBACK时,事务的第二部分被写入二进制日志。由于XA PREPARE是持久的,因此XA事务不会回滚,并且在服务器重新启动或客户端断开连接后仍然存在。客户端可以执行XA COMMIT / ROLLBACK,二进制日志保持最新。当GTID为ON且二进制日志关闭时,XA事务也可以正常工作。

让我们看看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操作成为可能。期待看到它在分布式系统中的使用。

你可能感兴趣的:(program)