一、2PC
将事务的提交过程分为 '资源准备' 和 '资源提交' 两个阶段,准备阶段所有事务参与者都预留资源的成功与否,决定了第二阶段提交或回滚。
第一阶段:准备阶段
1.协调者向所有参与者发送事务内容,询问是否可以提交事务,并等待答复。
2.各参与者执行本地事务操作,将 undo 和 redo 信息记入事务日志中(不提交事务)。
3.如参与者执行成功,给协调者反馈同意,否则反馈中止,表示事务不可以执行。
第二阶段:提交阶段
事务提交:
1.协调者向所有参与者发出正式提交的 commit 请求。
2.收到协调者的 commit 请求后,参与者正式执行事务提交操作,并释放在整个事务期间内占用的资源。
3.参与者完成事务提交后,向协调者节点发送ACK消息。
4.协调者节点收到所有参与者节点反馈的ACK消息后,完成事务。
事务回滚:
1.协调者向所有参与者发出 rollback 回滚操作的请求
2.参与者利用阶段一写入的undo信息执行回滚,并释放在整个事务期间内占用的资源
3.参与者在完成事务回滚之后,向协调者发送回滚完成的ACK消息
4.协调者收到所有参与者反馈的ACK消息后,取消事务
2PC的缺点:
1.性能问题:执行过程中,所有参与节点都是事务阻塞性的,其他节点访问公共资源就不得不处于阻塞状态,不适合高并发高性能场景。
2.可靠性问题:2PC非常依赖协调者,当协调者发生故障时,所有的参与者都处于锁定事务资源的状态中,无法继续完成事务操作。
3.数据一致性问题:在阶段二中,发生了局部网络异常或在发送请求时协调者发生了故障,导致只有一部分参与者接受到请求,整个系统出现了数据部一致性的现象。
4.二阶段无法解决的问题:协调者在发出消息后宕机,而唯一接收到这条消息的参与者同时也宕机了,这条事务的状态是不确定的。
二、3PC
1、CanCommit 准备阶段
1.事务询问:协调者向所有参与者发出 CanCommit 请求,等待所有参与者答复。
2.响应反馈:参与者收到请求后,可执行,则反馈 yes 并进入预备状态,否则反馈 no。
2、PreCommit 阶段
执行事务:
1.发送预提交请求:协调者向参与者发送 PreCommit 请求,并进入准备阶段。
2.事务预提交:参与者接收到 PreCommit 请求后,会执行本地事务操作,并将 undo 和 redo 信息记录到事务日志中(不提交事务)。
3.响应反馈:参与者成功的执行了事务操作,则返回ACK响应,同时开始等待最终指令。
中断事务:
1.发送中断请求:协调者向所有参与者发送 abort 请求。
2.中断事务:参与者收到abort请求或超时,执行事务的中断。
3、doCommit阶段
提交事务:
1.发送提交请求:协调接收到所有参与者发送的ACK响应,将从预提交状态进入到提交状态,并向所有参与者发送 doCommit 请求。
2.本地事务提交:参与者接收到doCommit请求之后,执行正式的事务提交,并释放所有事务资源。
3.响应反馈:事务提交完之后,向协调者发送ack响应。
4.完成事务:协调者接收到所有参与者的ack响应之后,完成事务。
中断事务:
1.发送中断请求:如果协调者处于工作状态,向所有参与者发出 abort 请求
2.事务回滚:参与者接收到abort请求之后,利用其在阶段二记录的undo信息来执行事务的回滚操作,并释放所有的事务资源。
3.反馈结果:参与者完成事务回滚之后,向协调者反馈ACK消息。
4.中断事务:协调者接收到参与者反馈的ACK消息之后,执行事务的中断。
4、优缺点
1.3PC设置了超时时间,减少了阻塞范围,避免了资源不释放情况。
2.数据不一致问题,当在参与者收到 preCommit 请求后等待 doCommit 指令时,协调者因为网络问题无法与参与者正常通信,会导致参与者继续提交事务,造成数据不一致。
三、TCC
分为两个阶段,业务逻辑的每个分支都需要实现 try、confirm、cancel 三个操作。
第一阶段由业务代码编排来调用Try接口进行资源预留,当所有参与者的 Try 接口都成功了,事务协调者提交事务,并调用参与者的 confirm 接口真正提交业务操作,否则调用每个参与者的 cancel 接口回滚事务。
1、第一阶段
Try,业务系统做检测并预留资源 (加锁,锁住资源)。
2、第二阶段
根据第一阶段的结果决定是执行confirm还是cancel
Confirm:执行真正的业务(执行业务,释放锁)
Cancle:是对Try阶段预留资源的释放(出问题,释放锁)
3、TCC如何保证最终一致性
1.TCC 事务机制以 Try 为中心的。Try 即使失败,仍然有 Cancel 执行撤销。
2.默认 Confirm 阶段是不出错的,也就是说只要 Try 成功,Confirm 一定成功(TCC设计之初的定义)。
3.Confirm 与 Cancel 如果失败,由TCC框架进行重试补偿。需要定时任务或人工介入。
4、TCC的注意事项
1.允许空回滚
Try 超时或者丢包,导致未Try,但执行Cancel。
2.防悬挂控制
Cancel 比 Try 先到。在 Cancel 空回滚返回成功之前,先记录该条事务 xid 或业务主键,标识已回滚过,Try执行前先检查这条事务xid或业务主键是否已经标记为回滚成功。
3.幂等控制
由于网络原因或者重试操作可能导致 Try - Confirm - Cancel 重复执行。通常可使用事务 xid 或业务主键控制。
5、TCC的优缺点
优点:
性能提升:具体业务来实现,控制资源锁的粒度变小,不会锁定整个资源。
数据最终一致性:基于 Confirm 和 Cancel 的幂等性,保证事务最终完成确认或者取消,保证数据的一致性。
可靠性:解决了 XA 协议的协调者单点故障问题,由主业务方发起并控制整个业务活动,业务活动管理器也变成多点,引入集群。
缺点:
业务耦合度较高,提高了开发成本。
四、Saga
将长事务拆分为多个本地短事务并依次正常提交,所有短事务均成功,那么分布式事务提交。
如果某个参与者失败,由 Saga 事务协调器协调根据相反顺序调用补偿操作,回滚已提交的参与者。Saga事务没有预留动作,直接提交。
1.每个 Saga 事务由一系列幂等的有序子事务(sub-transaction) Ti 组成。
2.每个 Ti 都有对应的幂等补偿动作 Ci,补偿动作用于撤销 Ti 造成的结果。
1、Saga的恢复策略
1.向后恢复(backward recovery)
撤销掉之前所有成功的子事务,使得整个 Saga 的执行结果撤销。
2.向前恢复(forward recovery)
方式用于必须成功的场景,对于执行不通过的事务,会尝试重试事务,不需要补偿。
2、Saga事务的实现方式
1.命令协调(Order Orchestrator)
以命令/回复的方式与每项服务进行通信,全权负责告诉每个参与者什么时候该做什么。
2.事件编排(Event Choreographyo)
每个服务产生自己的时间并监听其他服务的事件来决定是否应采取行动。
五、本地消息表
将分布式事务拆分成本地事务进行处理,有两种角色:事务主动方和事务被动方。通过本地事务保证数据业务操作和消息的一致性,然后通过定时任务将消息发送至消息中间件,待确认消息发送给消费方成功再将消息删除。
1.事务主动方在同一个本地事务中处理业务和写消息表。
2.事务主动方通过消息中间件,通知事务被动方处理事务。中间件可以基于 Kafka、RocketMQ。
3.事务被动方通过消息中间件,通知事务主动方事务已处理的消息。
4.事务主动方接收中间件的消息,更新消息表的状态为已处理。
六、MQ事务消息
基于MQ的分布式事务方案本质上是对本地消息表的封装,整体流程与本地消息表一致,不同的就是将本地消息表存在了MQ内部,而不是业务数据库中。
七、最大努力通知
在事务主动方增加了消息校对的接口,事务被动方没收到消息时可调事务主动方提供的消息校对的接口主动获取。
八、总结
2PC/3PC:依赖于数据库,提供强一致性和强事务性,延迟较高,适合传统的单体应用,在同一方法中存在跨库操作的情况。不适合高并发和高性能要求的场景。
TCC:适用于执行时间确定且较短,实时性高,对数据一致性高,互联网金融企业最核心的三个服务:交易、支付、账务。
本地消息表/MQ 事务:适于事务中参与方支持操作幂等,对一致性要求不高。事务涉及的参与方、参与环节较少,业务上有对账/校验系统兜底,能容忍数据不一致到一个人工检查周期。
Saga 事务:不能保证隔离性,要在业务层控制并发,适于并发操作同一资源较少的情况。Saga适于补偿动作容易处理的场景。