分布式事务解决方案

分布式系列

第一章 分布式事务解决方案


前言

本文主要介绍常用的几种分布式事务解决方案,同时也是面试频率超高的问题。

觉得不错的同学可以加我公众号,会经常分享一些技术干货,以及热点AI和科技新闻
在这里插入图片描述


一、两阶段提交

在这里插入图片描述

1.1 准备阶段

协调者询问参与者事务是否执行成功,参与者发回事务执行结果。

1.2 提交阶段

如果事务在每个参与者上都执行成功,事务协调者发送通知让参与者提交事务;
否则,协调者发送通知让参与者回滚事务。
需要注意的是,在准备阶段,参与者执行了事务,但是还未提交。只有在提交阶段接收到协调者发来的通知后,才进行提交或者回滚。

1.3 存在的问题

  • 同步阻塞 所有事务参与者在等待其它参与者响应的时候都处于同步阻塞状态,无法进行其它操作。
  • 单点问题 协调者在 2PC 中起到非常大的作用,发生故障将会造成很大影响。特别是在阶段二发生故障,所有参与者会一直等待状态,无法完成其它操作。
  • 数据不一致 在阶段二,如果协调者只发送了部分 Commit 消息,此时网络发生异常,那么只有部分参与者接收到 Commit 消息,也就是说只有部分参与者提交了事务,使得系统数据不一致。
  • 太过保守 任意一个节点失败就会导致整个事务失败,没有完善的容错机制。

二、补偿事务(TCC)

TCC 其实就是采用的补偿机制,其核心思想是:针对每个操作,都要注册一个与其对应的确认和补偿(撤销)操作。它分为三个阶段:

  • Try 阶段主要是对业务系统做检测及资源预留
  • Confirm 阶段主要是对业务系统做确认提交,Try阶段执行成功并开始执行 Confirm阶段时,默认 Confirm阶段是不会出错的。即:只要Try成功,Confirm一定成功。
  • Cancel 阶段主要是在业务执行错误,需要回滚的状态下执行的业务取消,预留资源释放。

举个例子,假入 Bob 要向 Smith 转账,思路大概是: 我们有一个本地方法,里面依次调用
1.首先在 Try 阶段,要先调用远程接口把 Smith 和 Bob 的钱给冻结起来。
2.在 Confirm 阶段,执行远程调用的转账的操作,转账成功进行解冻。
3.如果第2步执行成功,那么转账成功,如果第二步执行失败,则调用远程冻结接口对应的解冻方法 (Cancel)。

优点: 跟2PC比起来,实现以及流程相对简单了一些,但数据的一致性比2PC也要差一些
缺点: 缺点还是比较明显的,在2,3步中都有可能失败。TCC属于应用层的一种补偿方式,所以需要程序员在实现的时候多写很多补偿的代码,在一些场景中,一些业务流程可能用TCC不太好定义及处理。

三、本地消息表(异步确保)

本地消息表与业务数据表处于同一个数据库中,这样就能利用本地事务来保证在对这两个表的操作满足事务特性,并且使用了消息队列来保证最终一致性。
1.在分布式事务操作的一方完成写业务数据的操作之后向本地消息表发送一个消息,本地事务能保证这个消息一定会被写入本地消息表中。
2.之后将本地消息表中的消息转发到 Kafka 等消息队列中,如果转发成功则将消息从本地消息表中删除,否则继续重新转发。
3.在分布式事务操作的另一方从消息队列中读取一个消息,并执行消息中的操作。

优点: 一种非常经典的实现,避免了分布式事务,实现了最终一致性。
缺点: 消息表会耦合到业务系统中,如果没有封装好的解决方案,会有很多杂活需要处理。

四、MQ 事务消息

有一些第三方的 MQ 是支持事务消息的,比如RocketMQ,他们支持事务消息的方式也是类似于采用的二阶段提交,但是市面上一些主流的MQ都是不支持事务消息的,比如 RabbitMQ 和 Kafka 都不支持。

以阿里的 RocketMQ 中间件为例,其思路大致为:
第一阶段Prepared消息,会拿到消息的地址。 第二阶段执行本地事务,第三阶段通过第一阶段拿到的地址去访问消息,并修改状态。
也就是说在业务方法内要想消息队列提交两次请求,一次发送消息和一次确认消息。如果确认消息发送失败了RocketMQ会定期扫描消息集群中的事务消息,这时候发现了Prepared消息,它会向消息发送者确认,所以生产方需要实现一个check接口,RocketMQ会根据发送端设置的策略来决定是回滚还是继续发送确认消息。这样就保证了消息发送与本地事务同时成功或同时失败。
在这里插入图片描述
优点: 实现了最终一致性,不需要依赖本地数据库事务。
缺点: 实现难度大,主流 MQ 不支持,RocketMQ 事务消息部分代码也未开源。

五、阿里开源分布式事务解决方案seata

1、AT(auto transaction)

