自学笔记:分布式事务

分布式事务

什么是分布式事务?

传统事务在分布式应用会产生的问题

在之前所学的事务,指的是单机的事务。但是近些年来,由于微服务的发展,应用由单体应用转变为分布式应用,原来的事务就会出实现问题。

举例:以下订单为例,假设一个分布式应用由两个服务:订单服务和库存服务。当要完成一个订单时,需要同时创建订单以及减少库存。订单数据库和库存数据库分别在两个不同的服务器。
自学笔记:分布式事务

大致步骤:

  1. 下订单,开启事务
  2. 订单数据库生成订单
  3. 远程调用库存服务:库存数据库减少库存
  4. 提交/回滚事务

当远程调用库存服务,让库存正常减少了;库存服务想在远程通知调用方,说库存服务这边一切正常,但是由于网络等原因,调用方没收到。于是调用方以为出来问题,开始回滚。最终结果就是订单并没有生成,但是库存却减少了。

因此,针对分布式应用,才有了分布式事务。
在分布式应用中一次操作由多个服务协同完成,这种一次事务操作涉及多个服务通过网络协同完成的过程称为分布式事务。

CAP理论

CAP理论是指:分布式系统在设计时只能在一致性(Consistency)可用性(Availability)、**分区容忍性(Partition Tolerance)**中满足两种,无法兼顾三种。

**一致性(Consistency):**指强一致性,在写操作完成后开始的任何读操作都必须返回该值,或者后续写操作的结果。
假设用户向一台服务器写入或修改了值A=10,那么用户从其他服务器读取A的值必须是10。这就要求服务器之间的同步要及时迅速,或者当一个服务器被修改时,其他服务器必须停用,直到完成同步。

**可用性(Availability):**可用性是指,每次向未崩溃的节点发送请求,总能保证收到响应数据(允许不是最新数据)。
多个节点,哪怕一个节点宕机了,其他节点依然可以提供服务,不影响整个集群对外提供服务。

分区容忍性(Partition Tolerance):分区容忍性就是允许系统通过网络协同工作,分区容忍性要解决由于网络分区导致数据的不完整及无法访问等问题。
a和b服务器都是正常的,可以对外提供服务。但是a与b之间由于某种原因无法沟通,导致两个服务器之间信息不同步,系统要能容忍这种情况。

在分布式系统中,P(分区容忍性)是必须的,因此CA二者只能选其一。

假如满足一致性,当服务器A发生变化,BCDEF等服务器必须与A同步,假如由于某些原因,BCDEF等服务器与A无法即使通信。为了满足一致性,这些服务器都应该停止提供服务直到与A完成同步。但是这些服务并未崩溃却停止服务,就违背了可用性原则。


Base理论

BASE是对CAP中一致性和可用性权衡的结果。由于CAP理论是残酷的三选二,而分布式系统设计中AP的应用较多,即保证分区容忍性和可用性,牺牲数据的强一致性。但是牺牲一致性并不是说完全完全放弃数据一致性,而是牺牲强一致性换取最终一致性

由此,诞生了Base理论。Basically Available(基本可用),Soft State(软状态)和Eventually Consistent(最终一致性)。

**Basically Available(基本可用):**由于一些原因,为了保证可用性,延长响应的时间;给用户返回一个降级页面。用来缓解服务的压力。

**Soft State(软状态):**允许系统中的数据存在中间状态,并认为该状态不影响系统的整体可用性,即允许系统在多个不同节点的数据副本存在数据延时。

**Eventually Consistent(最终一致性):**由于允许软状态,也就是说不同的服务器的数据可以暂时不同步,但终究要同步。不同服务器的数据最终要趋向一致。

BASE理论是对CAP中的一致性和可用性进行一个权衡的结果,理论的核心思想就是:我们无法做到强一致,但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致(Eventually Consistent)。


XA分布式事务协议

XA分布式事务协议,包含二阶段提交(2PC),三阶段提交(3PC)两种实现。

2pc(2阶段提交)

协调者:事务的发起者称
参与者:事务的执行者称

步骤:

  • 准备阶段:
    事务协调者,向所有事务参与者发送事务内容,询问是否可以提交事务,并等待参与者回复。
    事务参与者收到事务内容,开始执行事务操作,讲 undo 和 redo 信息记入事务日志中(但此时并不提交事务)。
    如果参与者执行成功,给协调者回复yes,表示可以进行事务提交。如果执行失败,给协调者回复no,表示不可提交。
  • 提交阶段:
    如果协调者收到了参与者的失败信息或超时信息,直接给所有参与者发送回滚(rollback)信息进行事务回滚,否则,发送提交(commit)信息。
    参与者根据协调者的指令执行提交或者回滚操作,释放所有事务处理过程中使用的锁资源。并回复给协调者ack,协调者收到,服务完成。

图解:
自学笔记:分布式事务

**总结:**2PC 方案实现起来简单,实际项目中使用比较少,主要因为以下问题

  • 性能问题:所有参与者在事务提交阶段处于同步阻塞状态,占用系统资源,容易导致性能瓶颈。
  • 可靠性问题:如果协调者存在单点故障问题,如果协调者出现故障,参与者将一直处于锁定状态。
  • 数据一致性问题:在阶段 2 中,如果发生局部网络问题,一部分事务参与者收到了提交消息,另一部分事务参与者没收到提交消息,那么就导致了节点之间数据的不一致。

3pc(3阶段提交)

