分布式事务解决方案总结

一、传统架构跨库事务解决方案

名词解释

要了解XA协议,必须先了解X/Open DTP模型

X/Open DTP事务模型

X/Open DTP(Distributed Transaction Process)是X/Open 组织定义的一个分布式事务模型。这个模型主要使用了两段提交(2PC,Two-Phase-Commit)来保证分布式事务的完整性。在这个模型里面,有三个角色:

  • AP: Application,应用程序。也就是业务层。哪些操作属于一个事务,就是AP定义的。
  • TM: Transaction Manager,事务管理器。接收AP的事务请求,对全局事务进行管理,管理事务分支状态,协调RM的处理,通知RM哪些操作属于哪些全局事务以及事务分支等等。这个也是整个事务调度模型的核心部分。
  • RM:Resource Manager,资源管理器。一般是数据库,也可以是其他的资源管理器,如消息队列(如JMS数据源),文件系统等。

X/Open DTP这套模型定义了分布式事务的规范和 API 接口,由各个厂商进行具体的实现。后来J2EE遵循了 X/OpenDTP 规范,设计并实现了 java里的分布式事务编程接口规范-JTA。

XA协议

XA协议由Tuxedo首先提出的,并交给X/Open组织,作为资源管理器(数据库)与事务管理器的接口标准.

二阶段提交协议最早是用来实现数据库的分布式事务的,不过现在最常用的协议是 XA 协议。这个协议是 X/Open 组织基于二阶段提交协议提出的。

XA是一个规范或是一个事务的协议。XA规范定义了:
  • 1. TransactionManager : 这个TransactionManager可以通过管理多个ResourceManager来管理多个Resouce,也就是管理多个数据源
  • 2. XAResource : 针对数据资源封装的一个接口
  • 3. 两段式提交 : 多数据源事务提交的机制

JTA规范

JTA(Java Transaction Manager) ,JTA规范是XA规范的Java版。
  • 1. TransactionManager : 常用方法,可以开启,回滚,获取事务. begin(),rollback()...
  • 2. XAResouce : 资源管理,通过Session来进行事务管理,commit(xid)...
  • 3. XID : 每一个事务都分配一个特定的XID
JTA定义了一套接口,其中约定了几种主要的角色:TransactionManager、UserTransaction、Transaction、XAResource,并定义了这些角色之间需要遵守的规范,如Transaction的委托给TransactionManager等。

JTS

JTA与JTS的关系是什么?

上面提到事务管理器要和资源管理器要进行事务上下文传播的交互,其中应用服务器和事务管理器之间也有传播事务上下文的交互,有时候事务客户和应用服务器也需要传播事务上下文,众所周知,只要涉及到软件交互往往都会有一套规范。那么如何来传递这种事务上下文呢?这就是JTS了。JTS也定义了一套规范,它约定了各个程序角色之间如何传递事务上下文,它源自CORBA 的OTS规范,基于IIOP(一种软件交互协议)。不要认为JTS是JTA的实现,JTA其实就定义了一个空架子,告诉JTA的实现者应该怎样做怎样做,但是具体到做的时候JTS就来插一手了。因为JTA约定的这些角色要进行事务上下文的交互啊,JTS约定了应该怎样去进行交互。

几者的关系

下图是我对这几者关系的理解。

基于XA协议的两阶段提交方案(2PC,强一致性)

XA是一个分布式事务协议,由Tuxedo提出。XA中大致分为两部分:事务管理器(TM)和本地资源管理器(RM)。

分布式事务通常使用两阶段提交协议。两阶段提交协议把一个分布式事务的事务管理分为两类:一个是协调者,所有其它的是参与者。协调者负责做出该事务是提交还是撤销(Abort)的最后决定。所有参与者负责管理相应子事务的执行及在各自局部数据库上执行写操作。

Mysql官方文档关于XA事务的介绍:XA TransactionsRestrictions on XA Transactions

2PC的过程

2pc存在的问题

①.数据一致性问题

②.同步阻塞 

  • 性能(阻塞性协议,增加响应时间、锁时间、死锁);
  • 数据库支持完善度(MySQL5.7 之前都有缺陷);
  • 协调者依赖独立的 J2EE 中间件(早期重量级 Weblogic、Jboss、后期轻量级 Atomikos、Narayana 和 Bitronix);
  • 运维复杂;
  • 并不是所有资源都支持 XA 协议;

