分布式事务解决方案

分布式事务解决方案,第1张

严格意义上的事务实现应该是具备原子性、一致性、隔离性和持久性,简称 ACID。分布式事务顾名思义就是要在分布式系统中实现事务,它其实是由多个本地事务组合而成。

目前常用和常被提及分布式事务解决方案有五种:
  1. 两阶段提交(2PC)
  2. 三阶段提交(3PC)
  3. 补偿事务(TCC=Try-Confifirm-Cancel)
  4. 消息队列(MQ)
  5. Sagas事务模型(最终一致性)
下面就上述五种方案简单记录原理。 二阶段提交 两阶段提交 2PC 是分布式事务中最强大的事务类型之一,两段提交就是分两个阶段提交: 第一阶段询问各个事务数据源是否准备好。 第二阶段才真正将数据提交给事务数据源。 为了保证该事务可以满足 ACID ,就要引入一个协调者( Cooradinator)。其他的节点被称为参与者( Participant )。协调者负责调度参与者的行为,并最终决定这些参与者是否要把事务进行提交。 处理流程如下:

阶段一 a) 协调者向所有参与者发送事务内容,询问是否可以提交事务,并等待答复。 b) 各参与者执行事务 *** 作,将 undo redo 信息记入事务日志中(但不提交事务)。 c) 如参与者执行成功,给协调者反馈 yes ,否则反馈 no 阶段二 如果协调者收到了参与者的失败消息或者超时,直接给每个参与者发送回滚 (rollback) 消息;否则, 发送提交 (commit) 消息。两种情况处理如下: 情况 1 当所有参与者均反馈 yes ,提交事务 a) 协调者向所有参与者发出正式提交事务的请求(即 commit 请求)。 b) 参与者执行 commit 请求,并释放整个事务期间占用的资源。 c) 各参与者向协调者反馈 ack( 应答 ) 完成的消息。 d) 协调者收到所有参与者反馈的 ack 消息后,即完成事务提交。 情况 2 当有一个参与者反馈 no ,回滚事务 a) 协调者向所有参与者发出回滚请求(即 rollback 请求)。 b) 参与者使用阶段 1 中的 undo 信息执行回滚 *** 作,并释放整个事务期间占用的资源。 c) 各参与者向协调者反馈 ack 完成的消息。 d) 协调者收到所有参与者反馈的 ack 消息后,即完成事务。 问题 1) 性能问题 :所有参与者在事务提交阶段处于同步阻塞状态,占用系统资源,容易导致性能瓶颈。 2) 可靠性问题: 如果协调者存在单点故障问题,或出现故障,提供者将一直处于锁定状态。 3) 数据一致性问题: 在阶段 2 中,如果出现协调者和参与者都挂了的情况,有可能导致数据不一致。 优点: 尽量保证了数据的强一致,适合对数据强一致要求很高的关键领域。(其实也不能 100%保证强一致)。 缺点: 实现复杂,牺牲了可用性,对性能影响较大,不适合高并发高性能场景。 三阶段提交 三阶段提交是在二阶段提交上的改进版本, 3PC 最关键要解决的就是协调者和参与者同时挂掉的问 题,所以 3PC 2PC 的准备阶段再次一分为二,这样三阶段提交。 处理流程如下 :

阶段一

a) 协调者向所有参与者发出包含事务内容的 canCommit 请求,询问是否可以提交事务,并等待所 有参与者答复。 b) 参与者收到 canCommit 请求后,如果认为可以执行事务 *** 作,则反馈 yes 并进入预备状态,否 则反馈 no 阶段二 协调者根据参与者响应情况,有以下两种可能。 情况 1 所有参与者均反馈 yes ,协调者预执行事务 a) 协调者向所有参与者发出 preCommit 请求,进入准备阶段。 b) 参与者收到 preCommit 请求后,执行事务 *** 作,将 undo redo 信息记入事务日志中(但不 提交事务)。 c) 各参与者向协调者反馈 ack 响应或 no 响应,并等待最终指令。 情况 2 只要有一个参与者反馈 no ,或者等待超时后协调者尚无法收到所有提供者的反馈,即中断 事务 a) 协调者向所有参与者发出 abort 请求。 b) 无论收到协调者发出的 abort 请求,或者在等待协调者请求过程中出现超时,参与者均会中断事 务。 阶段三 该阶段进行真正的事务提交,也可以分为以下两种情况。 情况 1 所有参与者均反馈 ack 响应,执行真正的事务提交 a) 如果协调者处于工作状态,则向所有参与者发出 do Commit 请求。 b) 参与者收到 do Commit 请求后,会正式执行事务提交,并释放整个事务期间占用的资源。 c) 各参与者向协调者反馈 ack 完成的消息。 d) 协调者收到所有参与者反馈的 ack 消息后,即完成事务提交。 情况 2 只要有一个参与者反馈 no,或者等待超时后协调组尚无法收到所有提供者的反馈,即回滚 事务。 a) 如果协调者处于工作状态,向所有参与者发出 rollback 请求。 b) 参与者使用阶段 1 中的 undo 信息执行回滚 *** 作,并释放整个事务期间占用的资源。 c) 各参与者向协调组反馈 ack 完成的消息。 d) 协调组收到所有参与者反馈的 ack 消息后,即完成事务回滚。 优点:相比二阶段提交,三阶段提交降低了阻塞范围,在等待超时后协调者或参与者会中断事务。 避免了协调者单点问题。阶段 3 中协调者出现问题时,参与者会继续提交事务。 缺点: 数据不一致问题依然存在,当在参与者收到 preCommit 请求后等待 do commite 指令时, 此时如果协调者请求中断事务,而协调者无法与参与者正常通信,会导致参与者继续提交事务,造 成数据不一致。 补偿事务 TCC Try Confifirm Cancel )是服务化的二阶段编程模型,采用的补偿机制:

