认真学习分布式应用的分布式事务之2PC/3PC

认真学习分布式应用的分布式事务2PC/3PC
认真学习分布式应用的分布式事务TCC
认真学习分布式应用的分布式事务之最大努力通知型事务
认真学习分布式应用的分布式事务之消息最终一致性事务

几个关键词:ACID,XA,2PC,3PC,CAP,BASE,TCC及消息队列实现最终一致性。

【1】基本概念

随着分布式计算的发展,事务在分布式计算领域也得到了广泛的应用。在单机数据库中,我们很容易能够实现一套满足ACID特性的事务处理系统,但在分布式数据库中,数据分散在各台不同的机器上,如何对这些数据进行分布式的事务处理具有非常大的挑战。

分布式事务是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于分布式系统的不同节点上,通常一个分布式事务中会涉及对多个数据源或业务系统的操作。

可以设想一个最典型的分布式事务场景:一个跨银行的转账操作涉及调用两个异地的银 行服务,其中一个是本地银行提供的取款服务,另一个则是目标银行提供的存款服务,这两个服务本身是无状态并且相互独立的,共同构成了一个完整的分布式事务。如果从本地银行取款成功,但是因为某种原因存款服务失败了,那么就必须回滚到取款之前的状态,否则用户可能会发现自己的钱不翼而飞了。

从这个例子可以看到,一个分布式事务可以看做是多个分布式的操作序列组成的,例如 上面例子的取款服务和存款服务,通常可以把这一系列分布式的操作序列称为子事务物。因此,分布式事务也可以被定义为一种嵌套型的事务,同时也就具有了 ACID事务特性。但由于在分布式事务中,各个子事务的执行是分布式的,因此要实现一种能够保证ACID特性的分布式事务处理系统就显得格外复杂。


① 分布式事务常见解决方案

  • 2PC两段提交协议

  • 3PC三段提交协议(弥补两端提交协议缺点)

  • TCC或者GTS(阿里)

  • 消息中间件最终一致性

  • 传统项目采用JTA(Java操作分布式事物XA接口)+Atomikos+Druid

  • 使用LCN解决分布式事物,理念“LCN并不生产事务,LCN只是本地事务的搬运工”。

-2PC3PC补偿可靠事件TCC
一致性强一致性强一致性最终一致性最终一致性最终一致性
事务全局全局全局全局全局
吞吐量
实现复杂度
实时性

在金融/银行领域,通常要保证强一致性的,故而实现"最终一致性"的分布式事务解决方案比如TCC、消息中间件保证最终一致性是不采取的,一般采取2PC/3PC或变种方案。


② XA规范

X/Open 组织(即现在的 Open Group )定义了分布式事务处理模型。 X/Open DTP 模型( 1994 )包括应用程序( AP )、事务管理器( Transaction Manager )、资源管理器( Resource Manager )、通信资源管理器( CRM )四部分。

XA 规范主要 定义了 ( 全局 ) 事务管理器 ( Transaction Manager ) 和 ( 局部 ) 资源管理器 ( Resource Manager ) 之间的接口。 XA 接口是双向的系统接口,在事务管理器(Transaction Manager)以及一个或多个资源管理器(Resource Manager)之间形成通信桥梁。 XA 之所以需要引入事务管理器是因为,在分布式系统中,从理论上讲(参考Fischer等的论文),两台机器理论上无 法达到一致的状态,需要引入一个单点进行协调。 事务管理器控制着全局事务,管理事务生命周期,并协调资源。资源管理器负责
控制和管理实际资源(如数据库或 JMS队列)。

在这里插入图片描述

一般常见的事务管理器( TM )是交易中间件,常见的资源管理器( RM )是数据库,常见的通信资源管理器( CRM )是消息中间件。 通常把一个数据库内部的事务处理,如对多个表的操作,作为本地事务看待。数据库的事务处理对象是本地事务,而分布式事务处理的对象是全局事务。

所谓全局事务,是指分布式事务处理环境中,多个数据库可能需要共同完成一个工作,这个工作即是一个全局事务。

例如,一个事务中可能更新几个不同的数据库。对数据库的操作发生在系统的各处但必须全部被提交或回滚。此时一个数据库对自己内部所做操作的提交不仅依赖本身操作是否成功,还要依赖与全局事务相关的其它数据库的操作是否成功,如果任一数据库的任一操作失败,则参与此事务的所有数据库所做的所有操作都必须回滚。

