分布式理论之分布式事务

时间:2022-12-27 17:52:48

写在前面

我们知道,像MySQL的InnoDB存储引擎提供了事务的能力,严格遵守AICD的事务要求,但是在分布式环境中,一个请求会在多个服务实例存在多个事务,如购物,会有订单系统,支付系统,物流系统,各个系统的事务逻辑上是一个统一的整体,也要保证事务,这种状态下的事务我们就叫做分布式事务,本文要一起学习的就是有哪些方案可以来解决分布式事务的问题。

1:都有哪些方法解决分布式事务

在分析都有哪些方法来解决分布式事务之前,先来看下什么是XA协议,XA协议是一种分布式事务的协议,规定了事务管理器和本地事务管理器两个角色,其中事务管理器负责管理本地事务管理器的提交和回滚,而本地事务管理器需要实现XA相关的接口,针对XA协议目前有两种具体的实现,就引出了解决分布式事务的其中两种方法,分别是2PC(2 phase commit)即2阶段提交,3PC(3 phase commit)即3阶段提交。另外还有一种是基于分布式消息的最终一致性方案

2:具体方案分析

2.1:2PC

2PC全称是2 phase commit,即两阶段提交,是XA协议的实现,包含一个事务管理器角色,多个本地事务管理器角色,本地事务管理器一般是具体的DB,如Oracle,MySQL等,这里的两阶段就代表了使用2个阶段来完成所有事务的提交,分别是投票阶段和提交阶段。

首先开始第一个阶段投票阶段,事务管理器向所有的本地事务管理器发送CanCommit消息,当本地事务管理器完成操作后,将修改写到redo,undo,但是没有写到磁盘(真正提交时执行这一步骤操作),如果操作成功则向事务管理器回复YES,否则返回NO,如下是这两种情况示意图:

分布式理论之分布式事务

接下来进入第二个阶段,即提交阶段,如果是所有的本地事务管理器都返回YES,则事务管理器会发送DoCommit,然后本地事务管理会提交事务,并释放相关数据库锁,以及其他占用的资源,执行完成后返回HaveCommited给事务管理器,如果是有一个本地事务管理器返回的是NO,事务管理器则会发送DoAbort,让所有的事务回滚,执行完成后返回HaveCommited给事务管理器,发送DoAbort的情况如下图发送Docommit情况类似

分布式理论之分布式事务

2PC两阶段提交的优点是实现简单,但是缺点也非常明显,如下:

1:同步阻塞,不管是在投票阶段,还是在提交阶段,在存在本地事务管理器没有响应结果给事务管理器时,所有的本地事务管理器都会处于阻塞状态,即临界资源不能被其他事务访问,如果是出现某本地事务管理器异常宕机,或者网络不通情况时,这种阻塞将会更加严重,服务器的性能会收到比较大的影响。
2:数据一致性问题,在提交阶段,事务管理器发送给所有本地事务管理器的Docommit消息,并不能保证所有的本地事务管理器都收到,可能因为网络原因丢失,也可能恰巧某些本地事务管理器出现宕机。比如A,B两个本地事务管理器,如果是A正常收到,则A会正常提交事务,而B出现宕机无法正常收到,则事务就不会正常提交,B重新启动后事务会自动回滚`(mysql InnoDB如此)`,此时就出现了数据一致性问题。这就是一个比较严重的问题了,因为出现了数据错误。
3:单点问题,当事务管理器出现故障时,整个系统将会停滞。当然如果是事务管理器是集群的话这个问题也可以被解决。

针对可能出现严重的同步阻塞,2PC进行升级就产生了3PC,即三阶段提交,接下来一起看下。

2.2:3PC

3 phase commit,即3阶段提交,是XA协议的实现,包含一个事务管理器角色,多个本地事务管理器角色,本地事务管理一般是具体的DB,如Oracle,MySQL等,这里的三阶段就代表了使用三个阶段来完成所有事务的提交,分别是CanCommitPreCommitDoCommit

