分布式事务解决方案

2018/05/05 Distributed 共 3753 字,约 11 分钟
山川尽美
  1. 单体应用拆分为分布式系统后,进程间的通讯机制和故障处理措施变的更加复杂

    方案:

    随着 RPC 框架的成熟,该问题已得到解决

    • dubbo 可以支持多种通讯协议
    • springcloud 可以非常好的支持 restful 调用
  2. 系统微服务化后,一个看似简单的功能,内部可能需要调用多个服务并操作多个数据库实现,服务调用的==分布式事务==问题变的非常突出

    方案:

芋道源码 :

​ > 1. tcc :tcc-transaction ​ > 2. mq :rocketmq ,需要等 4.3 版;目前版本是阉割的。 ​ > 3. bed :sharding-jdbc ​ > 4. xa :mycat 弱 xa ;目前版本性能有问题。

  1. 微服务数量众多,其测试、部署、监控等都变的更加困难

    方案:

    随着 docker、devops 技术的发展以及各公有云 paas 平台自动化运维工具的推出,微服务的测试、部署与运维会变得越来越容易

分布式事务

分布式事务是指会涉及到操作==多个数据库==的事务。其实就是将对同一库事务的概念扩大到了对多个库的事务。目的是为了保证分布式系统中的==数据一致性==。

在分布式系统中,各个节点之间在物理上相互独立,通过网络进行沟通和协调。由于存在事务机制,可以保证每个独立节点上的数据操作可以满足 ACID。但是,相互独立的节点之间无法准确的知道其他节点中的事务执行情况。所以从理论上讲,两台机器理论上无法达到一致的状态。如果想让分布式部署的多台机器中的数据保持一致性,那么就要保证在所有节点的数据写操作,要不全部都执行,要么全部的都不执行。但是,一台机器在执行本地事务的时候无法知道其他机器中的本地事务的执行结果。所以他也就不知道本次事务到底应该 commit 还是 roolback。所以,常规的解决办法就是引入一个“==协调者==”的组件来统一调度所有分布式节点的执行。

基于 XA 协议的两阶段(2PC)提交方案

交易中间件与数据库通过 XA 接口规范,使用两阶段提交来完成一个全局事务,XA 规范的基础是两阶段提交协议。

  1. 第一阶段是表决阶段,所有参与者都将本地事务能否成功的信息反馈发给协调者;
  2. 第二阶段是执行阶段,协调者根据所有参与者的反馈,通知所有参与者,步调一致地在所有分支上提交或者回滚。 image

两阶段提交方案应用非常广泛,几乎所有商业 OLTP 数据库都支持 XA 协议。

缺点: ​ 两阶段提交方案存在==锁定资源时间长(同步阻塞)==,==对性能影响很大==,、==单点==、==脑裂==等问题基本不适合解决微服务事务问题。

  1. 同步阻塞问题: 执行过程中,所有参与节点都是事务阻塞型的。当参与者占有公共资源时,其他第三方节点访问公共资源不得不处于阻塞状态。
  2. 单点故障: 由于协调者的重要性,一旦协调者发生故障。参与者会一直阻塞下去。尤其在第二阶段,协调者发生故障,那么所有的参与者还都处于锁定事务资源的状态中,而无法继续完成事务操作。(如果是协调者挂掉,可以重新选举一个协调者,但是无法解决因为协调者宕机导致的参与者处于阻塞状态的问题)
  3. 数据不一致: 在二阶段提交的阶段二中,当协调者向参与者发送 commit 请求之后,发生了局部网络异常或者在发送 commit 请求过程中协调者发生了故障,这会导致只有一部分参与者接受到了 commit 请求。而在这部分参与者接到 commit 请求之后就会执行 commit 操作。但是其他部分未接到 commit 请求的机器则无法执行事务提交。于是整个分布式系统便出现了数据不一致性的现象。
  4. 二阶段无法解决的问题: 协调者在发出 commit 消息之后宕机,而唯一接收到这条消息的参与者同时也宕机了。那么即使协调者通过选举协议产生了新的协调者,这条事务的状态也是不确定的,没人知道事务是否已经被提交。

三阶段提交(3PC)

与两阶段提交不同之处在于

  1. 引入超时机制。同时在协调者和参与者中都引入超时机制。
  2. 在第一阶段和第二阶段中插入一个准备阶段。保证了在最后提交阶段之前各参与节点的状态是一致的。