一般情况下,某一数据库无法知道其它数据库在做什么,因此,在一个 DTP 环境中,交易中间件是必需的,由它通知和协调相关数据库的提交或回滚。而一个数据库只将其自己所做的操作(可恢复)影射到全局事务中。

XA 就是 X/Open DTP 定义的交易中间件与数据库之间的接口规范(即接口函数),交易中间件用它来通知数据库事务的开始、结束以及提交、回滚等。 XA 接口函数由数据库厂商提供。

二阶提交协议和三阶提交协议就是根据这一思想衍生出来的。可以说二阶段提交其实就是实现XA分布式事务的关键(确切地说:两阶段提交主要保证了分布式事务的原子性:即所有结点要么全做要么全不做)

JTA
Java事务API(简称JTA)是一个Java企业版的应用程序接口。在Java环境中,允许完成跨越多个XA资源的分布式事务。

JTA事务
在两个或多个网络计算机资源上访问并且更新数据,这些数据可能分布在多个数据库上。

在 JavaEE 平台下,WebLogic、Webshare 等主流商用的应用服务器提供了 JTA 的实现和支持。而在Tomcat 下是没有实现的(Tomcat 不能算是 JavaEE 应用服务器,比较轻量),这就需要借助第三方的框架 Jotm、Automikos 等来实现,两者均支持 Spring 事务整合。


【2】分布式事务之2PC(Two-Phase Commit)

数据库的2PC(两阶段提交)又叫做 XA Transactions,强一致性、性能不高。顾名思义,二阶段提交就是将事务的提交过程分成了两个阶段来进行处理。

① 2PC基本原理

2PC的核心原理是通过提交分阶段和记日志的方式,记录下事务提交所处的阶段状态,在组件宕机重启后,可通过日志恢复事务提交的阶段状态,并在这个状态节点重试。

两阶段提交协议,是将整个事务流程分为两个阶段,准备阶段(Preparephase)、提交阶段(commit phase),2是指两个阶段,P是指准备阶段,C是指提交阶段。

  • 准备阶段(Prepare phase):事务管理器给每个参与者发送Prepare消息,每个数据库参与者在本地执行事务,并写本地的Undo/Redo日志,此时事务没有提交。 (Undo日志是记录修改前的数据,用于数据库回滚,Redo日志是记录修改后的数据,用于提交事务后写入数据文件)
  • 提交阶段(commit phase):如果事务管理器收到了参与者的执行失败或者超时消息时,直接给每个参与者发送回滚(Rollback)消息;否则,发送提交(Commit)消息;参与者根据事务管理器的指令执行提交或者回滚操作,并释放事务处理过程中使用的锁资源。注意:必须在最后阶段释放锁资源。

示意图如下:
在这里插入图片描述

如果协调者重启后,通过日志可以确定提交处于Prepare还是PrepareAll状态。若是前者,说明有节点可能没有Prepare成功,或所有节点Prepare成功但还没有下发Commit,状态恢复后给所有节点下发RollBack;若是PrepareAll状态,需要给所有节点下发Commit,数据库节点需要保证Commit幂等。

② 事务提交

① 阶段1:请求阶段(commit-request phase,或称表决阶段,voting phase)

  • 协调者节点向所有参与者节点询问是否可以执行提交操作,并开始等待各参与者节点的响应。

  • 参与者节点执行询问发起为止的所有事务操作(但是不提交),并将Undo信息和Redo信息写入日志。

  • 各参与者节点响应协调者节点发起的询问。如果参与者节点的事务操作实际执行成功,则它返回一个”同意”消息。如果参与者节点的事务操作实际执行失败,则它返回一个”中止”消息。 故而有时候第一阶段也被称作投票阶段,即各参与者投票是否要继续接下来的提交操作。

② 阶段2:提交阶段(commit phase)

过程如下:

  • 发送提交请求: 协调者向所有参与者发出 commit 请求。
  • 事务提交: 参与者收到 commit 请求后,会正式执行事务提交操作,并在完成提交之后 释放整个事务执行期间占用的事务资源。
  • 反馈事务提交结果: 参与者在完成事务提交之后,向协调者发送 Ack 信息。
  • 完成事务: 协调者接收到所有参与者反馈的 Ack 信息后,完成事务。

ACK 确认字符,在数据通信中,接收站发给发送站的一种传输类控制字符。表示发来的数据已确认接收无误。

