随着业务的增长,需要进行分表、分库,甚至拆分应用演化成微服务。
因此一次交易需要跨库、跨服务保证每个系统中的交易要么全部成功,否则全部回滚。这里就涉及到分布式事务。

XA协议时一个基于数据库的分布式事务协议,其分为两部分:事务管理器和本地资源管理器。
事务管理器作为一个全局调度着,负责对各个本地资源管理器统一发送提交或者回滚命令。
二阶段提交和三阶段提交都是根据此协议衍生而来,Oracle和Mysql均已实现了XA接口。
除了二阶段提交和三阶段提交外还有Try Confirm Cancel (TCC)、本地消息队列等分布式事务解决方案。

二阶段提交 2PC

二阶段提交需要进行两个阶段的操作,准备阶段和提交阶段。

准备阶段就是事务管理器(协调者)分别给不同的系统发送“准备”命令,这些系统出了提交数据库事务之外的所有操作,都要在准备阶段操作完成。

提交阶段就是事务管理器(协调者)给不同的系统发送“提交”命令,每个系统提交自己的数据库事务,然后给协调者返回“提交成功”, 协调者收到所有响应以后,返回给客户端成功响应。
如果遇到异常情况提交不成功,需要做一些补偿机制来保证成功。

二阶段提交

对于二阶段提交:
如果准备阶段全部返回成功, 那么进入提交阶段,该必须保证成功。
如果准备阶段有一个失败,那么协调者通知每个系统回滚。

二阶段提交保证了原子性与隔离性。所以2PC适合对数据一致性高的场景。

缺陷:
性能低:整个事务的执行过程需要阻塞服务端线程和数据库会话。
协调者单点故障:一旦协调者宕机,就会导致事务回话一致处于等待提交阶段,直到事务超时自动回滚。
超时导致同步阻塞:当某个参与者节点通信处于超时,其余参与者都会被懂阻塞导致占用的资源不能释放。

适合场景:只有病发量不大且需要强一致的情况下才考虑使用2PC。

三阶段提交 3PC

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

三个阶段分别是can commit、Pre Commit 、Do Commit;

三阶段提交

CanCommit

类似于二阶段提交的准备阶段,协调者向所有参与者发送CanCommit命令,询问是否可以执行事务提交操作,如果可以提交就返回YES,否则返回NO。如果全部响应YES则进入下一个阶段。

PreCommit

协调者根据参与者的反应情况来决定是否可以进行事务的PreCommit操作。根据响应情况,有两种可能

所有参与者的反馈都是YES那么执行事务的预执行:
1)发送于提交请求,协调者向参与者发送Precommit请求,并进入Prepared阶段。
2)事务预提交,参与者接收到PreCommit请求后,会执行事务操作,并将undo和redo信息记录到事务日志中。
3)响应反馈:如果参与者成功的执行了事务操作,则返回ACK响应,同时开始等待最终指令。

假如任何一个参与者向协调者发送了NO响应,或者等待超时之后,协调者都没有接到参与者的响应,那么就执行事务的中断。
1)发送终端请求: 协调者向所有参与者发送abort请求。
2)中断事务:参与者收到来自协调者的abort请求之后(或者超时之后,仍未收到协调者的请求)执行事务的中断。

DoCommit

该阶段进行真正的事务提交,也分为两种情况。

执行提交

发送提交请求: 协调者接收到参与者发送的ACK响应,那么它将从预提交状态进入到提交状态。
并向所有参与者发送doCommit请求。
事务提交:参与者收到docommit请求之后,执行正式的事务提交。并在完成事务提交之后释放所有的事务资源。
响应反馈:参与者事务提交之后,向协调者发送ACK请求
完成事务: 协调者接收到所有参与者的ACK响应之后,完成事务。

中断事务

协调者没有接收到参与者发送的ACK响应(可能是参与者发送的不是ACK响应,也可能是响应超时),那么就会执行中断事务。

发送中断请求:协调者向所有参与者发送abort请求。
事务回滚:参与者接收到abort请求后,利用其在阶段二记录的undo信息来执行事务的回滚操作,并在完成回滚之后释放所有的事务资源。
反馈结果:参与者完成事务回滚之后,向协调者发送ACK信息
中断事务:协调者接收到参与者反馈的ACK消息之后,执行事务的中断操作。

三阶段提交状态图

如果已经完成了Precommit进入到Docommit阶段,有的参与者由于超时没有收到Docommit请求时,会自动提交本地事务,并且释放资源。

三阶段提交解决了二阶段提交无法释放资源的问题。
也保证了在提交事务之前所有参与者的状态都一致

补偿事务(TCC)

针对每个操作都要注册一个与其对应的确认和补偿(撤销操作)

它分为三个操作:

Try阶段

主要针对业务系统做检测以及资源预留。

Confirm阶段

确认执行业务操作。

Cancel阶段

取消执行业务操作。

TCC处理流程与2PC类似,不过2PC通常是在跨库的DB层面,TCC本质上就是一个应用层面的2PC,需要通过业务逻辑来实现。这种分布式事务的实现方式的优势在于,可以让应用自己定义数据库操作的粒度,是的降低锁冲突、提高吞吐量成为可能。

不足之处在于对应用的侵入性非常强,业务逻辑的每个分支都需要实现try、confirm、cancel三个操作。
此外其实现难度也比较大,需要按照网络状态、系统故障等不同的失败原因实现不同的回滚策略。
为了满足一致性的要求,confirm和cancel必须实现幂等。

TCC(图片来源于网络)

本地消息队列

如果只需要保证数据的最终一致性,那么可以使用消息队列来解决。