分布式事务之X/Open DTP模型
为什么需要分布式事务
之前文章讲过,所谓的事务通常指的是数据库事务,也即由数据库来完成事务提交和回滚。
比如在同一个数据库下完成一次转账操作,从账户A中转帐100¥到账账户B中,也就是需要从账户X中扣除100¥,从账户Y中新增100¥。如图:

数据库操作如下:
begin
update account set x = x -100 where x = 1;
update account set y = y + 100 where y = 2;
commit or rollback
但是真实场景下,用户数据不可能存在在一个数据库下,那么如何保证跨数据库、跨服务下的事务呢?
分布式事务应用场景
分布式事务主要应用于三大场景:跨数据库、跨服务以及混合分布式事务。
跨数据库分布式事务
当业务规模增大,单库单表无法满足业务需求时,自然就会出现分库分表的情况。但是,单机事务又不能保证分库后的事务属性,分布式事务几乎无法避免。分布式事务可以让应用轻松具备跨库分布式事务处理能力,像使用单机数据库事务一样,透明地使用分布式事务
跨服务的分布式事务
在基于 SOA(Service-Oriented Architecture,面向服务架构)的分布式应用环境下,系统按照功能解耦,拆分成不同的微服务,一项业务往往会涉及多个服务之间的调用。因此,为了保障业务完整,需要保证各调用服务间的数据一致性。分布式事务同样支持跨服务的分布式事务,并且可以很方便的与 SOFABoot、Spring Cloud、Dubbo 等框架打通,实现业务链路级别的分布式事务。
混合分布式事务
在一个大规模的分布式应用环境下,除了常见的微服务、数据库资源之外,还会涉及到消息队列、缓存等系统资源的使用。同时,依然需要保证这些资源间访问的数据的一致性。分布式事务支持在同一个分布式事务中引入数据库、服务、消息队列等不同类型的资源,并且支持混合接入模式。
什么是X/Open DTP模型
X/Open
是一个组织,由多个公司在1984年联合创建的一个用于定义和推进信息技术领域开放标准的公司。而DTP
全称Distributed Transaction Process
,是上述组织针对分布式事务提出的一个模型。该模型如下所示:
上面模型主要涉及3个模块:
-
AP(Aplication Program): 应用程序
-
指事务的发起者,比如数据库客户端或者访问数据库的程序
-
对数据库的访问,执行事务的开启、提交、中止操作
-
-
RM(Resource Manager): 资源管理器
-
包含多个数据库资源
-
管理共享资源,并提供访问接口供外部程序调用
-
具有事务提交和回滚能力
-
-
TM(Transaction Manager): 事务管理器
- 分布式事务协调者
- 与每个RM进行通信,协调并完成事务处理
它们三者之间关系:
- AP使用多个RM,通过TM的事务接口(TX interface)定义需要执行的事务操作
- AP也可以通过RM的本地事务接口来操作单个RM
- TM和RM之间基于XA规范通信,执行二阶段提交协议
基于XA规范的二阶段提交协议
上图时序图,详细流程如下:
- AP(应用程序)调用
tx_open()
通知TM(事务管理器)发起全局事务 - TM调用
ax_open()
与RM建立session(会话) - AP调用
tx_begin()
通知TM开启事务 - TM调用
xa_start()
标记事务分支(Transaction bransh)开启 - AP调用RM,并定义具体事务分支的操作(CURD),比如更新一条数据或者插入一条数据
- TM调用
xa_end()
标记事务分支(Transaction bransh)结束 - AP调用
tx_commit()
通知TM提交事务 - TM 调用 xa_prepare() 通知 RM 做好事务分支提交的准备工作,比如锁定相关资源,也就是执行二阶段提交协议的提交请求阶段
- TM 调用 xa_commit() 通知 RM 提交事务分支(xa_rollback() 通知 RM 回滚事务),也就是执行二阶段提交协议的提交执行阶段
- TM 调用 xa_close() 关闭与 RM 的会话
从流程中可以看到,事务的原子性提交操作是通过两阶段提交协议(2PC)来完成。
-
第一阶段
- 事务管理器通过XA协议,向资源管理器发送prepare命令,询问各个资源管理器预提交是否成功
- 每个资源管理器,回复事务管理器成功或者失败
-
第二阶段
-
根据第一阶段资源管理器回复情况。如果全部回复预提交成功,则事务管理器向所有资源管理器发送最终提交commit命令
-
如果有任一个资源管理器回复失败,则事务管理器向所有资源管理器发送回滚rollback命令
-
如何通过MySQL XA实现分布式事务
首先说明,MySQL在XA事务中扮演的是资源管理器角色,而不是事务管理器角色。
在Mysql中存在两种XA事务:
-
内部XA事务主要用来协调存储引擎和二进制日志,
-
外部事务可以参与到外部分布式事务中(比如多个数据库实现的分布式事务)
这里我们主要讨论外部事务,首先,让我们看下mysql XA事务语法:
# 在mysql实例中开启一个XA事务,指定一个全局唯一标识;
mysql> XA START 'any_unique_id';
# XA事务的操作结束;
mysql> XA END 'any_unique_id';
# 告知mysql准备提交这个xa事务;
mysql> XA PREPARE 'any_unique_id';
# 告知mysql提交这个xa事务;
mysql> XA COMMIT 'any_unique_id';
# 告知mysql回滚这个xa事务;
mysql> XA ROLLBACK 'any_unique_id';
# 查看本机mysql目前有哪些xa事务处于prepare状态;
mysql> XA RECOVER;
下面让我们通过mysql命令行,在单节点上演示下事务的执行过程:
-
假设数据库中有数据表user
-
开启事务,对数据库执行新增操作
-
提交事务/回滚事务
TIP: 在单节点上演示XA事务,是没什么意义的。需要在多个数据库结合事务管理器进行演示。
其他注意事项
-
XA事务和本地事务是互斥的
mysql> xa start '1'; Query OK, 0 rows affected (0.00 sec) mysql> begin; ERROR 1399 (XAE07): XAER_RMFAIL: The command cannot be executed when global transaction is in the ACTIVE state mysql> lock table test1 read; ERROR 1399 (XAE07): XAER_RMFAIL: The command cannot be executed when global transaction is in the ACTIVE state
-
开启本地事务无法使用XA事务
mysql> begin; Query OK, 0 rows affected (0.00 sec) mysql> xa start '1'; ERROR 1400 (XAE09): XAER_OUTSIDE: Some work is done outside global transaction
-
xa start之后必须xa end,否则不能执行xa commit和xa rollback
mysql> xa start '1'; Query OK, 0 rows affected (0.00 sec) mysql> xa rollback '1'; ERROR 1399 (XAE07): XAER_RMFAIL: The command cannot be executed when global transaction is in the ACTIVE state mysql> xa end '1'; Query OK, 0 rows affected (0.00 sec) mysql> xa rollback '1'; Query OK, 0 rows affected (0.00 sec)
内容小结
通过本章学习,我们大致了解了分布式事务原理,以及分布式事务的一些特点:
-
XA 规范是个标准的规范,也就是说,无论是否是相同的数据库,只要这些数据库(比如 MySQL、Oracle、SQL Server)支持 XA 规范,那么它们就能实现分布式事务,也就是能保证全局事务的一致性
-
两阶段提交协议是阻塞式协议,所以事务管理器必须等待每一个资源管理器发送回复之后,才能进行下一步操作。一旦资源管理器挂掉,事务管理器则收不到响应,就会造成事务管理器一直等待。这时候就必须引入超时机制,一旦超过某个时间还没有回复,则认为失败,回滚所有操作,这些都是非常耗性能的。
-
一旦事务管理器挂掉,则资源管理器则一直等待事务管理器的命令。这时候可能就需要使用备份的事务管理器来接替原来的事务管理器的工作