三阶段提交是在二阶段提交上的改进版本,主要是加入了超时机制。同时在协调者和参与者中都引入超时机制。
三阶段将二阶段的准备阶段拆分为2个阶段,插入了一个preCommit阶段,以此来处理原先二阶段,参与者准备后,参与者发生崩溃或错误,导致参与者无法知晓是否提交或回滚的不确定状态所引起的延时问题。

步骤:

  • **canCommit:**协调者向参与者发送commit请求,参与者如果可以提交就返回Yes响应,否则返回No响应。注意:参与者此时不执行是事物,只是预先估计自己能不能正常执行事务操作。

  • **PreCommit:**根据canCommit阶段执行不同操作。

    1. canCommit阶段参与者均回复yes
      • 协调者向所有参与者发出 preCommit 请求,进入准备阶段。
      • 参与者收到 preCommit 请求后,执行事务操作,将 undo 和 redo 信息记入事务日志中(但不提交事务)。
      • 各参与者向协调者反馈 ack 响应或 no 响应,并等待最终指令。
    2. canCommit阶段有参与者均回复no,或者未收到某参与者的回复
      • 协调者向所有参与者发出 abort 请求。
      • 参与收到协调者发出的 abort 请求,或者在等待协调者请求过程中出现超时,参与者均会中断事务。
  • **doCommit:**执行真正的事务提交,根据PreCommit阶段执行不同操作。

    1. PreCommit阶段参与者的回复全部都是yes。
      • 如果协调者处于工作状态,则向所有参与者发出 do Commit 请求。
      • 参与者收到 do Commit 请求后,或者在等待协调者请求过程中出现超时,会正式执行事务提交,并释放整个事务期间占用的资源。
      • 各参与者向协调者反馈 ack 完成的消息。
      • 协调者收到所有参与者反馈的 ack 消息后,即完成事务提交。
    2. PreCommit阶段有参与者均回复no,或者未收到某参与者的回复
      • 如果协调者处于工作状态,向所有参与者发出 abort 请求。
      • 参与者使用阶段 1 中的 undo 信息执行回滚操作,并释放整个事务期间占用的资源。
      • 各参与者向协调者反馈 ack 完成的消息。
      • 协调者收到所有参与者反馈的 ack 消息后,即完成事务中断。

    doCommit阶段,如果参与者迟迟未收到协调者的指令,则默认提交事务。

总结:

  • 优点:相比二阶段提交,三阶段提交降低了阻塞范围,在等待超时后协调者或参与者会中断事务。避免了协调者单点问题,阶段 3 中协调者出现问题时,参与者会继续提交事务。
  • 缺点:数据不一致问题依然存在,当在参与者收到 preCommit 请求后等待 do commite 指令时,此时如果协调者请求中断事务,而协调者无法与参与者正常通信,会导致参与者继续提交事务,造成数据不一致。

TCC

TCC(Try-Confirm-Cancel),是一种应用层面侵入业务的两阶段提交。是目前最火的一种柔性事务方案,其核心思想是:针对每个操作,都要注册一个与其对应的确认和补偿(撤销)操作。

步骤:

  1. 第一阶段
    • **try(尝试):**主要是对业务系统做检测及资源预留 (加锁,锁住资源)
  2. 第二阶段:根据try阶段的结果,决定执行Confirm(确认)或Cancel(取消)。
    • **Confirm(确认):**执行真正的业务执行业务,释放锁。
      当Try正常执行时,Confirm 也基本能正常执行。Confirm 阶段也可以看成是对 Try 阶段的一个补充,Try+Confirm 一起组成了一个完整的业务逻辑。
    • **Cancel(取消):**是预留资源的取消,出问题,释放锁。

**案例:**以电商下单为例进行方案解析,这里把整个过程简单分为扣减库存,订单创建 2 个步骤,库存服务和订单服务分别在不同的服务器节点上。

假设商品库存为 100,购买数量为 2,这里检查和更新库存的同时,冻结用户购买数量的库存,同时创建订单,订单状态为待确认。

  1. Try 阶段

    TCC 机制中的 Try 仅是一个初步操作,它和后续的确认一起才能真正构成一个完整的业务逻辑,这个阶段主要完成:

    • 完成所有业务检查( 一致性 ) 。

    • 预留必须业务资源( 准隔离性 ) 。

    • Try 尝试执行业务。

      自学笔记:分布式事务

  2. Confirm / Cancel 阶段

    根据 Try 阶段服务是否全部正常执行,继续执行确认操作(Confirm)或取消操作(Cancel)。

    Confirm 和 Cancel 操作满足幂等性,如果 Confirm 或 Cancel 操作执行失败,将会不断重试直到执行完成。

    • Confirm:当 Try 阶段服务全部正常执行, 执行确认业务逻辑操作
      自学笔记:分布式事务

    • Cancel:当 Try 阶段存在服务执行失败, 进入 Cancel 阶段
      自学笔记:分布式事务

      Cancel 取消执行,释放 Try 阶段预留的业务资源,上面的例子中,Cancel 操作会把冻结的库存释放,并更新订单状态为取消。

总结:
TCC 事务机制相比于上面介绍的 XA 事务机制,有以下优点:

  • 性能提升:具体业务来实现控制资源锁的粒度变小,不会锁定整个资源。
  • 数据最终一致性:基于 Confirm 和 Cancel 的幂等性,保证事务最终完成确认或者取消,保证数据的一致性。
  • 可靠性:解决了 XA 协议的协调者单点故障问题,由主业务方发起并控制整个业务活动,业务活动管理器也变成多点,引入集群。

缺点: TCC 的 Try、Confirm 和 Cancel 操作功能要按具体业务来实现,业务耦合度较高,提高了开发成本。


本地消息表