前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >分布式事务实战---XA两阶段提交(2PC)方案详解

分布式事务实战---XA两阶段提交(2PC)方案详解

作者头像
JavaEdge
发布2022-11-30 15:39:53
4690
发布2022-11-30 15:39:53
举报
文章被收录于专栏:JavaEdgeJavaEdge

XA,2PC,two-phase commit protocol,两阶段事务提交采⽤的是 X/OPEN 组织定义的DTP 模型所抽象的:

  • AP 应用程序,Application Program,定义事务边界(即定义事务的开始和结束),并且在事务边界内对资源进行操作
  • TM(事务管理器) Transaction Manager,负责分配事务唯一标识,监控事务的执行进度,并负责事务的提交、回滚等
  • RM(资源管理器) Resource Manager,如数据库、文件系统等,并提供访问资源的方式
XA 接口

xa_start :负责开启或者恢复一个事务分支 xa_end: 负责取消当前线程与事务分支的关联 xa_prepare:询问 RM 是否准备好提交事务分支 xa_commit:通知 RM 提交事务分支 xa_rollback: 通知 RM 回滚事务分支 xa_recover : 需要恢复的 XA 事务

保证分布式事务的强⼀致性。其中 TM 与 RM 间采⽤ XA 协议进⾏双向通信。 XA 整体设计思路可概括为:在现有事务模型基础上微调扩展而实现的分布式事务。

与传统的本地事务相⽐,XA 事务增加了准备阶段,数据库除了被动接受提交指令外,还可以反向通知调⽤⽅事务是否可以被提交。TM 可以收集所有分⽀事务的准备结果,并于最后进⾏原⼦提交,以保证事务的强⼀致性

  • 两阶段提交模型

Java 通过定义 JTA 接口实现了 XA 模型

JTA 接口中的 ResourceManager 需要数据库⼚商提供 XA 驱动实现,TransactionManager 则需要事务管理器的⼚商实现,传统的事务管理器需要同应⽤服务器绑定,因此使⽤的成本很⾼。而嵌⼊式的事务管器可以以 jar 包的形式提供服务,同 Apache ShardingSphere集成后,可保证分⽚后跨库事务强⼀致性。通常,只有使⽤了事务管理器⼚商所提供的 XA 事务连接池,才能⽀持 XA 的事务。Apache ShardingSphere在整合 XA 事务时,采⽤分离 XA 事务管理和连接池管理的⽅式,做到对应⽤程序的零侵⼊。

MySQL 从5.0.3开始支持 InnoDB 引擎的 XA 分布式事务,MySQL Connector/J 从5.0.0版本开始支持 XA。

在 DTP 模型中,MySQL 属于资源管理器(RM)。分布式事务中存在多个 RM,由事务管理器 TM 来统一进行协调。

MySQL 的 XA
  • XA {START | BEGIN} xid [JOIN | RESUME] 开启XA事务,如果使用的是XA START而非XA BEGIN,那么不支持[JOIN | RESUME],xid是个唯一值,表示事务分支标 全局 + 分支 id
  • XA END xid [SUSPEND [FOR MIGRATE]] 结束一个xA事务,不支持[SUSPEND [FOR MIGRATE]]
  • XA PREPARE xid

准备提交

  • XA COMIT xid [ONE PHASE] 提交,如果使用了 ONE PHASE,贼表示使用一阶段提交。两阶段提交协议中,如果只有一个 RM 参与,那么可以优化为一阶段提交
  • XA ROLLBACK xid

回滚

  • XA recover[convert xid]

列出所有处于 prepare 阶段的 XA 事务

代码语言:javascript
复制
mysql> select * from t;
+----+------+------+
| id | c    | d    |
+----+------+------+
|  0 |    0 |    0 |
|  5 |    5 |    5 |
| 10 |   10 |   10 |
| 15 |   15 |   15 |
| 20 |   20 |   20 |
| 25 |   25 |   25 |
| 30 |   10 |   30 |
+----+------+------+
7 rows in set (0.01 sec)

mysql> xa start 'x01';
Query OK, 0 rows affected (0.00 sec)

mysql> insert into t values(1,2,3);
Query OK, 1 row affected (0.01 sec)