二阶段提交算法的最大缺点就在于它的执行过程中间,节点都处于阻塞状态。即节点之间在等待对方的响应消息时,它将什么也做不了。特别是,当一个节点在已经占有了某项资源的情况下,为了等待其他节点的响应消息而陷入阻塞状态时,当第三个节点尝试访问该节点占有的资源时,这个节点也将连带陷入阻塞状态。

另外,协调者节点指示参与者节点进行提交等操作时,如有参与者节点出现了崩溃等情况而导致协调者始终无法获取所有参与者的响应信息,这时协调者将只能依赖协调者自身的超时机制来生效。但往往超时机制生效时,协调者都会指示参与者进行回滚操作。这样的策略显得比较保守。

3PC

分为三个阶段:canCommit、preCommit、 doCommit。无论是二阶段提交还是三阶段提交都无法彻底解决分布式的一致性问题。

虽然 3PC 使用了超时机制,解决了协调者故障后参与者的阻塞问题,但与此同时却多了一次网络通信,性能上反而变得更差,也不太推荐。

适用场景

2PC方案的分布式事务,适合于在单体应用里跨多个库的事务。因为强依赖于数据库层面来实现,所以效率低,不太适合高并发的场景。

在分布式系统中,尤其是微服务系统,不允许直连某个服务的数据库,而是需要通过http接口调用的方式。

基于JTA规范的事务框架

像很多其他的java规范一样,JTA仅仅定义了接口,具体的实现则是由供应商(如J2EE厂商)负责提供,目前JTA的实现主要由以下几种:

  1. J2EE容器所提供的JTA实现:如JBoss,WebLogic、Webshare 等主流商用应用服务器提供了 JTA 的实现和支持。
  2. 第三方JTA实现:如JOTM,Atomikos。适用于那些不使用J2EE应用服务器的环境里,如Tomcat,Jetty以及普通的java应用。Spring也提供了对JOTM,Atomikos的整合。但Spring3.0已经不再支持JOTM。而Atomikos是一个开源项目,适合单块应用跨库的场景

Spring的PlatformTransactionManager,也有JTA的实现JtaTransactionManager。也就是说,你可以使用Spring的事务规范,却使用JTA的实现,而且也几乎不需要任何配置,只要在具体的运行环境中包含JTA的实现就可以。

  • 比如你用JBoss的应用服务器,系统就会使用Jboss的JTA实现;
  • 如果你的class path里面有Atomikos的库,系统就会使用Atomikos的JTA实现。
  • 如果你使用spring-boot,你只需要在你的依赖里面、或运行环境里面,提供你所需要的JTA实现,它就会自动使用。

二、分布式理论

1. CAP理论

CAP理论告诉我们,一个分布式系统不可能同时满足一致性、可用性和分区容错性这三个基本需求,最多只能同时满足其中两项

  • C:Consistency,一致性。同一数据的多个副本是否实时相同。
  • A:Availability,可用性。一定时间内系统返回一个明确的结果 则称为该系统可用。
  • P:Partition tolerance,分区容错性。将同一服务分布在多个系统中,从而保证某一个系统宕机,仍然有其他系统提供相同的服务。

2. Base理论

BASE是Basically Available(基本可用)、Soft state(软状态)和Eventually consistent(最终一致性)三个短语的缩写。BASE理论是对CAP中一致性和可用性权衡的结果,其来源于对大规模互联网系统分布式实践的总结, 是基于CAP定理逐步演化而来的。

BASE理论的核心思想是:即使无法做到强一致性,但每个应用都可以根据自身业务特点,采用适当的方式来使系统达到最终一致性。接下来看一下BASE中的三要素: 

  • 基本可用(Basically Available):指分布式系统在出现不可预知故障的时候,允许损失部分可用性。
  • 软状态( Soft State):指允许系统中的数据存在中间状态,并认为该中间状态的存在不会影响系统的整体可用性。  
  • 最终一致( Eventual Consistency):强调的是所有的数据更新操作,在经过一段时间的同步之后,最终都能够达到一个一致的状态。因此,最终一致性的本质是需要系统保证最终数据能够达到一致,而不需要实时保证系统数据的强一致性。

 

酸(ACID)碱(BASE)平衡,酸碱指的是ACID和BASE两个词,ACID强调强一致性,而BASE则追求弱一致性,满足基本可用。

互联网领域并没有XA事务一样形成X/OpenDTP那样的工业规范,而是仅仅在具体的行业里获得较多的认可;大家熟知的CAP和BASE理论,对于CAP来说,对于共享数据的系统,由于网络分区问题的存在,我们只能满足AP或者CP;对于BASE理论,满足基本可用。

 