首先是第一个阶段CanCommit,事务管理器向所有的本地事务管理器发送CanCommit消息,本地事务管理器仅仅检查是否可以顺利执行事务,不作任何实际的执行,可以执行返回YES,不能执行返回NO,返回YES和NO的情况如下图:

分布式理论之分布式事务

当存在返回NO的情况时,则停止,不进入第二个阶段,否则进入第二个阶段PreCommit,事务管理器向所有的本地事务管理器发送PreCommit,然后本地事务管理器开始执行事务,写redo,undo,执行成功的话则返回ACK给事务管理器(如果是超时也没有收到下一步执行,如DoCommit,Abort则自动回滚事务,这里会在一定程度上减少同步阻塞问题,不用死等),失败返回No,此时有以下三种情况:

1:如果是最终都成功返回ACK给事务管理器
2:如果是存在No
3:如果是事务管理器在超时前没有收到所有本地事务管理器的回复消息

对于23这里会在一定程度上减少同步阻塞问题,不用死等未响应的本地事务管理器),事务管理器都会发送Abort,回滚事务。对于1,则会正常进入第三个阶段DoCommit,
对于23,事务管理器都会发送Abort,回滚事务。对于1,则会正常进入第三个阶段DoCommit,事务管理器发送DoCommit消息给所有的本地事务管理器,完成事务的最终提交,完成回复ACK,完成整个事务,可能情况如下图:

分布式理论之分布式事务

3PC,三阶段提交的优点是在事务管理器和本地事务管理器都引入了超时机制,在一定程度上减轻了同步阻塞问题,虽然没有完全解决,但也有改善,缺点是依然存在数据一致性问题。

2.3:基于分布式消息的最终一致性

不管是2PC还是3PC都存在阻塞等待,数据一致性无法得到保证的问题,基于此便产生了基于分布式消息组件的异步事务处理机制,我们需要引入一个消息中间件来在多个应用之间转发消息,基于消息应用完成事务处理,如下图:

分布式理论之分布式事务

注意,这里的核心是消息中间件,必须保证数据不丢失,具备重试机制等,上一个事务在完成事务前需要保证发送给其他应用的消息都已经进行了持久化,这样才能保证其他事务最终都会执行(失败重试,或者是人工介入方式,但最终数据的状态一定是一致的),如下图(该图为大概示意)

分布式理论之分布式事务

实际场景可能如下图:

分布式理论之分布式事务

过程如下:

1:订单系统把订单消息发给消息中间件,消息状态标记为“待确认”。
2:消息中间件收到消息后,进行消息持久化操作,即在消息存储系统中新增一条状态为“待发送”的消息。
3:消息中间件返回消息持久化结果(成功 / 失败),订单系统根据返回结果判断如何进行业务操作。失败,放弃订单,结束(必要时向上层返回失败结果);成功,则创建订单。
4:订单操作完成后,把操作结果(成功 / 失败)发送给消息中间件。
5:消息中间件收到业务操作结果后,根据结果进行处理:失败,删除消息存储中的消息,结束;成功,则更新消息存储中的消息状态为“待发送(可发送)”,并执行消息投递。
6:如果消息状态为“可发送”,则 MQ 会将消息发送给支付系统,表示已经创建好订单,需要对订单进行支付。支付系统也按照上述方式进行订单支付操作。
7:订单系统支付完成后,会将支付消息返回给消息中间件,中间件将消息传送给订单系统。若支付失败,则订单操作失败,订单系统回滚到上一个状态,MQ 中相关消息将被删除;若支付成功,则订单系统再调用库存系统,进行出货操作,操作流程与支付系统类似。

但是这种方式无法同步的给用户返回操作结果,所以只能应用于用户不需要同步获取操作结果的场景。

写在后面

参考文章列表:

mysql xa协议_关于分布式事务,XA协议的学习笔记