也就是说,除了引入超时机制之外,3PC 把 2PC 的准备阶段再次一分为二,这样三阶段提交就有CanCommitPreCommitDoCommit三个阶段。

  • CanCommit 阶段: 协调者向参与者发送 commit 请求,参与者如果可以提交就返回 Yes 响应,否则返回 No 响应。

    这里有 2 步:

    • ==协调者询问参与者==:协调者向参与者发送 CanCommit 请求询问是否可以执行事务,然后等待参与者响应。
    • ==参与者响应协调者==:正常情况下,参与者若可以顺利执行事务,则返回 Yes,并进入预备状态,否则返回 No。
  • PreCommit 阶段:

    协调者根据参与者的反应情况来决定是否可以执行事务的 PreCommit 操作。有两种情况:

    假如协调者从所有的参与者获得的反馈都是 Yes 响应,那么就会执行事务的预执行。

    1. ==发送预提交请求== 协调者向参与者发送 PreCommit 请求,并进入 Prepared 阶段。
    2. ==事务预提交== 参与者接收到 PreCommit 请求后,会执行事务操作,并将 undo 和 redo 信息记录到事务日志中。
    3. ==响应反馈== 如果参与者成功的执行了事务操作,则返回 ACK 响应,同时开始等待最终指令。

    假如有任何一个参与者向协调者发送了 No 响应,或者等待超时之后,协调者都没有接到参与者的响应,那么就执行事务的中断。

    1. ==发送中断请求== 协调者向所有参与者发送 abort 请求。
    2. ==中断事务== 参与者收到来自协调者的 abort 请求之后(或超时之后,仍未收到协调者的请求),执行事务的中断。
  • doCommit 阶段 该阶段执行真正的事务提交,也分为两种情况:

    执行提交

    1. ==发送提交请求== 协调者接收到参与者发送的 ACK 响应,那么他将从预提交状态进入到提交状态。并向所有参与者发送 doCommit 请求。
    2. ==事务提交== 参与者接收到 doCommit 请求之后,执行正式的事务提交。并在完成事务提交之后释放所有事务资源。
    3. ==响应反馈== 事务提交完之后,向协调者发送 Ack 响应。
    4. ==完成事务== 协调者接收到所有参与者的 ack 响应之后,完成事务。

    中断事务: 协调者没有接收到参与者发送的 ACK 响应(可能是接受者发送的不是 ACK 响应,也可能响应超时),那么就会执行中断事务。

    1. ==发送中断请求== 协调者向所有参与者发送 abort 请求
    2. ==事务回滚== 参与者接收到 abort 请求之后,利用其在阶段二记录的 undo 信息来执行事务的回滚操作,并在完成回滚之后释放所有的事务资源。
    3. ==反馈结果== 参与者完成事务回滚之后,向协调者发送 ACK 消息
    4. ==中断事务== 协调者接收到参与者反馈的 ACK 消息之后,执行事务的中断。

2PC 与 3PC 的区别

相对于 2PC,3PC 主要解决的单点故障问题,并减少阻塞,因为一旦参与者无法及时收到来自协调者的信息之后,他会默认执行 commit。而不会一直持有事务资源并处于阻塞状态。但是这种机制也会导致数据一致性问题,因为,由于网络原因,协调者发送的 abort 响应没有及时被参与者接收到,那么参与者在等待超时之后执行了 commit 操作。这样就和其他接到 abort 命令并执行回滚的参与者之间存在数据不一致的情况。

TCC 方案

TCC 方案其实是两阶段提交的一种改进。其将整个业务逻辑的每个分支显式的分成了==Try==、==Confirm==、==Cancel== 三个操作。

  • Try 部分完成业务的准备工作,
  • confirm 部分完成业务的提交,
  • cancel 部分完成事务的回滚。

基本原理如下图所示。 image

  1. 事务开始时,业务应用会向事务协调器注册启动事务。
  2. 之后业务应用会调用所有服务的try接口,完成一阶段准备。
  3. 之后事务协调器会根据 try 接口返回情况,决定调用confirm接口或者cancel接口。如果接口调用失败,会进行重试。

优点:

TCC 方案让应用自己定义数据库操作的粒度,使得降低锁冲突、提高吞吐量成为可能。

不足:

  • 对应用的侵入性强。业务逻辑的每个分支都需要实现 try、confirm、cancel 三个操作,应用侵入性较强,改造成本高。
  • 实现难度较大。需要按照网络状态、系统故障等不同的失败原因实现不同的回滚策略。为了满足一致性的要求, confirm 和 cancel 接口必须实现幂等。

基于消息的最终一致性方案

消息一致性方案是通过==消息中间件==保证上、下游应用数据操作的一致性。

基本思路是: 将==本地操作和发送消息放在一个事务中==,保证本地操作和消息发送要么两者都成功或者都失败。下游应用向消息系统订阅该消息,收到消息后执行相应操作。 image

消息方案从本质上讲是将分布式事务转换为两个本地事务,然后依靠下游业务的重试机制达到最终一致性。

缺点: 基于消息的最终一致性方案==对应用侵入性也很高==,应用需要进行大量业务改造,成本较高。


参考:

文档信息

Search

    Table of Contents