小理解-分布式事务

说到事务,当前可分为两种,一个是本地事务,一个是分布式事务。

1、本地事务

     相信大家都不陌生,对事务的定义以及ACID都会十分熟悉,使用spring为我们提供的@Transaction注解,可以轻松的实现本地事务。

     注:对于@Transaction不了解的,请自行度娘,深入了解其7中传播方式、4中隔离级别,当然对于其他属性,也同样需要了解,尤其readonly属性

2、分布式事务

     在分布式大行其道的当前,分布式事务是我们系统中必须要面临的问题,但是从广义上来看,分布式事务其实也是事务, 只是由于业务上的定义以及微服务架构设计的问题,所以需要在多个服务之间保证业务的事务性,也就是 ACID 四个特性;从单机的数据库事务变成分布式事务时,原有单机中相对可靠的方法调用以及进程间通信方式已经没有办法使用,同时由于网络通信经常是不稳定的,所以服务之间信息的传递会出现障碍。

      服务之间通信方式的改变是造成分布式事务复杂的最主要原因,在同一个事务之间的执行多段代码会因为网络的不稳定造成各种奇怪的问题,当我们通过网络请求其他服务的接口时,往往会得到三种结果:正确、失败和超时,无论是成功还是失败,我们都能得到唯一确定的结果,超时代表请求的发起者不能确定接受者是否成功处理了请求,这也是造成诸多问题的诱因。

 

2PC协议与3PC协议

     两阶段提交是一种使分布式系统中所有节点在进行事务提交时保持一致性而设计的一种协议;在一个分布式系统中,所有的节点虽然都可以知道自己执行操作后的状态,但是无法知道其他节点执行操作的状态,在一个事务跨越多个系统时,就需要引入一个作为协调者的组件来统一掌控全部的节点并指示这些节点是否把操作结果进行真正的提交。

     两阶段提交的执行过程就跟它的名字一样分为两个阶段,投票阶段提交阶段,在投票阶段中,协调者(Coordinator)会向事务的参与者(Cohort)询问是否可以执行操作的请求,并等待其他参与者的响应 ,所有执行成功的参与者会向协调者发送 AGREEMENT 或者 ABORT 表示执行操作的结果。

     

      当所有的参与者都返回了确定的结果(同意或者终止)时,两阶段提交就进入了提交阶段,协调者会根据投票阶段的返回情况向所有的参与者发送提交或者回滚的指令。

     

      当事务的所有参与者都决定提交事务时,协调者会向参与者发送 COMMIT 请求,参与者在完成操作并释放资源之后向协调者返回完成消息,协调者在收到所有参与者的完成消息时会结束整个事务,反义 ABORT 指令也是一样

     两阶段提交协议是一个阻塞协议,也就是说在两阶段提交的执行过程中,除此之外,如果事务的执行过程中协调者永久宕机,事务的一部分参与者将永远无法完成事务,它们会等待协调者发送 COMMIT 或者 ROLLBACK 消息,甚至会出现多个参与者状态不一致的问题

     鉴于这个问题,便有人提出了3PC协议,增加了预提交阶段和超时处理, 如果协调者或者参与者在规定的之间内没有接受到来自其他节点的响应,就会根据当前的状态选择提交或者终止整个事务,预提交阶段的引入其实让事务的参与者有了除回滚之外的其他选择。

     

    当参与者向协调者发送 ACK 后,如果长时间没有得到协调者的响应,在默认情况下,参与者会自动将超时的事务进行提交,不会像两阶段提交中被阻塞住;上述的图片非常清楚地说明了在不同阶段,协调者或者参与者的超时会造成什么样的行为。 

XA协议

      MySQL 的 InnoDB 引擎其实能够支持分布式事务,也就是我们经常说的 XA 事务;XA 事务就是用了我们在上一节中提到的两阶段提交协议实现分布式事务,其中事务管理器为协调者,而资源管理器就是分布式事务的参与者。

      正如两阶段提交协议中定义的,MySQL 提供的 XA 接口可以非常方便地实现协议中的投票和提交阶段,我们可以通过一下的流程图简单理解一下 MySQL XA 的接口是如何使用的:

 

     

       XA 确实能够保证较强的一致性,但是在 MySQL XA 的执行过程中会对相应的资源加锁,阻塞其他事务对该资源的访问,如果事务长时间没有 COMMIT 或者 ROLLBACK,其实会对数据库造成比较严重的影响。

消息服务

     最常见的两种服务等级就是 At-Most-Once 和 At-Least-Once,前者能够保证发送方不对接收方是否能收到消息作保证,消息要么会被投递一次,要么不会被投递,这其实跟一次普通的网络请求没有太多的区别;At-Least-Once 能够解决消息投递失败的问题,它要求发送者检查投递的结果,并在失败或者超时时重新对消息进行投递,发送者会持续对消息进行推送,直到接受者确认消息已经被收到,相比于 At-Most-Once,At-Least-Once 因为能够确保消息的投递会被更多人使用。

     除了这两种常见的服务等级之外,还有另一种服务等级,也就是 Exactly-Once,这种服务等级不仅对发送者提出了要求,还对消费者提出了要求,它需要接受者对接收到的所有消息进行去重,发送者和接受者一方对消息进行重试,另一方对消息进行去重,两者分别部署在不同的节点上,这样对于各个节点上的服务来说,它们之间的通信就是 Exactly-Once 的,但是需要注意的是,Exacly-Once 一定需要接收方的参与。

     我们可以通过实现 AMQP 协议的消息队列来实现分布式事务,在协议的标准中定义了 tx_selecttx_commit 和 tx_rollback 三个事务相关的接口,其中 tx_select 能够开启事务,tx_commit 和 tx_rollback 分别能够提交或者回滚事务。

     使用消息服务实现分布式事务在底层的原理上与其他的方法没有太多的差别,只是消息服务能够帮助我们实现的消息的持久化以及重试等功能,能够为我们提供一个比较合理的 API 接口,方便开发者使用。

事务的处理方式,协同or编排

      如果一个分布式事务,我们采用协同的方式进行开发,每一个本地的事务都会触发一个其他服务中的本地事务的执行,也就是说事务的执行过程是一个流的形式进行的:

        服务之间的通信其实就是通过事件进行的,每一个本的事务最终都会向服务的下游发送一个新的事件,既可以是消息队列中的消息,也可以是 RPC 的请求,只是下游提供的接口需要保证幂等和重入。

 

     如果一个分布式事务,我们采用编排的方式进行开发,需要引入中心化的协调器节点, 根据任务的调用情况决定是否需要调用对应的补偿方案,并在网络请求出现超时时进行重试:

 

     

        服务编排的过程中,我们是从协调者本身触发考虑整个事务的执行过程的。 前者强调各个服务的自治与去中心化,后者需要一个中心化的组件对事务执行的过程进行统一的管理,两者的优缺点其实就是中心化与去中心化的优缺点。

     

     当然以上都是理论,对于分布式事务,现在也有不少解决方案,可以直接供我们使用,例如:

总结     

     从XA到2PC,再到最后通过消息服务保证最终一致性来实现分布式事务。一致性要求上,逐渐降低,但是系统的吞吐量以及可用性,都在逐渐提高,实际开发中我们要根据自身的业务特性,选择合适的方式。

     当前比较常用的方式就是TCC和消息服务。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值