分布式事务解决方案

将一个总体模块拆分为多个微服务,某些业务场景须要同时操做多个原子服务的数据,分布式事务就是用来保证多个原子服务数据源一致性的解决方案。算法

分布式事务产生的缘由?

数据库分库分表:因为单表的数据量巨大须要分库分表,分库分表以后,此时一个操做可能涉及访问多个数据库,为了保证数据一致性,就须要用到分布式事务。 应用SOA化:指的是业务的服务化,将一个总体的系统拆分为多个子系统,每一个子系统都有本身的数据库,为了保证数据一致性,就须要用到分布式事务。数据库

常见的分布式解决方案

1. 两阶段提交/XA(2PC)

两阶段提交的算法思路能够归纳为:参与者将操做成功或失败的结果通知协调者,再由协调者根据全部参与者的反馈状况决定个参与者是否要提交操做仍是停止操做。网络

第一阶段:请求阶段(投票阶段)

  1. 协调者向全部的参与者发送事务执行请求,并等待参与者反馈事务执行结果。
  2. 事务参与者收到请求以后,本地执行事务,但不提交。
  3. 参与者将本身事务执行状况反馈给协调者,同时等待协调者的下一步通知。

第二阶段:提交阶段(执行阶段)

在第一阶段协调者的询盘以后,各个参与者会回复本身事务的执行状况,这时候存在三种可能:异步

  1. 全部的参与者回复可以正常执行事务。分布式

    • 协调者向各个参与者发送commit通知,请求提交事务。
    • 参与者收到事务提交通知以后,执行commit操做。
    • 参与者向协调者返回事务commit结果信息。
  2. 一个或多个参与者回复事务执行失败。微服务

  3. 协调者等待超时。编码

    • 协调者向各个参与者发送事务rollback通知,请求回滚事务。
    • 参与者收到事务回滚通知以后,执行rollback操做。
    • 参与者向协调者返回事务rollback结果信息。

两阶段提交的缺点

  1. 同步阻塞:执行过程当中,全部参与者的节点都是事务阻塞型的。当参与者占用公共资源时,其余第三方节点访问公共资源不得不处于阻塞状态。
  2. 单点故障:因为协调者的重要性,一旦协调者发生故障,参与者会一直阻塞,尤为时在第二阶段,协调者发生故障,那么全部的参与者都处于锁定事务资源的状态中,而没法继续完成事务操做。(若是是协调者挂掉,能够从新选举一个协调者,可是没法解决由于协调者宕机致使的参与者处于阻塞状态的问题)
  3. 数据不一致:在第二阶段中,当协调者想参与者发送commit请求以后,发生了局部网络异常或者在发送commit请求过程当中协调者发生了故障,这会致使只有一部分参与者接收到了commit请求。而在这部分参与者接到commit请求以后就会执行commit操做。可是其余部分未接收到commit请求的节点则没法提交事务。因而,整个分布式系统就出现了数据不一致的现象。

两阶段提交没法解决的问题

当协调者和参与者同时出现故障时,两阶段提交没法保证事务的完整性。若是调者在发出commit消息以后宕机,而惟一接收到commit消息的参与者同时也宕机了。那么即便协调者经过选举协议产生了新的协调者,这条事务的状态也是不肯定的,由于没人知道事务是否已经被提交。spa

2. 三阶段提交

第一阶段:CanCommit

协调者向参与者发送事务执行请求CanCommit,参与者若是能够提交就返回YES响应,不然就返回NO响应。中间件

第二阶段:PreCommit

协调者根据参与者反馈的结果来决定是否继续执行事务的PreCommit操做,根据协调者反馈的结果,有如下两种可能:blog

  1. 假如协调者收到参与者的反馈结果都是YES,那么就会执行PreCommit操做。
    • 发送预提交请求:协调者向参与者发送PreCommit请求,并进入Prepared阶段。
    • 事务预提交:参与者接收到PreCommit请求后,执行事务操做。
    • 响应反馈:事务操做执行成功,则返回ACK响应,而后等待协调者的下一步通知。
  2. 假若有任何一个参与者向协调者发送了NO响应,或者等待超时以后,协调者没有收到参与者的响应,那么就中断事务。
    • 发送中断请求:协调者向全部参与者发送中断请求。
    • 中断事务:参与者收到中断请求以后(或超时以后,仍未收到协调者的请求),执行事务中断操做。

第三阶段:DoCommit

  1. 执行提交
    • 发送提交请求:协调者收到ACK以后,向全部的参与者发送DoCommit请求。
    • 事务提交:参与者收到DoCommit请求以后,提交事务。
    • 响应反馈:事务提交以后,向协调者发送ACK响应。
    • 完成事务:协调者收到ACK响应以后,完成事务。
  2. 中断事务 在第二阶段,协调者没有收到参与者发送的ACK响应,那么就会执行中断事务。

