# 分布式事务

前言:分布式 CAP 理论和 BASE 理论

强一致性:任何一次读都能读到某个数据的最近一次写的数据。在任意时刻,所有节点中的数据是一样的。

弱一致性:数据更新后,如果能容忍后续的访问只能访问到部分或者全部访问不到,则是弱一致性。

最终一致性:不保证在任意时刻任意节点上的同一份数据都是相同的,但是随着时间的迁移,不同节点上的同一份数据总是在向趋同的方向变化。

# 刚性事务

CAP 中的 CP

遵循 ACID,对数据要求强一致性

# 两阶段提交

两阶段提交(2PC - Prepare & Commit)

第一阶段:准备阶段:
协调者向所有参与者发送 REQUEST-TO-PREPARE
当参与者收到 REQUEST-TO-PREPARE 消息后,向协调者发送消息 PREPARED 或者 NO ,表示事务是否准备好;如果发送的是 NO ,那么事务要回滚;

第二阶段:提交阶段:
协调者收集所有参与者的返回消息,如果所有的参与者都回复的是 PREPARED , 那么协调者向所有参与者发送 COMMIT 消息;否则,协调者向所有回复 PREPARED 的参与者发送 ABORT 消息;
参与者如果回复了 PREPARED 消息并且收到协调者发来的 COMMIT 消息,或者它收到 ABORT 消息,它将执行提交或回滚,并向协调者发送 DONE 消息以确认。

image

# 三阶段提交

三阶段提交 ( CanCommitPreCommitDoCommit )

CanCommit:协调者向所有参与者发送 CanCommit 命令,询问是否可以执行事务提交操作。如果全部响应 YES 则进入下一个阶段。

PreCommit:协调者向所有参与者发送 PreCommit 命令,询问是否可以进行事务的预提交操作,参与者接收到 PreCommit 请求后,如参与者成功的执行了事务操作,则返回 Yes 响应,进入最终 commit 阶段。一旦参与者中有向协调者发送了 No 响应,或因网络造成超时,协调者没有接到参与者的响应,协调者向所有参与者发送 abort 请求,参与者接受 abort 命令执行事务的中断。

DoCommit:在前两个阶段中所有参与者的响应反馈均是 YES 后,协调者向参与者发送 DoCommit 命令正式提交事务,如协调者没有接收到参与者发送的 ACK 响应,会向所有参与者发送 abort 请求命令,执行事务的中断。

img

3PC 是对 2PC 的一种升级优化,3PC 在 2PC 的第一阶段和第二阶段中插入一个准备阶段。保证了在最后提交阶段之前,各参与者节点的状态都一致。同时在协调者和参与者中都引入超时机制,当参与者各种原因未收到协调者的 commit 请求后,会对本地事务进行 commit,不会一直阻塞等待,解决了 2PC 的单点故障问题,但 3PC 还是没能从根本上解决数据一致性的问题。

# 柔性事务

CAP 中的 AP

遵循 BASE,允许一定时间内不同节点的数据不一致,但要求最终一致。

# 补偿事务(TCC )

TCC(Try-Confirm-Cancel)又被称补偿事务,TCC 与 2PC 的思想很相似,事务处理流程也很相似。

TCC 它的核心思想是:针对每个操作都要注册一个与其对应的 Try 和 Cancel。

Try 阶段:尝试执行,完成所有业务检查(一致性), 预留必须业务资源(准隔离性)
Confirm 阶段:确认执行真正执行业务,不作任何业务检查,只使用 Try 阶段预留的业务资源,Confirm 操作满足幂等性。要求具备幂等设计,Confirm 失败后需要进行重试。
Cancel 阶段:取消执行,释放 Try 阶段预留的业务资源 Cancel 操作满足幂等性 Cancel 阶段的异常和 Confirm 阶段异常处理方案基本上一致。

# SAGA 事务

Saga 是由一系列的本地事务构成。每一个本地事务在更新完数据库之后,会发布一条消息或者一个事件来触发 Saga 中的下一个本地事务的执行。如果一个本地事务因为某些业务规则无法满足而失败,Saga 会执行在这个失败的事务之前成功提交的所有事务的补偿操作。

# 本地消息表

核心思路是将分布式事务拆分成本地事务进行处理。

image-20240805001206162

  1. 当系统 A 被其他系统调用发生数据库表更操作,首先会更新数据库的业务表,其次会往相同数据库的消息表中插入一条数据,两个操作发生在同一个事务中
  2. 系统 A 的脚本定期轮询本地消息往 mq 中写入一条消息,如果消息发送失败会进行重试
  3. 系统 B 消费 mq 中的消息,并处理业务逻辑。如果本地事务处理失败,会在继续消费 mq 中的消息进行重试,如果业务上的失败,可以通知系统 A 进行回滚操作

本地消息表实现的条件

  1. 消费者与生成者的接口都要支持幂等
  2. 生产者需要额外的创建消息表
  3. 需要提供补偿逻辑,如果消费者业务失败,需要生产者支持回滚操作

容错机制

  1. 步骤 1 失败时,事务直接回滚
  2. 步骤 2、3 写 mq 与消费 mq 失败会进行重试
  3. 步骤 3 业务失败系统 B 向系统 A 发起事务回滚操作

实际应用:

比如订单状态变更之后,会见消息记录写入 mq,消费者消费消息,触发事件,给用户发短信等等。

# MQ 事务

实际上是对本地消息表的一个封装,将本地消息表移动到了 MQ 内部。

# 最大努力通知

最大努力通知也称为定期校对,是对 MQ 事务方案的进一步优化。它在事务主动方增加了消息校对的接口,如果事务被动方没有接收到消息,此时可以调用事务主动方提供的消息校对的接口主动获取。

在可靠消息事务中,事务主动方需要将消息发送出去,并且消息接收方成功接收,这种可靠性发送是由事务主动方保证的;

但是最大努力通知,事务主动方尽最大努力(重试,轮询....)将事务发送给事务接收方,但是仍然存在消息接收不到,此时需要事务被动方主动调用事务主动方的消息校对接口查询业务消息并消费,这种通知的可靠性是由事务被动方保证的。

最大努力通知适用于业务通知类型,例如微信交易的结果,就是通过最大努力通知方式通知各个商户,既有回调通知,也有交易查询接口。

实际应用:

支付回调,支付服务收到第三方服务支付成功通知后,先更新自己库中付款单支付状态,然后同步通知订单服务支付成功。如果此次同步通知失败,会通过异步脚步不断重试地调用订单服务的接口。

# 参考文章

掘金:https://juejin.cn/post/6844903936718012430#heading-27

小米信息技术团队:https://xiaomi-info.github.io/2020/01/02/distributed-transaction/

知乎:https://zhuanlan.zhihu.com/p/95852045

凤凰架构:https://icyfenix.cn/architect-perspective/general-architecture/transaction/distributed.html

分布式事务及实现方案: https://pdai.tech/md/arch/arch-z-transection.html

-->