.Net Core with 微服务 – 分布式事务 – 2PC、3PC

  • A+
所属分类:.NET技术
摘要

最近比较忙,好久没更新了。这次我们来聊一聊分布式事务。
在微服务体系下,我们的应用被分割成多个服务,每个服务都配置一个数据库。如果我们的服务划分的不够完美,那么为了完成业务会出现非常多的跨库事务。即使按照 DDD 的原则来切分服务还是免不了有的业务场景需要多个业务同时提交成功或者同时回滚的场景。比如会员使用积分下订单这个场景,那么会员服务的积分扣减需要跟订单下单成功同时完成。如果下单成功,但是扣减积分接口失败,那么就会造成数据的不一致性。这个时候我们就需要使用分布式事务来保证数据的一致性。
由于分布式事务要介绍的东西比较多,这一篇只介绍 2PC、3PC 的基本概念,所以 .net 相关的内容大概也只会出现在标题上一次,笑哭。

最近比较忙,好久没更新了。这次我们来聊一聊分布式事务。
在微服务体系下,我们的应用被分割成多个服务,每个服务都配置一个数据库。如果我们的服务划分的不够完美,那么为了完成业务会出现非常多的跨库事务。即使按照 DDD 的原则来切分服务还是免不了有的业务场景需要多个业务同时提交成功或者同时回滚的场景。比如会员使用积分下订单这个场景,那么会员服务的积分扣减需要跟订单下单成功同时完成。如果下单成功,但是扣减积分接口失败,那么就会造成数据的不一致性。这个时候我们就需要使用分布式事务来保证数据的一致性。
由于分布式事务要介绍的东西比较多,这一篇只介绍 2PC、3PC 的基本概念,所以 .net 相关的内容大概也只会出现在标题上一次,笑哭。

什么是 2PC

2PC 既 Two-phase Commit ,中文翻译为二阶段提交。2PC 要求每个事务的参与方都把一个事务抽象成2个阶段。下面大概分析下 2PC 事务的流程。
首先提出2个概念:

  1. 参与方
    分布式事务中所有需要同时进入事务的业务方。
  2. 协调器
    分布式环境下为了对多个事务参与方进行统一的调度管理,我们需要一个调度器。

阶段一

事务开始后,协调器下达事务开始的命令,每个参与方收到命令后开始执行准备阶段(Prepare phase),所谓准备阶段就是执行本地事物,这个时候资源被锁定,但是不进行提交。如果这个阶段没有发生异常,那么参与方会通知协调器“执行成功”。如果某个参与方在这个阶段失败了,那么同样通知协调器“执行失败”,协调器会给所有参与方发布回滚的命令。参与方在收到“回滚”命令后执行回滚操作。

阶段二

如果所有的参与方在阶段一全部响应成功,那么协调器就会给每个参与方发布执行提交操作的命令。参与方收到提交命令后开始尝试进行事务提交。如果事务提交成功,参与方会通知协调器“提交成功”。待到所有的参与方全部回复“提交成功”,那么本次事务成功执行。
.Net Core with 微服务 - 分布式事务 - 2PC、3PC
到这里我们可以看到 2PC 模型跟数据库的事务模型是高度契合的,所以 2PC 经常用来把多个数据库事物包装成一个分布式事务的场景。事实上大多数数据库如:oracle,mysql等自己已经实现了基于XA协议的2PC 事务。

2PC 的问题

  1. 在一阶段,假设参与方A执行事务成功并通知了协调器,参与方B执行失败,由于网络的问题一直无法上报给协调器,这个时候会造成参与方A事务一直是等待提交状态,阻塞整个业务。这个时候就需要引入超时机制,在一定时间内没收到协调器的指令后直接回滚事务。
  2. 在一阶段,假设参与方A执行事务成功并通知了协调器,参与方B执行成功,由于网络的问题一直无法上报给协调器,这个时候会造成参与方A、参与方B事务一直是等待提交状态,阻塞整个业务。这个时候不光在参与方A一侧需要引入超时机制,在参与方B同样需要进入超时机制来自动回滚事务。
  3. 在二阶段,如果参与方A提交成功,参与方B因为某些原因提交失败,或者是服务器宕机或者是网络原因B一致没有收到提交的指令,这个时候就会造成数据不一致,这种情况 2PC 几乎没有补偿能力,只能依靠后期手动修复数据。
  4. 如果协调器在一阶段中间挂了,那么跟以上1、2情况类似,需要通过超时机制来补偿。
  5. 如果协调器在二阶段中间挂了,比如只给参与方A发送了提交请求,那么就会造成以上问题3类似的问题,造成数据不一致。
  6. 2PC 因为依赖数据库本地事务,我们知道事务一旦开启就会阻塞后面的业务执行。所以该方法在并发高的情况下会有比较大的性能问题。而且他所阻塞的时间远远高于单机事务,因为它所耗的时间取决于执行时间最长的那个参与方所执行的事务。

3PC

由于 2PC 的众多问题,又有人发明了 3PC 事务。
3PC 事务是对 2PC 的一次改进:

  1. 首先引入了超时机制避免事务长时间阻塞。
  2. 3PC 在 2PC 的 Prepare phase 阶段之前又加入了一个阶段叫做 CanCommit 阶段。现在3个阶段分别是:CanCommit、PreCommit、DoCommit 。后两个阶段大致可以映射到 2PC 的一阶段跟二阶段。那么CanCommit 阶段是干嘛的呢?CanCommit 只是一次预检,协调器先问一下各个参与者是否可以进行事务,同时也校验一下当前的网络是否正常,参与者服务器有没有宕机。经过这一次校验后,至少可以比 2PC 安全一点,减少因为当前网络故障服务宕机带来的故障的概率。但是 3PC 任然无法完全解决问题,在 DoCommit 命令发布后,依然有可能部分参与者提交成功,部分失败,2PC 数据不一致的问题 3PC 依然无法避免。

总结

以上简单介绍了 2PC、3PC 分布式事务的原理。我们可以看到 2PC 在理想情况下是可以保证数据一致性的。但是在复杂的生产环境下服务器宕机、网络故障的情况时有发生,最终导致数据的不一致,并且 2PC 的性能也差强人意。3PC 虽然改进了 2PC 的一些缺点,但是仍然没有解决掉最致命的数据不一致的问题、以及性能的问题。所以 2PC、3PC 并不是分布式事务的首选方案。那么下期我们将继续这个话题,继续介绍 TCC 分布式事务。

相关文章

NET Core with 微服务 - 什么是微服务
.Net Core with 微服务 - 架构图
.Net Core with 微服务 - Ocelot 网关
.Net Core with 微服务 - Consul 注册中心
.Net Core with 微服务 - Seq 日志聚合
.Net Core with 微服务 - Elastic APM
.Net Core with 微服务 - Consul 配置中心
.Net Core with 微服务 - Polly 熔断降级

关注我的公众号一起玩转技术

.Net Core with 微服务 - 分布式事务 - 2PC、3PC