在该阶段,协调者将基于第一个阶段的投票结果进行决策:提交或取消(中断事务)。当且仅当所有的参与者同意提交,事务协调者才通知所有的参与者提交事务。否则协调者将通知所有的参与者取消事务。参与者在接收到协调者发来的消息后将执行相应的操作并释放所有事务处理过程中使用的锁资源。

③ 事务中断

假如任何一个参与者向协调者反馈了No响应,或者在等待超时之后,协调者尚无法接收到所有参与者的反馈响应,那么就会中断事务。

阶段一:

  • 事务询问 协调者向所有的参与者发送事务内容,询问是否可以执行事务提交操作,并开始等待各参与者的响应。
  • 执行事务 (写本地的Undo/Redo日志)
  • 各参与者向协调者反馈事务询问的响应 总结: 各个参与者进行投票是否让事务进行.

阶段二

  • 发送回滚请求: 协调者向所有参与者发出 Rollback 请求。
  • 事务回滚: 参与者接收到 Rollback 请求后,会利用其在阶段一中记录的 Undo 信息来执行事务回滚操作,并在完成回滚之后 释放在整个事务执行期间占用的资源。
  • 反馈事务回滚结果: 参与者在完成事务回滚之后,向协调者发送 Ack 信息。
  • 中断事务: 协调者接收到所有参与者反馈的 Ack 信息后,完成事务中断。

从上面的逻辑可以看出,二阶段提交就做了2个事情:投票,执行。

④ 2PC的缺点

不管最后结果如何,第二阶段都会结束当前事务。二阶段提交看起来确实能够提供原子性的操作,但是不幸的事,二阶段提交还是有几个缺点的。

① 同步阻塞问题

二阶段提交协议存在最明显也是最大的一个问题就是同步阻塞,在二阶段提交的执行过程中,所有参与该事务操作的逻辑都处于阻塞状态,也就是说,各个参与者在等待其他参与者响应的过程中,无法进行其他操作。这种同步阻塞极大的限制了分布式系统的性能。

② 单点故障

由于协调者的重要性,一旦协调者发生故障。参与者会一直阻塞下去。尤其在第二阶段,协调者发生故障,那么所有的参与者还都处于锁定事务资源的状态中,而无法继续完成事务操作。(如果是协调者挂掉,可以重新选举一个协调者,但是无法解决因为协调者宕机导致的参与者处于阻塞状态的问题)

③ 数据不一致

在二阶段提交的阶段二中,当协调者向参与者发送commit请求之后,发生了局部网络异常或者在发送commit请求过程中协调者发生了故障。这会导致只有一部分参与者接受到了commit请求。而在这部分参与者接到commit请求之后就会执行commit操作。但是其他部分未接到commit请求的机器则无法执行事务提交,于是整个分布式系统便出现了数据不一致性的现象。

④ 二阶段无法解决的问题

协调者再发出commit消息之后宕机,而唯一接收到这条消息的参与者同时也宕机了。那么即使协调者通过选举协议产生了新的协调者,这条事务的状态也是不确定的,没人知道事务是否已经被提交。

如果在二阶段提交的提交询问阶段中,参与者出现故障而导致协调者始终无法获取到所有参与者的响应信息的话,这时协调者只能依靠其自身的超时机制来判断是否需要中断事务,显然,这种策略过于保守。换句话说,二阶段提交协议没有设计较为完善的容错机制,任意一个节点失败都会导致整个事务的失败。


【3】分布式事务之3PC

三阶段提交协议,是2pc的改进版,与两阶段提交不同的是,三阶段提交是“非阻塞”协议。除了引入超时机制之外,3PC把2PC的准备阶段再次一分为二,这样三阶段提交就有CanCommitPreCommitDoCommit三个阶段。
在这里插入图片描述

① 阶段一:CanCommit阶段(参与者不会本地执行事务)

3PC的CanCommit阶段其实和2PC的准备阶段很像。协调者向参与者发送canCommit请求,参与者如果可以提交就返回Yes响应,否则返回No响应。

  • 事务询问协调者向所有的参与者发送一个包含事务内容的canCommit请求,询问是否可以执行事务提交操作,并开始等待各参与者的响应。

  • 响应反馈 参与者接到CanCommit请求之后,正常情况下,如果其自身认为可以顺利执行事务,则返回Yes响应,并进入预备状态。否则反馈No。


② 阶段二:PreCommit阶段

协调者在得到所有参与者的响应之后,会根据结果有2种执行操作的情况:执行事务预提交,或者中断事务。假如所有参与反馈的都是Yes,那么就会执行事务预提交。

