1. 2PC提交协议

分布式事务【2阶段提交协议2PC】.svg

  1. 准备阶段
    1. 协调者通知参与者做提交准备,然后进入决策投票过程
    2. 参与者收到通知后进行提交准备(执行本地事务,并将redolog,undolog写入日志),成功则告知协调者自己的投票为”同意”,否则告知投票为”取消”
  2. 提交阶段

    1. 协调者收到所有参与者的投票均为”同意”:
      1. 协调者通知参与者提交事务
      2. 参与者完成提交,并释放事务占用的资源
      3. 参与者回复”提交完成”
      4. 协调者收到所有回复”提交完成”,事务完成
    2. 存在某个参与者投票”取消”或者超时:
      1. 协调者通知参与者回滚事务
      2. 参与者执行回滚(用之前写入的undolog),并释放事务占用的资源
      3. 参与者回复”回滚完成”
      4. 协调者收到所有回复”回滚完成”,事务取消

        2PC提交协议有什么缺点?

  3. 【同步阻塞问题】 是同步阻塞协议,占用资源

  4. 【单点故障】 协调者挂了参与者会一致阻塞下去
  5. 【数据不一致】网络原因导致部分参与者未接收到提交请求,则会导致数据不一致

    2. 3PC提交协议

    分布式事务【3阶段提交协议3PC】.svg

    2PC和3PC的区别是什么?

  6. 3pc比2pc多了一个can commit阶段,减少了不必要的资源浪费。因为2pc在第一阶段会占用资源,而3pc在这个阶段不占用资源,只是校验一下sql,如果不能执行,就直接返回,减少了资源占用。

  7. 引入超时机制。同时在协调者和参与者中都引入超时机制。
    1. 2pc:只有协调者有超时机制,超时后,发送回滚指令。
    2. 3pc:协调者和参与者都有超时机制。
      1. 协调者超时机制: can commit,pre commit中,如果收不到参与者的反馈,则协调者向参与者发送中断指令
      2. 参与者超时机制: pre commit阶段,参与者进行中断; do commit阶段,参与者进行提交。|

        3. TCC(Try-Confirm-Cancel)

  1. T(Try):业务检查阶段,这阶段主要进行业务校验和检查或者资源预留;也可能是直接进行业务操作。
  2. C (Confirm):业务确认阶段,这阶段对Try阶段校验过的业务或者预留的资源进行确认。
  3. C (Cancel):业务回滚阶段,这阶段和上面的C (Confirm)是互斥的,用于释放Try阶段预留的资源或者业务。

撤销和确认操作的执行可能需要重试,因此还需要保证操作的幂等
相对于 2PC、3PC ,TCC 适用的范围更大,但是开发量也更大,毕竟都在业务上实现,而且有时候你会发现这三个方法还真不好写。

4. 消息队列+本地事件表方案

分布式事务【消息队列+本地事务表】.svg

5. 可靠消息服务方案

分布式事务【可靠消息服务】.svg

6. 最大努力通知方案

分布式事务【最大努力通知】.svg

7. SEATA

Seata的分布式事务解决方案是业务层面的解决方案,只依赖于单台数据库的事务能力。Seata框架中一个分布式事务包含3中角色:

  1. Transaction Coordinator (TC): 事务协调器,维护全局事务的运行状态,负责协调并驱动全局事务的提交或回滚。
  2. Transaction Manager (TM): 控制全局事务的边界,负责开启一个全局事务,并最终发起全局提交或全局回滚的决议。
  3. Resource Manager (RM): 控制分支事务,负责分支注册、状态汇报,并接收事务协调器的指令,驱动分支(本地)事务的提交和回滚。

其中,TM是一个分布式事务的发起者和终结者,TC负责维护分布式事务的运行状态,而RM则负责本地事务的运行。如下图所示
image.png
下面是一个分布式事务在Seata中的执行流程:

  1. TM 向 TC 申请开启一个全局事务,全局事务创建成功并生成一个全局唯一的 XID。
  2. XID 在微服务调用链路的上下文中传播。
  3. RM 向 TC 注册分支事务,接着执行这个分支事务并提交(重点:RM在第一阶段就已经执行了本地事务的提交/回滚)
  4. TM 根据 TC 中所有的分支事务的执行情况,发起全局提交或回滚决议。
  5. TC 调度 XID 下管辖的全部分支事务完成提交或回滚请求。

    为什么Seata在第一阶段就直接提交了分支事务?

    Seata能够在第一阶段直接提交事务,是因为Seata框架为每一个RM维护了一张UNDO_LOG表(这张表需要客户端自行创建),其中保存了每一次本地事务的回滚数据。因此,二阶段的回滚并不依赖于本地数据库事务的回滚,而是RM直接读取这张UNDO_LOG表,并将数据库中的数据更新为UNDO_LOG中存储的历史数据。

如果第二阶段是提交命令,那么RM事实上并不会对数据进行提交(因为一阶段已经提交了),而实发起一个异步请求删除UNDO_LOG中关于本事务的记录。

seata第一阶段提交会造成脏读吗?

存在于seata之外的数据修改seata无法进行控制,对于这种数据校验失败的回滚也不能进行覆盖性的回滚,这个决策只能交给用户。所以对于seata AT模式的一个原则是数据修改的入口都要交给seata事务控制。即:数据修改的服务都应该纳入到seata的事务控制内

seata第一阶段提交获取全局锁?

RM在同一个本地事务中执行业务SQL和UNDO_LOG数据的插入。在提交这个本地事务前,RM会向TC申请关于这条记录的全局锁。如果申请不到,则说明有其他事务也在对这条记录进行操作,因此它会在一段时间内重试,重试失败则回滚本地事务,并向TC汇报本地事务执行失败

seata全局锁何时释放?

RM在事务提交前,申请到了相关记录的全局锁,因此直接提交本地事务,并向TC汇报本地事务执行成功。此时全局锁并没有释放,全局锁的释放取决于二阶段是提交命令还是回滚命令,详细请看seata原理