3. 补偿事务(TCC)

TCC是一种比较成熟的分布式事务解决方案,可用于解决跨库操做的数据一致性问题,适用于公司内部对一致性、实时性要求较高的业务场景。其中Try、Confirm、Cancel 3个方法均由业务编码实现。其中Try操做为第一阶段,负责资源的检查和预留;Confirm操做为第二阶段,执行真正的业务操做;Cancel时执行取消(回滚)操做。 业务实现TCC服务以后,该TCC服务将做为分布式事务的其中一个资源,参与到整个分布式事务中;事务管理器分两阶段协调的TCC服务,第一阶段调用全部TCC服务的Try方法,在第二阶段执行全部TCC服务的Confirm或者Cancel方法。

实现TCC服务时注意事项

  1. 业务操做分两阶段完成 接入TCC前,业务操做只须要一步就能完成,可是在接入TCC以后,须要考虑如何将其分红2阶段完成,把资源的检查和预留放在一阶段的Try操做中进行,把真正的业务操做的执行放在二阶段的Confirm操做中进行。TCC服务要保证第一阶段Try操做成功以后,二阶段Confirm操做必定能成功。

2. 容许空回滚事务协调器在调用TCC服务的一阶段Try操做时,可能会出现由于丢包而致使的网络超时,此时事务协调器会触发二阶段回滚,调用TCC服务的Cancel操做;TCC服务在未收到Try请求的状况下收到Cancel请求,这种场景被称为空回滚;TCC服务在实现时应当容许空回滚的执行。

3. 防悬挂控制事务协调器在调用TCC服务的一阶段Try操做时,可能会出现因网络拥堵而致使的超时,此时事务协调器会触发二阶段回滚,调用TCC服务的Cancel操做;在此以后,拥堵在网络上的一阶段Try数据包被TCC服务收到,出现了二阶段Cancel请求比一阶段Try请求先执行的状况;用户在实现TCC服务时,应当容许空回滚,可是要拒绝执行空回滚以后到来的一阶段Try请求。

4. 幂等控制不管是网络数据包重传,仍是异常事务的补偿执行,都会致使TCC服务的Try、Confirm或者Cancel操做被重复执行;用户在实现TCC服务时,须要考虑幂等控制,即Try、Confirm、Cancel 执行一次和执行屡次的业务结果是同样的。

举例,假入 Bob 要向 Smith 转帐,思路大概是: 1. 首先在 Try 阶段,要先调用远程接口把 Smith 和 Bob 的钱给冻结起来。 2. 在 Confirm 阶段,执行远程调用的转帐的操做,转帐成功进行解冻。 3. 若是第2步执行成功,那么转帐成功,若是第二步执行失败,则调用远程冻结接口对应的解冻方法 (Cancel)。

4. 本地消息表

基于本地消息的最终一致性方案的最核心作法就是在执行业务操做的时候,记录一条消息数据到DB,而且消息数据的记录与业务数据的记录必须在同一个事务内完成,这是该方案的前提核心保障。在记录完成后消息数据后,后面咱们就能够经过一个定时任务到DB中去轮询状态为待发送的消息,而后将消息投递给MQ。这个过程当中可能存在消息投递失败的可能,此时就依靠 重试机制 来保证,直到成功收到MQ的ACK确认以后,再将消息状态更新或者消息清除;然后面消息的消费失败的话,则依赖MQ自己的重试来完成,其最后作到两边系统数据的最终一致性。基于本地消息服务的方案虽然能够作到消息的最终一致性,可是它有一个比较严重的弊端,每一个业务系统在使用该方案时,都须要在对应的业务库建立一张消息表来存储消息。

5. MQ事务消息

RocketMQ中间件可以支持一种事务消息机制,确保本地操做和发送消息的异步处理达到本地事务的结果一致。

  1. 第一阶段,生产者在执行事务以前,首先向MQ发送一个Prepare消息(消息保存在broker中,不会被消息者看到),RocketMQ可以拿到消息的地址。
  2. 第二阶段,生产者执行本地事务操做。
  3. 第三阶段,确认消息发送,经过第一阶段拿到的地址取访问消息,并修改状态。

注:若是确认消息发送失败, RocketMQ会按期扫描消息集群中的事务消息 ,若是发现了Prepare消息,它会向消息的发送者确认本地事务是否已执行成功,而后再根据业务实现的策略决定时继续发送仍是回滚(消息生产者须要设置监听)。

6. 最大努力通知

举例,订单支付以后,支付宝向商户推送支付结果,若是商户没有回复Success,支付宝会重复推送N次支付结果。

相关文章
相关标签/搜索