一、DTP模型
下图说明了一个DTP系统的本地实例,其中AP调用TM来构造事务。这些框表示X/Open DTP模型中的软件组件。箭头指示控制流的方向。
图中(3)标识的接口就是XA接口,即TM和RMs相互作用的接口(双向接口)。XA接口不是一个普通的应用程序编程接口(API)。它是DTP软件组件之间的系统级接口。X/Open正在开发其他DTP接口,供应用程序直接使用。
XA是由X/Open组织提出的分布式事务的规范。X/Open是一个独立的、全球性的开放系统组织,由世界上最大的信息系统供应商、用户组织和软件公司提供支持。它的使命是通过开放系统的实际实现,从计算中为用户带来更大的价值。
X/Open DTP(Distributed Transaction Process)是一个分布式事务模型。这个模型主要使用了两段提交(2PC - Two-Phase-Commit)来保证分布式事务的完整性。在这个模型里面,有三个角色
AP(An application Program),应用程序。也就是业务层,定义事务边界并指定构成事务的操作。直白一点,哪些操作属于一个事务,就是AP定义的。
RM(Resource Manager),资源管理器。一般是数据库,也可以是其他的资源管理器,如消息队列(如JMS数据源),文件系统等。提供对共享资源的访问。
TM(Transaction Manager),事务管理器。接收AP的事务请求,对全局事务进行管理,管理事务分支状态,协调RM的处理,通知RM哪些操作属于哪些全局事务以及事务分支等等。这个也是整个事务调度模型的核心部分。
文章开头的图只是一个最简单的模型,用于说明原理。实际系统往往会由多个简单的模型组合起来形成一个TM域。下图的域包括2个简单模型。
图中红框的角色
CRM(Communication Resource Manager),CRM允许模型的一个实例访问当前TM域内部或外部的另一个实例。
二、两阶段提交
所有关于分布式事务的介绍中都必然会讲到两阶段提交,它是实现XA分布式事务的关键(确切地说:两阶段提交主要保证了分布式事务的原子性,即所有结点要么全做要么全不做)。两个阶段是指准备阶段和提交阶段。
一个描述两阶段提交很好的类比是典型的结婚仪式,每个参与者(结婚典礼中的新郎和新娘)都必须服从安排,在正式步入婚姻生活之前说“我愿意”。存在一种悲剧情形,“参与者”之一在做出承诺前的最后一刻反悔。两阶段提交也类似,如果有一个参与者不确认成功,整个事务就失败了。
当客户端向事务管理器(TM)发出commit()请求,事务管理器开始两阶段提交过程。在第一阶段,所有的资源被轮询到,问它们是否准备好了提交作业。每个参与者可能回答“准备好(READY)”,“只读(READ_ONLY)”,或“未准备好(NOT_READY)”。如果有任意一个参与者在第一阶段响应“未准备好(NOT_READY)”,则整个事务回滚。如果所有参与者都回答“准备好(READY)”,那这些资源就在第二阶段提交。回答“只读(READ_ONLY)”的资源,则在协议的第二阶段处理中被排除掉。
下图展示了两阶段提交的基本流程
三、一般编程方式
1、配置TM,通过TM或者RM提供的方式,把RM注册到TM。可以理解为给TM注册RM作为数据源。一个TM可以注册多个RM。
2、AP从TM获取资源管理器的代理(例如:使用JTA接口,从TM管理的上下文中,获取出这个TM所管理的RM的JDBC连接或JMS连接)
3、AP向TM发起一个全局事务。这时,TM会通知各个RM。XID(全局事务ID)会通知到各个RM。
4、AP通过2中获取的连接,直接操作RM进行业务操作。这时,AP在每次操作时把XID(包括所属分支的信息)传递给RM,RM正是通过这个XID与3步中的XID关联来知道操作和事务的关系的。
5、AP结束全局事务。此时TM会通知RM全局事务结束。
6、开始二段提交,也就是prepare - commit的过程。
上边几点更多停留在方法论的层面,我基于springboot做了个实验。
1、引入jta+atomikos依赖。jta是java对XA接口的实现,atomikos是一个事务管理器(atomikos也是最早做商用分布式事务产品的公司)
2、配置多数据源。一个是本地安装的mysql,另一个采用公司的测试数据库
3、编写数据源配置代码
4、剩下的工作就跟springboot的写本地事务完全一样,用好@Transactional标签就好。spring会帮你搞定剩下的一切功能!
很简单就搞定了不同数据库之间的事务问题。分布式事务是不是就是这么简单呢?显然不是
四、XA的局限性
目前,Oracle、Informix、DB2和Sybase等各大数据库厂家都提供对XA的支持,很幸运,MySQL 5.0以后的版本也支持。是不是可以放心使用MySQL的XA功能呢?我咨询了公司的DBA。
DBA给我推荐了一篇他整理的文章
http://48fc26d1.wiz03.com/wapp/pages/view/share/s/18_2rh2TVh7v2YT4pP1INFGM1B-ldX2v2Ajd2-5SPz0niqbR
文章里提到了MySQL的XA功能存在的问题
1、阻塞问题
两阶段提交执行过程中,所有的参与者都需要听从协调者的统一调度,期间处于阻塞状态而不能从事其他操作,这样效率及其低下。
2、单点故障
由于协调者的重要性,一旦协调者C发生故障。参与者si会一直阻塞下去。尤其在第二阶段,协调者发生故障,那么所有的参与者还都处于锁定事务资源的状态中,而无法继续完成事务操作。(如果是协调者挂掉,可以重新选举一个协调者,但是无法解决因为协调者宕机导致的参与者处于阻塞状态的问题),二阶段提交的缺陷在于如果C崩溃,所有Si可能都需要等待C,从而产生阻塞。
3、数据不一致
在二阶段提交的阶段二中,当协调者向参与者发送commit请求之后,发生了局部网络异常或者在发送commit请求过程中协调者发生了故障,这会导致只有一部分参与者接受到了commit请求。而在这部分参与者接到commit请求之后就会执行commit操作。但是其他部分未接到commit请求的机器则无法执行事务提交。于是整个分布式系统便出现了数据不一致性的现象。--解决方式是
set global tx_isolation='serializable',session tx_isolation='serializable';
此外文章还详细记录了MySQL 外部XA崩溃恢复的过程,一些Demo代码以及更细节的原理分析等,推荐阅读。
除了以上3点,现在系统都采用分布式架构,一个分布式事务很多时候不是亲自去操作另一个数据库,而是去调用其他团队的服务。其他团队的服务很可能不支持XA协议!
最后,支持XA协议的不只是数据库,看看下图的Message Broker