一篇入门分布式事务

前言

在接触分布式事务之前就听讲过好几种分布式解决方案,今天来剖析一下各方案的区别。最常用的应该是MQ的最终一致性的消息事务,在用MQ的过程中,你可能已经实现过分布式事务了。

初识分布式事务

分布式事务是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上。

这里我们举个例子:例如在大型电商系统中,下单接口通常会扣减库存、减去优惠、生成订单 id, 而订单服务与库存、优惠、订单 id 都是不同的服务,下单接口的成功与否,不仅取决于本地的 db 操作,而且依赖第三方系统的结果,这时候分布式事务就保证这些操作要么全部成功,要么全部失败。

本质上来说,分布式事务就是为了保证不同数据库的数据一致性

2PC

两阶段提交(Two-phase Commit,2PC),通过引入协调者(Coordinator)来协调参与者的行为,并最终决定这些参与者是否要真正执行事务。有以下两个阶段

  • 首先是准备阶段,Coordinator会向所有事务参与者询问资源是否就绪,即有没有线程正在占用,是不是还处在其他事物当中,如果资源就绪了,就进行锁定,即表明这个资源正在参与分布式事务的流程中,将资源锁定不允许修改
  • 然后到提交阶段,Coordinator向所有事务参与者发送提交信号,所有事务参与者就会进行事务的提交,有一个参与者失败则进行回滚
  • 2PC的优点

    尽量保证了数据的强一致,实现成本较低,在各大主流数据库都有自己实现,对于MySQL是从5.5开始支持。

  • 2PC的缺点

    1. **同步阻塞:**2PC的缺点也是显而易见的,它是一个强一致性的同步阻塞协议,事务执行过程中需要将所需资源全部锁定。

    2. **单点问题:**协调者在整个流程中扮演的角色很关键,如果其宕机,比如在第一阶段已经完成,在第二阶段正准备提交的时候事务管理器宕机,参与者就会一直阻塞,导致数据库无法使用。

    3. **数据不一致:**两阶段提交协议虽然为分布式数据强一致性所设计,**但仍然存在数据不一致性的可能,**比如在第二阶段中,假设协调者发出了事务commit的通知,但是因为网络问题该通知仅被一部分参与者所收到并执行了commit操作,其余的参与者则因为没有收到通知一直处于阻塞状态,这时候就产生了数据的不一致性。

3PC

3PC是2PC的一种改进版本,为了解决两阶段提交协议的阻塞问题即2PC的缺点,当协调者崩溃时,参与者不能做出最后的选择,就会一直保持阻塞锁定资源的状态。

2PC中只有协调者有超时机制,3PC在协调者和参与者中都引入了超时机制,协调者出现故障后,参与者就不会一直阻塞了。

3PC在2PC的第一阶段和第二阶段中又插入了一个准备阶段 ,保证了在最后提交阶段之前各参与节点的状态是一致的。

虽然3PC用超时机制,解决了协调者故障后参与者的阻塞问题,但与此同时却多了一次网络通信,性能上反而变得更差了(不推荐)

TCC

TCC(Try-Confirm-Cancel)分为以下三个阶段:

  1. Try阶段:尝试执行,完成所有业务检查(一致性),预留必须业务资源(准隔离性)
  2. Confirm阶段:确认执行真正执行业务,不作任何业务检查,只使用Try阶段预留的业务资源,Confirm操作满足幂等性。要求具备幂等设计,Confirm失败后需要进行重试。
  3. Cancel阶段:取消执行,释放Try阶段预留的业务资源 Cancel操作满足幂等性Cancel阶段的异常和Confirm阶段异常处理方案基本上一致。

举例子:

如果你用100元买了一个面包。 Try阶段:你需要向你的钱包检查是否够100元并锁住这100元,面包也是一样的。

如果有一个失败,则进行cancel(释放这100元和这个面包),如果cancel失败不论什么失败都进行重试cancel,所以需要保持幂等。

如果都成功,则进行confirm,确认这100元扣,和这个面包被卖,如果confirm失败无论什么失败则重试(会依靠活动日志进行重试)

缺点:

在第2,3步中都有可能失败。TCC属于应用层的一种补偿方式,所以需要程序员在实现的时候多写很多补偿的代码,在一些场景中,一些业务流程可能用TCC不太好定义及处理。

消息事务(最终一致性)

非常常用的分布式事务的解决方案,或许你会在使用消息队列的时候不知不觉的做到了分布式事务,只是你没有察觉到

基本流程如下:

  1. 第一阶段:Prepare消息,拿到消息的地址(注意此时没有发送消息)
  2. 第二阶段:执行本地事务
  3. 第三阶段:通过第一阶段拿到的消息地址去发送消息
  • 思考:为什么是先执行本地事务后才进行消息的发送?不能先发送消息再执行本地事务吗?

    如果先发送消息,那本地事务如果出现问题进行回滚了,但是你消息已经发送出去了,是不能将消息会滚的

    所以先执行本地事务,如果本地事务出现问题了就不会进行消息的发送,本地事务成功提交,进行消息发送

    那如何保证消息一定生产成功并保证消息一定能够被消费呢?这就是MQ的作用了,下面我们对MQ进行分析

  • 如果消费失败了怎么办?

    消费失败了则由MQ进行重试,消息接收端需要保证幂等。如果消息消费失败,这个就需要人工进行处理,因为这个概率较低,如果为了这种小概率时间而设计这个复杂的流程反而得不偿失

相关推荐
遇到BUG就睡觉30 分钟前
面试经验总结
面试
Targo37 分钟前
Lucene.NET 的搜索服务
后端
运维Z叔1 小时前
安全面试经验分享 | 某安全厂商北京安服工程师实习岗
网络·经验分享·安全·网络安全·面试·渗透·漏洞挖掘
nongcunqq1 小时前
Django获取request请求中的参数
后端·python·django
架构师专栏2 小时前
Java 新手学习线路,Java 学习路线是怎样的?
java·后端·程序员
架构师专栏2 小时前
有粉丝问:如何快速打好 Java 基础?
java·后端·程序员
啟王朝5 小时前
Java面试
java·开发语言·面试
路多辛5 小时前
Web 安全之 VAPT (漏洞评估与渗透测试)详解
运维·网络·后端·安全·web安全·网络安全
扁豆的主人11 小时前
网站架构核心要素
架构
危险游戏i11 小时前
【docker镜像如何在不同的架构上运行】
docker·容器·架构