优点:完全无侵入,无需额外写保障事务的代码
缺点

  • 非强一致性,因为在第一阶段已经提交持久化数据
  • 数据存在锁

AT 模式分为两个阶段:

  • 一阶段:执行用户SQL,事务提交(保存更新前快照before image->业务SQL->保存更新后快照after image->生成行锁防止脏读)
  • 二阶段:Seata框架自动生成
  • 提交:删除before image、删除after image、删除行锁
  • 回滚:校验脏写->利用before image 生成逆向sql->事务(数据还原->删除before image、删除after image、删除行锁)

2、TCC
缺点:业务侵入强,三个阶段的逻辑都需要自己实现
优点:相比AT没有全局行锁,性能比AT高
在这里插入图片描述

在LocalTCC模式下,可以选择开启useTCCFence=true,通过seata框架内置的tcc分支事务状态表解决TCC的三大问题:

@TwoPhaseBusinessAction(name = “local-tcc-action”, useTCCFence = true // 是否启用TCCFence

由SeataTCC框架处理TCC三大问题(幂等、悬挂、空回滚)

  • 空回滚
    • 定义:try阶段由于网络或者参与者异常等原因,实际应该更新的数据没有更新,而事务管理器判定应该回滚cancel,此时cancel不应该执行任何回滚操作,这种场景叫做空回滚
    • 解决方案:开启useTCCFence-true,seata框架会帮助解决。
    • 原理:Seata 的做法是新增一个 TCC 事务控制表,包含事务的 XID 和 BranchID 信息,在 Try 方法执行时插入一条记录,表示一阶段执行了,执行 Cancel 方法时读取这条记录,如果记录不存在,说明 Try 方法没有执行。
  • confirm、cancel幂等
    • 定义:参与者执行完第二阶段confirm或cancel后,由于网络等原因,事务管理器未接收到结果,导致重复调用第二阶段
    • 解决方案:开启useTCCFence-true,seata框架会帮助解决。
    • 原理:同样的也是在 TCC 事务控制表中增加一个记录状态的字段 status,该字段有 3 个值,分别为:
      • tried:1
      • committed:2
      • rollbacked:3
        二阶段 Confirm/Cancel 方法执行后,将状态改为 committed 或 rollbacked 状态。当重复调用二阶段 Confirm/Cancel 方法时,判断事务状态即可解决幂等问题。
  • 悬挂
    • 定义:参与者执行try时超时,导致事务管理器调用cancel,事务结束之后try操作才执行完成,锁定的资源无法释放,这种场景叫做悬挂
    • 解决方案:开启useTCCFence-true,seata框架会帮助解决。
    • 原理:在 TCC 事务控制表记录状态的字段 status 中增加一个状态:suspended:4
      当执行二阶段 Cancel 方法时,如果发现 TCC 事务控制表没有相关记录,说明二阶段 Cancel 方法优先一阶段 Try 方法执行,因此插入一条 status=4 状态的记录,当一阶段 Try 方法后面执行时,判断 status=4 ,则说明有二阶段 Cancel 已执行,并返回 false 以阻止一阶段 Try 方法执行成功。

TCC分为三个阶段:

  • Try:做业务检查和资源预留
  • Confirm:确认提交
  • Cancel:业务执行错误需要回滚的状态下执行分支事务的业务取消,预留资源释放

3、saga
Saga模式是SEATA提供的长事务解决方案,seata-saga模式是通过状态机来实现的,它使用状态图定义服务调用流程并生成json状态语言定义文件,状态图的节点可以是一个服务,也可以是补偿节点。这个生成的json由状态机引擎来驱动执行,出现异常是状态机引擎对调用成功的服务从后往前补偿,而补偿的逻辑需要由服务自己来实现。

优点

  • Saga模式非常适合用来处理时间跨度比较长的分布式事务问题。
  • 对于分布式事务参与方的完成时效性没有要求。
  • Saga模式可以在不同的阶段进行补偿操作,从而保证了数据的最终一致性。
  • Saga模式可以通过异步消息来实现,从而提高了系统的可扩展性。

缺点

  • Saga模式需要开发人员自己实现补偿操作,这增加了开发难度。
  • Saga模式需要开发人员自己实现事务状态机,这增加了开发难度。
  • Saga模式可能会导致事务执行时间过长,从而影响系统的性能。

4、XA

优点

  • 事务的强一致性,满足ACID原则。
  • 常用数据库都支持,实现简单,并且没有代码侵入

缺点

  • 性能较差,因为一阶段需要锁定数据库资源,等待二阶段结束才释放,所以性能较差
  • 依赖关系型数据库实现事务。NoSQL参与不进来

一阶段:
事务协调者通知每个事务参与者执行其本地事务
本地事务执行后暂不提交,继续持有数据库锁;向事务协调者报告事务的执行状态
二阶段:
事务协调者基于一阶段的报告来决定下一步操作
如果一阶段所有事务都成功:则通知所有事务参与者都提交事务
如果一阶段有事务执行失败:则通知所有事务参与者都回滚事务

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

romanYSX

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值