2PC和3PC
2PC
两阶段提交又称2PC,2PC是一个非常经典的强一致、中心化的原子提交协议。
中心化是指协议中有两类节点:一个是中心化协调者节点(coordinator)和N个参与者节点(partcipant)。
两个阶段:
-
第一阶段:投票阶段
-
第二阶段:提交/执行阶段。
举例订单服务A,需要调用支付服务B去支付,支付成功则处理购物订单为待发货状态,否则就需要将购物订单处理为失败状态。
第一个阶段:投票阶段
-
事务询问:协调者向参与者发送预处理请求,称之为prepare阶段,并开始等待参与者的响应;
-
执行本地事务:各个参与者节点执行本地事务操作,但在执行完成后并不会真正提交数据库本地事务,而是先向协调者报告说:“我这边可以处理了/我这边不能处理”。
-
参与者向协调者反馈:如果参与者成功执行了事务操作,那么就反馈给协调者Yes响应,表示事务可以执行,如果没有参与者成功执行事务,那么就反馈给协调者No响应,表示事务不可以执行。
第二个阶段:提交/执行阶段(成功流程)
-
所有的参与者反馈给协调者的信息都是Yes,那么就会执行事务提交。协调者向所有参与者节点发出Commit请求;
-
事务提交:参与者收到Commit请求之后,就会正式执行本地事务Commit操作,并在完成提交之后释放整个事务执行期间占用的事务资源。
第二个阶段:提交/执行阶段(异常流程)
-
有任何一个参与者向协调者反馈NO,则协调者向所有参与者节点发出RoollBack请求;
-
事务回滚:参与者接收到RoollBack请求后,会回滚本地事务。
2PC缺点
- 性能问题
无论是在第一阶段的过程中,还是在第二阶段,所有的参与者资源和协调者资源都是被锁住的,只有当所有节点准备完毕,事务协调者才会通知进行全局提交,
参与者进行本地事务提交后才会释放资源。这样的过程会比较漫长,对性能影响比较大。
- 丢失消息导致的数据不一致问题
数据不一致。当执行事务提交过程中,如果协调者向所有参与者发送Commit请求后,发生局部网络异常或者协调者在尚未发送完Commit请求,即出现崩溃,最终导致只有部分参与者收到、执行请求。于是整个系统将会出现数据不一致的情形;
- 单节点故障由于协调者的重要性
一旦协调者发生故障。参与者会一直阻塞下去。尤其在第二阶段,协调者发生故障,那么所有的参与者还都处于锁定事务资源的状态中,而无法继续完成事务操作。(虽然协调者挂掉,可以重新选举一个协调者,但是无法解决因为协调者宕机导致的参与者处于阻塞状态的问题)
3PC
三阶段提交协议(3PC)主要是为了解决两阶段提交协议的阻塞问题,2pc存在的问题是当协作者崩溃时,参与者不能做出最后的选择。
与两阶段提交不同的是,三阶段提交有两个改动点。
1、引入超时机制。同时在协调者和参与者中都引入超时机制。
2、在第一阶段和第二阶段中插入一个准备阶段。保证了在最后提交阶段之前各参与节点的状态是一致的。
也就是说,除了引入超时机制之外,3PC把2PC的准备阶段再次一分为二,这样三阶段提交就有CanCommit、PreCommit、DoCommit三个阶段。
-
CanCommit:协调者向参与者发送CanCommit请求,询问是否可以执行事务提交,参与者收到CanCommit请求之后,如果认为可以顺利执行事务,则返回YES否则返回NO
-
PreCommit:协调者根据CanCommit阶段参与者的反应情况,决定是否可以进行PreCommit。如果所有参与者获得的反馈都是YES,那么就执行PreCommit操作。
-
DoCommit:该阶段进行真正的事务提交。
3PC比2PC好在哪里?
-
降低同步阻塞。 在 3PC 中,第一阶段并没有让参与者直接执行事务,而是在第二阶段才会让参与者进行事务的执行。大大降低了阻塞的概率和时长。并且,在 3PC 中,如果参与者未收到协调者的消息,那么他会在等待一段时间后自动执行事务的 commit,而不是一直阻塞。
-
提升了数据一致性 2PC 中有一种情况会导致数据不一致,如在 2PC 的阶段二中,当协调者向参与者发送 commit 请求之后,发生了网络异常,只有一部分参与者接受到了 commit 请求。而在这部分参与者接到 commit 请求之后就会执行 commit 操作。但是其他部分未接到 commit 请求的机器则无法执行事务提交。于是整个分布式系统便出现了数据不一致性的现象。
这种情况在 3PC 的场景中得到了很好的解决,因为在 3PC 中,如果参与者没有收到协调者的消息时,他不会一直阻塞,过一段时间之后,他会自动执行事务。这就解决了那种协调者发出 commit 之后。