① 执行事务预提交

假如协调者从所有的参与者获得的反馈都是Yes响应,那么进入Prepared阶段:

  • 发送预提交请求:协调者向所有参与者节点发出preCommit的请求,并进入Prepared阶段。
  • 事务预提交:参与者接收到preCommit请求后,会执行事务操作,并将Undo和Redo信息记录到事务日志中。
  • 各参与者向协调者反馈事务执行的响应:如果参与者成功执行了事务操作,那么就会反馈给协调者Ack响应,同时等待最终的指令:提交(commit)或中止(abort)

② 中断事务

假如任何一个参与者向协调者反馈了No响应,或者在等待超时之后,协调者尚无法接收到所有参与者的反馈响应,那么就会中断事务。

  • 发送中断请求:协调者向所有参与者节点发出abort请求。
  • 中断事务:无论是收到来自协调者abort请求,或者是在等待协调者请求过程中出现超时,参与者都会中断事务。

阶段三:doCommit阶段

该阶段将进行真正的事务提交,会存在以下两种可能情况:

① 执行提交

  • 发送提交请求:进入这一阶段,假设协调者处于正常工作状态,并且它接受到了来自参与者的Ack响应,那么它将从“预提交”状态转换到“提交”状态,并向所有的参与者发送doCommit请求。
  • 事务提交:参与者接收到doCommit请求后,会正式执行事务提交操作,并在完成提交之后释放在整个事务执行期间占用的事务资源。
  • 反馈事务提交结果:参与者在完成事务提交之后,向协调者发送Ack消息
  • 完成事务:协调者接收到所有参与者反馈的Ack消息后,完成事务。

② 中断事务

进入这一阶段,假设协调者处于正常工作状态,并且有任意一个参与者向协调者反馈了No响应,或者在等待超时之后,协调者尚无法接收到所有参与者反馈响应,那么就会中断事务。

  • 发送中断请求:协调者向所有的参与者节点发送abort请求
  • 事务回滚:参与者接收到abort请求后,会利用其在阶段二中记录的Undo信息来执行事务回滚操作,并在完成回滚之后释放在整个事务执行期间的资源。
  • 反馈事务回滚结果:参与者在完成事务回滚之后,向协调者发送Ack消息。
  • 中断事务:协调者接收到所有参与者反馈的Ack消息后,中断事务。

④ 3PC总结

3PC注意事项

需要注意的是,一旦进入阶段三,可能会存在以下两种故障。

  • 协调者出现问题
  • 协调者和参与者之间的网络出现故障

无论出现哪种情况,最终都会导致参与者无法及时接收到来自协调者的doCommit或是abort请求,针对这样的异常情况,参与者都会在等待超时之后,继续进行事务提交。【参与者在等待协调者发送最终提交请求来临阶段,参与者超时未得到协调者的真正提交请求,会自动提交事务

3PC优点和缺点

三阶段提交协议的优点:相较于二阶段提交协议,三阶段提交协议最大的优点就是降低了参与者的阻塞范围,并且能够在出现单点故障(协调者挂掉后)后继续达成一致。

三阶段提交协议的缺点:三阶段提交协议在去除阻塞的同时也引入了新问题。那就是在参与者接收到preCommit消息后,如果网络出现分区,此时协调者所在的节点和参与者无法进行正常网络通信,在这种情况下,该参与者依然会进行事务提交,这必然出现数据不一致性。

⑤ 2PC与3PC区别

相对于2PC,3PC主要解决的单点故障问题,并减少阻塞。因为一旦参与者无法及时收到来自协调者的信息之后,它会默认执行commit。而不会一直持有事务资源并处于阻塞状态。

但是这种机制也会导致数据一致性问题。因为由于网络原因,协调者发送的abort响应没有及时被参与者接收到,那么参与者在等待超时之后执行了commit操作。这样就和其他接到abort命令并执行回滚的参与者之间存在数据不一致的情况。

在 JavaEE 平台下,WebLogic、Webshare 等主流商用的应用服务器提供了 JTA 的实现和支持。而在Tomcat 下是没有实现的(Tomcat 不能算是 JavaEE 应用服务器,比较轻量),这就需要借助第三方的框架 Jotm、Automikos 等来实现,两者均支持 Spring 事务整合。

参考博文:

分布式事务一致性协议2pc和3pc
分布式事务之2PC和3PC

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值