TCC 其实就是采用的补偿机制,其核心思想是:针对每个 *** 作,都要注册一个与其对应的确认和补 偿(撤销) *** 作。 它分为三个步骤: Try 阶段主要是对业务系统做检测及资源预留。 Confifirm 阶段主要是对业务系统做确认提交, Try 阶段执行成功并开始执行 Confifirm阶段时,默 认 Confifirm 阶段是不会出错的。即:只要 Try 成功, Confifirm 一定成功。 Cancel 阶段主要是在业务执行错误,需要回滚的状态下执行的业务取消,预留资源释放。 举个例子,假入你要向 老田 转账,思路大概是: 我们有一个本地方法,里面依次调用步骤: 1、 首先在 Try 阶段,要先调用远程接口把 你 和 老田 的钱给冻结起来。 2 、在 Confifirm 阶段,执行远 程调用的转账的 *** 作,转账成功进行解冻。 3 、如果第 2步执行成功,那么转账成功,如果第二步执 行失败,则调用远程冻结接口对应的解冻方法 (Cancel) 优点: 性能提升:具体业务来实现控制资源锁的粒度变小,不会锁定整个资源。 数据最终一致性:基于 Confifirm Cancel 的幂等性,保证事务最终完成确认或者取消,保证数据的一致性。 可靠性:解决了 XA 协议的协调者单点故障问题,由主业务方发起并控制整个业务活动,业务活动 管理器也变成多点,引入集群。 缺点: TCC Try Confifirm Cancel *** 作功能要按具体业务来实现,业务耦合度较高,提高了开发成本。 消息队列 本地消息表(异步确保) 本地消息表这种实现方式应该是业界使用最多的,其核心思想是将分布式事务拆分成本地事务进行 处理,这种思路是来源于 ebay 。我们可以从下面的流程图中看出其中的一些细节:

基本思路就是:

