分布式事务

  • 2020-05-20
  • 浏览 (1267)

分布式事务简介

分布式事务是指会涉及到操作多个数据库(或者提供事务语义的系统,如JMS)的事务。其实就是将对同一数据库事务的概念扩大到了对多个数据库的事务。目的是为了保证分布式系统中事务操作的原子性。分布式事务处理的关键是必须有一种方法可以知道事务在任何地方所做的所有动作,提交或回滚事务的决定必须产生统一的结果(全部提交或全部回滚)。

分布式事务实现机制

事务包含原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)。

二阶段提交

  • 准备阶段又称投票阶段。在这一阶段,协调者询问所有参与者是否准备好提交,参与者如果已经准备好提交则回复Prepared,否则回复Non-Prepared。
  • 提交阶段又称执行阶段。协调者如果在上一阶段收到所有参与者回复的Prepared,则在此阶段向所有参与者发送commit指令,所有参与者立即执行commit操作;否则协调者向所有参与者发送rollback指令,参与者立即执行rollback操作。

两阶段提交前提条件

  • 网络通信是可信的。虽然网络并不可靠,但两阶段提交的主要目标并不是解决诸如拜占庭问题的网络问题。同时两阶段提交的主要网络通信危险期(In-doubt Time)在事务提交阶段,而该阶段非常短。
  • 所有crash的节点最终都会恢复,不会一直处于crash状态。
  • 每个分布式事务参与方都有WAL日志,并且该日志存于稳定的存储上。
  • 各节点上的本地事务状态即使碰到机器crash都可从WAL日志上恢复。

两阶段提交容错方式

两阶段提交中的异常主要分为如下三种情况

  • 协调者正常,参与方crash
  • 协调者crash,参与者正常
  • 协调者和参与方都crash

对于第一种情况,若参与方在准备阶段crash,则协调者收不到Prepared回复,协调方不会发送commit命令,事务不会真正提交。若参与方在提交阶段提交,当它恢复后可以通过从其它参与方或者协调方获取事务是否应该提交,并作出相应的响应。

第二种情况,可以通过选出新的协调者解决。

第三种情况,是两阶段提交无法完美解决的情况。尤其是当协调者发送出commit命令后,唯一收到commit命令的参与者也crash,此时其它参与方不能从协调者和已经crash的参与者那儿了解事务提交状态。但如同上一节两阶段提交前提条件所述,两阶段提交的前提条件之一是所有crash的节点最终都会恢复,所以当收到commit的参与方恢复后,其它节点可从它那里获取事务状态并作出相应操作。

二阶段提交的算法思路可以概括为:协调者询问参与者是否准备好了提交,并根据所有参与者的反馈情况决定向所有参与者发送commit或者rollback指令(协调者向所有参与者发送相同的指令)。

一致性、隔离性和持久性靠的是各分布式事务参与方自己原有的机制,而两阶段提交主要保证了分布式事务的原子性。

分布式事务如何保证原子性

在分布式系统中,各个节点(或者事务参与方)之间在物理上相互独立,通过网络进行协调。每个独立的节点(或组件)由于存在事务机制,可以保证其数据操作的ACID特性。但是,各节点之间由于相互独立,无法确切地知道其经节点中的事务执行情况,所以多节点之间很难保证ACID,尤其是原子性。

如果要实现分布式系统的原子性,则须保证所有节点的数据写操作,要不全部都执行(生效),要么全部都不执行(生效)。但是,一个节点在执行本地事务的时候无法知道其它机器的本地事务的执行结果,所以它就不知道本次事务到底应该commit还是 roolback。常规的解决办法是引入一个“协调者”的组件来统一调度所有分布式节点的执行。

XA规范

XA是由X/Open组织提出的分布式事务的规范。XA规范主要定义了(全局)事务管理器(Transaction Manager)和(局部)资源管理器(Resource Manager)之间的接口。XA接口是双向的系统接口,在事务管理器(Transaction Manager)以及一个或多个资源管理器(Resource Manager)之间形成通信桥梁。XA引入的事务管理器充当上文所述全局事务中的“协调者”角色。事务管理器控制着全局事务,管理事务生命周期,并协调资源。资源管理器负责控制和管理实际资源(如数据库或JMS队列)。目前,Oracle、Informix、DB2、Sybase和PostgreSQL等各主流数据库都提供了对XA的支持。

XA规范中,事务管理器主要通过以下的接口对资源管理器进行管理
xa_open,xa_close:建立和关闭与资源管理器的连接。
xa_start,xa_end:开始和结束一个本地事务。
xa_prepare,xa_commit,xa_rollback:预提交、提交和回滚一个本地事务。
xa_recover:回滚一个已进行预提交的事务。

JTA介绍

作为java平台上事务规范JTA(Java Transaction API)也定义了对XA事务的支持,实际上,JTA是基于XA架构上建模的。在JTA 中,事务管理器抽象为javax.transaction.TransactionManager接口,并通过底层事务服务(即Java Transaction Service)实现。像很多其他的Java规范一样,JTA仅仅定义了接口,具体的实现则是由供应商(如J2EE厂商)负责提供,目前JTA的实现主要有以下几种:

J2EE容器所提供的JTA实现(如JBoss)。
独立的JTA实现:如JOTM(Java Open Transaction Manager),Atomikos。这些实现可以应用在那些不使用J2EE应用服务器的环境里用以提供分布事事务保证。

greenplum 不支持XA

http://gpdb.docs.pivotal.io/590/ref_guide/feature_summary.html 有一段话:
The following features of SQL 1999 are not supported in Greenplum Database:
...
Prepared transactions (PREPARE TRANSACTION, COMMIT PREPARED, ROLLBACK PREPARED). This also means Greenplum does not support XA Transactions (2 phase commit coordination of database transactions with external transactions).

所以使用springboot + Atomikos集成分布式事务,会报异常:
PREPARE TRANSACTION is not yet supported in Greenplum Database

不使用分布式事务的解决方案

当一个业务使用到多个数据源处理业务时,应该将不同的数据源操作写到不同类的方法中,然后嵌套调用。

如一个业务既用到mysql又用到gp,

则在A类中写个方法method0,声明事务为:@Transactional(transactionManager = "mysqlTransactionManager", rollbackFor = Exception.class),处理mysql的业务,

在B类中写个method1,声明事务为:@Transactional(transactionManager = "greenplumTransactionManager", rollbackFor = Exception.class),处理gp的业务,

A的method0调用B的method1来完成整个业务。

调用完后最好不要有其他业务操作,以免这时发生异常导致A的method0事务回滚,而此时B的method1不会发生回滚。

参考资料

分布式事务(一)两阶段提交及JTA
Spring Boot之分布式事务(JTA、Atomikos、Druid、Mybatis)
Spring事务管理嵌套事务详解

0  赞