分布式事务有以下几种常见的解决方案:

  • 2PC
  • TCC事务补偿型方案(两阶段,补偿型)
  • 可靠消息最终一致性方案(异步确保)
    • 本地消息表
    • 独立消息服务
  • 最大努力通知型方案(定期校对)

在最终一致性这个方案上,有两种选择方案,一种是基于可靠消息中间件来实现异步的最终一致性、另一种就是通过 MQ 来实现最大努力通知型。

三、分布式事务解决方案

1.TCC事务补偿型方案(两阶段型、补偿型事务) 

TCC事务补偿型方案属于两阶段型的一种实现,但区别于2PC协议的两阶段提交。TCC全称为Try-Confirm-Cancel,分为三个阶段:

  • Try 阶段:对各个服务的资源做检测以及对资源进行锁定或者预留。
  • Confirm 阶段:在各个服务中执行实际的操作。
  • Cancel 阶段:如果任何一个服务的业务方法执行出错,那么这里就需要进行补偿,就是执行已经执行成功的业务逻辑的回滚操作。
有一个很常见的误区:很多人把两阶段操作等同于两阶段协议2PC操作。其实TCC操作也属于两阶段型操作。与2PC协议相比,TCC有以下特点:
①位于业务层,而非资源层
②没有单独的准备阶段(prepare),Try操作兼具资源操作和准备能力
③Try操作可以灵活选择业务资源的锁定粒度(以业务定粒度)
④较高开发成本

 

优缺点

因为事务回滚严重依赖于自己写代码来回滚和补偿,而且与业务重耦合,会造成补偿代码巨大,所以通常情况下,这种方案很少使用。但仅仅对一致性要求非常高的场景,才会使用该方案,例如支付,交易等场景。

 

2.可靠消息最终一致性方案(异步确保型)

a.本地消息表

本地消息表是eBay 提出来的一套方案。eBay 的架构师曾在一篇解释BASE 原理的论文《Base:An Acid Alternative》中提到一个eBay 分布式系统一致性问题的解决方案。它的核心思想是将需要分布式处理的任务通过消息或者日志的方式来异步执行,消息或日志可以存到本地文件、数据库或消息队列,再通过业务规则进行失败重试,它要求各服务的接口是幂等的。

发送方:

业务操作和消息的存储在同一个事务中,要么同时成功,要么同时失败。如果事务成功,则将该消息向MQ也发送一份。

消息发送失败,也不要紧,因为发送方业务数据和本地消息数据是一致的,后面可以重试发送消息。

消息发送成功,发送方收到确认后,就会将消息标识为删除。

接收方:

接收到消息后,进行业务处理。接收方需要做幂等处理,防止重复消费。

如果接收方处理失败,则可以进行充重试,重试失败再进行人工干预。

优缺点

这个方案严重依赖于数据库的消息表来管理事务,高并发场景、扩展等不太方便。一般很少使用。

b.独立消息服务

基于本地消息服务的方案虽然可以做到消息的最终一致性,但是它有一个问题,每个业务系统在使用该方案时,都需要在对应的业务库创建一张消息表来存储消息。针对这个问题,就衍生出了独立消息服务的方案,也就是将该功能单独提取出来,做成一个消息服务来统一处理。

 

3.最大努力通知型方案(定期校对,非可靠消息)

 

一般适用于对一致性要求不严格的场景,允许一定程度的失败。最大努力通知方案用的比较少。

选用哪种方案?

【中华石杉】
对于资金等绝对不能出错的严格场景,可以使用TCC来保证强一致性;其它场景,可以使用可靠消息最终一致性方案来实现分布式事务。 【
90%的分布式接口调用,不需要使用分布式事务,而是直接使用监控(发邮件、短信)、记录日志、事后快速定位,排查和解决】。

四、分布式事务框架

有一些开源的分布式解决方案的框架,比如ByteTCC、LCN等。另外,阿里也发布了开源分布式事务框架 Seata(由Fescar和跟蚂蚁 TCC 方案整合而成)。seata分布式事务调研

相关的测试,可以参考:TCC 几个框架的测试情况记录

 

参考:

常用的分布式事务解决方案介绍

分布式事务选型的取舍

posted @ 2017-11-26 14:23  静水楼台/Java部落阁  阅读(299)  评论(0编辑  收藏  举报