mysql> update t set id = 11 where id = 10;
Query OK, 1 row affected (0.01 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> xa end 'x01';
Query OK, 0 rows affected (0.00 sec)

mysql> xa prepare 'x01';
Query OK, 0 rows affected (0.00 sec)

mysql> xa commit 'x01';
Query OK, 0 rows affected (0.00 sec)

mysql> select * from t;
+----+------+------+
| id | c    | d    |
+----+------+------+
|  0 |    0 |    0 |
|  1 |    2 |    3 | # 添加的记录
|  5 |    5 |    5 |
| 11 |   10 |   10 | # 修改的记录
| 15 |   15 |   15 |
| 20 |   20 |   20 |
| 25 |   25 |   25 |
| 30 |   10 |   30 |
+----+------+------+
8 rows in set (0.01 sec)

mysql> xa start 'x01';
Query OK, 0 rows affected (0.00 sec)

mysql> insert into t values(2,5,8);
Query OK, 1 row affected (0.00 sec)

mysql> xa end 'x01';
Query OK, 0 rows affected (0.00 sec)

mysql> xa prepare 'x01';
Query OK, 0 rows affected (0.00 sec)

mysql> xa recover;
# gtr 全局事务 id
# bq 分支事务 id,这就能区分是大事务,还是小事务
+----------+--------------+--------------+------+
| formatID | gtrid_length | bqual_length | data |
+----------+--------------+--------------+------+
|        1 |            3 |            0 | x01  |
+----------+--------------+--------------+------+
1 row in set (0.00 sec)

mysql> xa start 'x01';
Query OK, 0 rows affected (0.00 sec)

mysql> insert into t values(2,5,8);
Query OK, 1 row affected (0.00 sec)

mysql> xa end 'x01';
Query OK, 0 rows affected (0.00 sec)

mysql> xa prepare 'x01';
Query OK, 0 rows affected (0.00 sec)

mysql> xa recover;
+----------+--------------+--------------+------+
| formatID | gtrid_length | bqual_length | data |
+----------+--------------+--------------+------+
|        1 |            3 |            0 | x01  |
+----------+--------------+--------------+------+
1 row in set (0.00 sec)

mysql> xa rollback 'x01';
Query OK, 0 rows affected (0.00 sec)

mysql> select * from t;
+----+------+------+
| id | c    | d    |
+----+------+------+
|  0 |    0 |    0 |
|  1 |    2 |    3 |
|  5 |    5 |    5 |
| 11 |   10 |   10 |
| 15 |   15 |   15 |
| 20 |   20 |   20 |
| 25 |   25 |   25 |
| 30 |   10 |   30 |
+----+------+------+
8 rows in set (0.00 sec)

实践分支事务 id

代码语言:javascript
复制
mysql> xa start 'x01', 'b01';
Query OK, 0 rows affected (0.00 sec)

mysql> select * from t;
+----+------+------+
| id | c    | d    |
+----+------+------+
|  0 |    0 |    0 |
|  1 |    2 |    3 |
|  5 |    5 |    5 |
| 11 |   10 |   10 |
| 15 |   15 |   15 |
| 20 |   20 |   20 |
| 25 |   25 |   25 |
| 30 |   10 |   30 |
+----+------+------+
8 rows in set (0.00 sec)

mysql> insert into t values(2,5,8);
Query OK, 1 row affected (0.00 sec)

mysql> select * from t;
+----+------+------+
| id | c    | d    |
+----+------+------+
|  0 |    0 |    0 |
|  1 |    2 |    3 |
|  2 |    5 |    8 |
|  5 |    5 |    5 |
| 11 |   10 |   10 |
| 15 |   15 |   15 |
| 20 |   20 |   20 |
| 25 |   25 |   25 |
| 30 |   10 |   30 |
+----+------+------+
9 rows in set (0.00 sec)

mysql> xa end 'x01', 'b01';
Query OK, 0 rows affected (0.00 sec)

mysql> xa prepare 'x01', 'b01';
Query OK, 0 rows affected (0.01 sec)

mysql> xa recover;
+----------+--------------+--------------+--------+
| formatID | gtrid_length  | bqual_length | data   |
+----------+--------------+--------------+--------+
|        1 |            3 |            3 | x01b01 |
+----------+--------------+--------------+--------+
1 row in set (0.01 sec)
# gtrid_length 全局事务 id 的长度
# bqual_length 分支事务 id 的长度

mysql> xa commit 'x01', 'b01';
Query OK, 0 rows affected (0.00 sec)

mysql> xa recover;
Empty set (0.00 sec)

mysql> select * from t;
+----+------+------+
| id | c    | d    |
+----+------+------+
|  0 |    0 |    0 |
|  1 |    2 |    3 |
|  2 |    5 |    8 |
|  5 |    5 |    5 |
| 11 |   10 |   10 |
| 15 |   15 |   15 |
| 20 |   20 |   20 |
| 25 |   25 |   25 |
| 30 |   10 |   30 |
+----+------+------+
9 rows in set (0.00 sec)
  • MySQL XA事务状态 ACTIVE状态的XA事务,我们可以执行构成事务的SQL语句,然后发布一个XA END语句。XA END把事务放入IDLE状态。

XA事务和非XA事务(即本地事务)是互斥的。 例如,已经执行了 XA START 开启一个XA事务,则本地事务不会被启动,直到XA事务已被提交或被回滚为止。相反的,若已使用START TRANSACTION启动一个本地事务,则XA语句不能被使用,直到该事务被提交或被回滚为止。

时序图
  • 完整的 XA 事务处理时序图
  • 单个 MySQL 的内部操作
XA 过程中,事务失败怎么办?

业务 SQL 执行过程,某个 RM 崩溃怎么处理,全部 prepare 后,某个 RM 崩溃怎么处理,commit 时,某个 RM 崩溃怎么办?

MySQL < 5.7的bug
  • 已经prepare的事务, 在客户端退出或者服务宕机的时候,2PC的事务 会被回滚。
  • 在服务器故障重启提交后, 相应的Binlog被丢失

MySQL 5.6版本在客户端退出的时候,自动把已经prepare的事务回滚了 ,那么MySQL为什么要这样做?这主要取决于MySQL的内部实现,MySQL 5.7以前的版本,对于prepare的事务,MySQL 是不会记录binlog的(官方说是减少fsync, 起到了优化的作用)。只有当分布式事务提交的时候才会把前面的操作写入binlog信息,所以对于binlog来说,分布式事务与普通的事务没有区别,而prepare以 前的操作信息都保存在连接的I0 CACHE中,如果这个时候客户端退出了,以前的binlog信息都会被丢失,再次重连后允许提交的话, 会造成Binlog丢失,从而造成主从数据的不一致,所以官方在客户端退出的时候直接把已经prepare的事务都回滚了!

MySQL > 5.7版本的优化

https://dev.mysql.com/worklog/task/?id=6860,MySQL对于分布式事务,在prepare的时候就完成了写Binlog的操作,通过新增一种叫XA _prepare_ log_ event的event类型来实现,这与以前版本的主要区别(以前版本prepare时不写Binlog)。

事务管理器,负责协调多个数据库(资源管理器)的事务。

  1. 事务管理器先问各个DB:预提交 ok 吗?
  2. 如果每个数据库都回复ok,即预提交成功,开始正式提交事务,在各DB开始执行操作,这里失败会有失败异常重试,日志分析,人工重试。

X/Open,即现在的open group,是一个独立的组织,主要负责制定各种行 业技术标准。X/Open组织主要由各大知名公司或厂商支持,这些组织不光遵循X/Open组织定义的行业技术标准,也参与到标准的制定。

故障分析

单点故障

协调者出错,事务会失败。

集群部署即可。

阻塞资源

占用数据库连接,性能低。

保持连接是为了第一阶段和第二阶段使用的是同一个事务,确保提交或回滚事务,只需将操作前、后的数据落库记录,就能将当前的连接放弃了。 将连接释放掉,就能保证系统的性能了。

提交阶段什么都不用做,因为数据库已有操作后记录。 回滚也可以,因为已经记录过操作前的记录。

数据不一致

二阶段出错,数据不一致

适用场景

适合单块应用中,跨多库的分布式事务,而且因其严重依赖DB层面解决事务,所以效率很低,不适合高并发场景。

互联网公司基本都不用,因某个系统内部如果出现跨多库的操作,是不合规的。现在的微服务,一个大的系统分成几十甚至上百个服务。一般规约每个服务只能操作自己对应的一个数据库。

如果你要操作别的服务对应的库,不允许直连别的服务的库。要操作别人的服务的库,必须通过调用别的服务的接口

主流的开源XA分布式事务解决方案

Element

ATOMIKOS

narayanna

seata

TM

去中心化设计,性能较高

去中心化设计,性能较高

中心化设计,性能较差,bug 多

日志存储件

只支持文件

文件、数据库

文件、数据库

扩展性

较好

一般

一般

事务恢复

只支持单机事务恢复

集群模式恢复

问题很多,未能正确恢复

XA

标准 XA 实现

标准 XA 实现

非标准的 XA 实现

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2021-02-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 故障分析
    • 单点故障
      • 阻塞资源
        • 数据不一致
        • 适用场景
        • 主流的开源XA分布式事务解决方案
        相关产品与服务
        云数据库 SQL Server
        腾讯云数据库 SQL Server (TencentDB for SQL Server)是业界最常用的商用数据库之一,对基于 Windows 架构的应用程序具有完美的支持。TencentDB for SQL Server 拥有微软正版授权,可持续为用户提供最新的功能,避免未授权使用软件的风险。具有即开即用、稳定可靠、安全运行、弹性扩缩等特点。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档