2PC又称两阶段提交协议。
2PC是一个非常经典的强一致、中心化的原子提交协议。
它执行过程中需要协调者、参与者两种角色,一个协调者中心化节点,N个参与者节点共同合作完成分布式事务操作。
下面以一个订单支付的场景案例进行对2PC协议的解析。
支付成功后,需要修改订单状态为支付完成,否则修改状态为支付失败。
2PC协议主要分为两个阶段:投票阶段、提交/执行阶段
第一阶段:投票
- 首先协调者收到请求后向参与者(订单服务)和参与者(支付服务)发出prepare预处理指令,通知它们进入2PC处理逻辑,并等待它们的响应。
- 订单服务修改订单状态为支付完成,但不commit,支付服务开始进行支付动态,但不commit。
- 支付服务支付成功后向协调者返回Yes,否则返回No,订单服务修改订单状态成功后向协调者返回Yes,否则返回No。
第二阶段:提交/执行(成功)
- 订单服务和支付服务在上一阶段都返回Yes后,协调者会向两个参与者发送commit指令。
- 订单服务和支付服务收到commit指令后会执行commit操作,至此,完成了整个事务操作。
第二阶段:提交/执行(失败)
- 在投票阶段如果参与者有其中一个返回的是No的话,那么协调者会向两个参与者发送rollback指令,通知两个参与者都回滚。
- 订单服务和支付服务收到rollback指令后,都会执行rollback。
上述就是2PC协议的整体执行流程,但是也存在非常明显的缺点。
2PC缺点
- 性能问题
在2PC执行的整个过程中,参与者都需要等其他参与者执行完并向协调者响应Yes或No后才能执行commit/rollback操作,在等待的过程中资源是被锁定的,只有执行完commit/rollback操作才会释放,显而易见性能是低下的。 - 单点问题
a. 协调者单点
(1)如果协调者在发送完prepare指令后宕机,则在参与者执行完相应逻辑后,回复Yes/No后,则再也无法收到协调者的指令,会一直等待,直到超时。
(2)如果协调者在发送完commit/rollback指令后宕机,如果所有参与者都收到了commit/rollback指令影响不大,但是如果只有部分参与者收到了commit指令,那么就会导致数据不一致,如果只有部分参与者收到了rollback指令,这种情况不会导致数据不一致,因为未收到rollback指令的参与者会在等待超时后自动rollback。
解决方案
(1)协调者冗余,设置协调者备份,当协调者挂掉后,可启用备份协调者,原协调者记录日志,备用协调者可在原协调者宕机后,根据日志继续完成操作。
(2)在协调者宕机后,可在参与者中重新选举一个新的协调者
b. 参与者单点
(1) 如果参与者在收到prepare指令后,突然宕机,那么它就无法向协调者响应Yes/No,协调者会一直等待直到超时,最后向其他参与者发送rollback指令,进行回滚。
(2) 如果参与者在执行完相应逻辑向协调者响应Yes/No之前,与i会发生相同的现象。
(3) 如果参与者在收到commit指令后宕机,那么就会出现数据不一致的情况,因为其他参与者收到commit指令后进行commit操作。
(4) 如果参与者在收到rollback指令后宕机,不会出现数据不一致的情况,数据库会在事务超时后自动回滚。
欢迎关注公众号