消息生产方,需要额外建一个消息表,并记录消息发送状态。消息表和业务数据要在一个事务里提 交,也就是说他们要在一个数据库里面。然后消息会经过 MQ发送到消息的消费方。如果消息发送 失败,会进行重试发送。 消息消费方,需要处理这个消息,并完成自己的业务逻辑。此时如果本地事务处理成功,表明已经 处理成功了,如果处理失败,那么就会重试执行。如果是业务上面的失败,可以给生产方发送一个 业务补偿消息,通知生产方进行回滚等 *** 作。 生产方和消费方定时扫描本地消息表,把还没处理完成的消息或者失败的消息再发送一遍。如果有 靠谱的自动对账补账逻辑,这种方案还是非常实用的。 这种方案遵循 BASE理论,采用的是最终一致性,笔者认为是这几种方案里面比较适合实际业务场景的,即不会出现像 2PC 那样复杂的实现 ( 当调用链很长的时候, 2PC 的可用性是非常低的 ),也不会像 TCC 那样可能出现确认或者回滚不了的情况。 优点: 一种非常经典的实现,避免了分布式事务,实现了最终一致性。在 .NET中 有现成的解决方案。 缺点: 消息表会耦合到业务系统中,如果没有封装好的解决方案,会有很多杂活需要处理。 MQ事务消息 有一些第三方的 MQ 是支持事务消息的,比如 RocketMQ,他们支持事务消息的方式也是类似于采用的二阶段提交,但是市面上一些主流的 MQ 都是不支持事务消息的,比如 RabbitMQ Kafka 都不支持。 以阿里的 RocketMQ 中间件为例,其思路大致为: 第一阶段 Prepared消息,会拿到消息的地址。 第二阶段执行本地事务,第三阶段通过第一阶段拿到 的地址去访问消息,并修改状态。 也就是说在业务方法内要想消息队列提交两次请求,一次发送消息和一次确认消息。如果确认消息 发送失败了 RocketMQ 会定期扫描消息集群中的事务消息,这时候发现了 Prepared消息,它会向消 息发送者确认,所以生产方需要实现一个 check 接口, RocketMQ会根据发送端设置的策略来决定是回滚还是继续发送确认消息。这样就保证了消息发送与本地事务同时成功或同时失败。 遗憾的是, RocketMQ 并没有 .NET 客户端。可自行查阅 RocketMQ 的更多信息。 优点: 实现了最终一致性,不需要依赖本地数据库事务。 缺点: 实现难度大,主流 MQ 不支持,没有 .NET 客户端, RocketMQ 事务消息部分代码也未开源。 Sagas事务模型 Saga模式是一种分布式异步事务,一种最终一致性事务,是一种柔性事务,有两种不同的方式来实现 saga 事务,最流行的两种方式是: 一、 事件 / 编排 Choreography :没有中央协调器(没有单点风险)时,每个服务产生并聆听其他 服务的事件,并决定是否应采取行动。 该实现第一个服务执行一个事务,然后发布一个事件。该事件被一个或多个服务进行监听,这些服务再执行本地事务并发布(或不发布)新的事件,当最后一个服务执行本地事务并且不发布任何事件时,意味着分布式事务结束,或者它发布的事件没有被任何 Saga 参与者听到都意味着事务结束。

 

处理流程说明: 订单服务保存新订单,将状态设置为 pengding 挂起状态,并发布名为 ORDER_CREATED_EVENT的事件。 支付服务监听 ORDER_CREATED_EVENT ,并公布事件 BILLED_ORDER_EVENT 库存服务监听 BILLED_ORDER_EVENT ,更新库存,并发布 ORDER_PREPARED_EVENT 货运服务监听 ORDER_PREPARED_EVENT,然后交付产品。最后,它发布ORDER_DELIVERED_EVENT 最后,订单服务侦听 ORDER_DELIVERED_EVENT 并设置订单的状态为 concluded 完成。 假设库存服务在事务过程中失败了。进行回滚: 库存服务产生 PRODUCT_OUT_OF_STOCK_EVENT 订购服务和支付服务会监听到上面库存服务的这一事件: ①支付服务会退款给客户。 ②订单服务将订单状态设置为失败。 优点: 事件 / 编排是实现 Saga 模式的自然方式 ; 它很简单,容易理解,不需要太多的努力来构建,所有参与者都是松散耦合的,因为他们彼此之间没有直接的耦合。如果您的事务涉及 2 4个步骤,则可能是非常合适的。 二、 命令 / 协调 orchestrator :中央协调器负责集中处理事件的决策和业务逻辑排序。 saga 协调器 orchestrator 以命令 / 回复的方式与每项服务进行通信,告诉他们应该执行哪些 *** 作。

 

订单服务保存 pending 状态,并要求订单 Saga 协调器(简称 OSO )开始启动订单事务。 OSO 向收款服务发送执行收款命令,收款服务回复 Payment Executed 消息。 OSO 向库存服务发送准备订单命令,库存服务将回复 OrderPrepared 消息。 OSO 向货运服务发送订单发货命令,货运服务将回复 Order Delivered 消息。 OSO 订单 Saga 协调器必须事先知道执行 创建订单 事务所需的流程 ( 通过读取 BPM 业务流程 XML配置获得)。如果有任何失败,它还负责通过向每个参与者发送命令来撤销之前的 *** 作来协调分布式的回滚。当你有一个中央协调器协调一切时,回滚要容易得多,因为协调器默认是执行正向流程,回滚时只要执行反向流程即可。 优点: 避免服务之间的循环依赖关系,因为 saga 协调器会调用 saga 参与者,但参与者不会调用协调器。 集中分布式事务的编排。 只需要执行命令 / 回复 ( 其实回复消息也是一种事件消息 ) ,降低参与者的复杂性。 在添加新步骤时,事务复杂性保持线性,回滚更容易管理。 如果在第一笔交易还没有执行完,想改变有第二笔事务的目标对象,则可以轻松地将其暂停在协调 器上,直到第一笔交易结束。 图文来源网络,查看参考内容

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/757330.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-30
下一篇 2022-04-30

发表评论

登录后才能评论

评论列表(0条)

保存