目前大部分的互联网公司在设计总体架构的时候,都会按照业务模块,将系统拆分红不少小系统,例如订单系统、卡券系统、支付系统等等,简单来讲,就是分而治之,这样每一个人能够专一维护本身的代码。而后不一样的小系统本身开发、测试和上线,都不会跟别人耦合在一块儿,能够本身独立进行,很是的方便,大大简化了大规模系统的开发成本。 数据库
然而有了多个子系统以后,分布式事务应该怎么来实现?下面就介绍一下分布式事务的几种实现方式。网络
分布式事务指的是一个请求在多个系统的调用链当中如何确保数据一致。例如,一个支付请求,支付成功后,会回调请求订单系统修改订单状态,会回调请求卡券系统修改卡券状态。那么若是回调请求由于网络缘由丢失了,可能出现用户支付了,订单仍然显示为未支付状态,卡券仍然在冻结中,没法置为已使用状态。架构
2PC是很是经典的强一致、中心化的原子提交协议,协议中定义了两类节点:一个中心化协调者节点和多个参与者节点。2PC分为两个阶段:分布式
准备阶段:性能
一、协调者向全部参与者发送事务内容,询问是否能够提交事务,并等待全部参与者答复。测试
二、各参与者执行事务操做,将Undo和Redo信息记入事务日志中(但不提交事务)。设计
三、如参与者执行成功,给协调者反馈YES,便可以提交;如执行失败,给协调者反馈NO,即不可提交。日志
提交阶段:队列
(全部参与者均反馈YES)事务
一、协调者向全部参与者发出正式提交事务的请求(即Commit请求)。
二、参与者执行Commit请求,并释放整个事务期间占用的资源。
三、各参与者向协调者反馈Ack完成的消息。
四、协调者收到全部参与者反馈的Ack消息后,即完成事务提交。
(任何一个参与者反馈NO)
一、协调者向全部参与者发出回滚请求(即Rollback请求)。
二、参与者使用阶段1中的Undo信息执行回滚操做,并释放整个事务期间占用的资源。
三、各参与者向协调者反馈Ack完成的消息。
四、协调者收到全部参与者反馈的Ack消息后,即完成事务中断。
2PC两阶段提交过程当中会遇到一些问题:
一、性能问题。从流程上咱们能够看得出,其最大缺点就在于它的执行过程当中间,节点都处于阻塞状态。各个操做数据库的节点此时都占用着数据库资源,只有当全部节点准备完毕,事务协调者才会通知进行全局提交,参与者进行本地事务提交后才会释放资源。这样的过程会比较漫长,对性能影响比较大。
二、协调者单点故障问题。事务协调者是整个XA模型的核心,一旦事务协调者节点挂掉,会致使参与者收不到提交或回滚的通知,从而致使参与者节点始终处于事务没法完成的中间状态。
三、丢失消息致使的数据不一致问题。在第二个阶段,若是发生局部网络问题,一部分事务参与者收到了提交消息,另外一部分事务参与者没收到提交消息,那么就会致使节点间数据的不一致问题。
2PC的改进版本,其在两阶段提交的基础上增长了CanCommit阶段,并引入了超时机制。一旦事务参与者迟迟没有收到协调者的Commit请求,就会自动进行本地commit,这样相对有效地解决了协调者单点故障的问题。。
阶段1:CanCommit
一、协调者向全部参与者发出包含事务内容的CanCommit请求,询问是否能够提交事务,并等待全部参与者答复。
二、参与者收到CanCommit请求后,若是认为能够执行事务操做,则反馈YES并进入预备状态,不然反馈NO。
阶段2:PreCommit
事务预提交:(全部参与者均反馈YES时)
一、协调者向全部参与者发出PreCommit请求,进入准备阶段。
二、参与者收到PreCommit请求后,执行事务操做,将Undo和Redo信息记入事务日志中(但不提交事务)。
三、各参与者向协调者反馈Ack响应或No响应,并等待最终指令。
中断事务:(任何一个参与者反馈NO,或者等待超时后协调者尚没法收到全部参与者的反馈时)
一、协调者向全部参与者发出abort请求。
二、不管收到协调者发出的abort请求,或者在等待协调者请求过程当中出现超时,参与者均会中断事务。
阶段3:do Commit
提交事务:(全部参与者均反馈Ack响应时)
一、若是协调者处于工做状态,则向全部参与者发出do Commit请求。
二、参与者收到do Commit请求后,会正式执行事务提交,并释放整个事务期间占用的资源。
三、各参与者向协调者反馈Ack完成的消息。
四、协调者收到全部参与者反馈的Ack消息后,即完成事务提交。
中断事务:(任何一个参与者反馈NO,或者等待超时后协调者尚没法收到全部参与者的反馈时)
一、若是协调者处于工做状态,向全部参与者发出abort请求。
二、参与者使用阶段1中的Undo信息执行回滚操做,并释放整个事务期间占用的资源。
三、各参与者向协调者反馈Ack完成的消息。
四、协调者收到全部参与者反馈的Ack消息后,即完成事务中断。
TCC 将事务提交分为 Try - Confirm - Cancel 3个操做。
一、Try:预留业务资源/数据效验
二、Confirm:确认执行业务操做
三、Cancel:取消执行业务操做
TCC事务处理流程和 2PC 二阶段提交相似,不过 2PC一般都是在跨库的DB层面,而TCC本质就是一个应用层面的2PC。
基于消息实现的事务适用于分布式事务的提交或回滚只取决于事务发起方的业务需求,其余数据源的数据变动跟随发起方进行的业务场景。
基于消息实现的事务并不能解决全部的业务场景,例如如下场景:某笔订单完成时,同时扣掉用户的现金。
这里事务发起方是管理订单库的服务,但对整个事务是否提交并不能只由订单服务决定,由于还要确保用户有足够的钱,才能完成这笔交易,而这个信息在管理现金的服务里。这里咱们能够引入基于补偿实现的事务,其流程以下:
以上这个是正常成功的流程,异常流程须要回滚的话,将额外发送远程调用到现金服务以加上以前扣掉的金额。
以上流程比基于消息队列实现的事务的流程要复杂,同时开发的工做量也更多:
能够看到,该事务流程相对于基于消息实现的分布式事务更为复杂,须要额外开发相关的业务回滚方法,也失去了服务间流量削峰填谷的功能。但其仅仅只比基于消息的事务复杂多一点,若不能使用基于消息队列的最终一致性事务,那么能够优先考虑使用基于补偿的事务形态。
其适用于参与者较少,单个本地事务执行时间较少,而且参与者自身可用性很高的场景,不然,其极可能致使性能降低严重。