分布式事务

分布式事务

CAP理论

  • 可用性 Consistency
  • 分区容错性 Partition Tolerance
  • 一致性 Availability

一般会牺牲一致性来换取系统的可用性和分区容错性

解决方案

1、二阶段提交2PC(Two-Phase-Commit)

  • AP application 应用程序
  • RM ResourceManager 资源管理器
  • TM TransactionManager 事务管理器
准备阶段(第一阶段):

事务管理器TM向所有的资源管理器RM发送Prepare消息,每个参与者要么直接返回失败(如权限验证失败),要么在本地执行事务,写本地的redo和undo日志,但不提交

提交阶段(第二阶段)

当所有TM所有RM节点获得的消息都为"同意"时:

1、协调者节点向所有参与者节点发出"正式提交(commit)"的请求

2、参与者节点正式完成操作,并释放在整个事务期间内占用的资源

3、参与者节点向协调节点发送"完成"消息

4、协调者节点收到所有参与者节点返回"完成"消息后,完成事务

如果任一参与节点在第一阶段返回的响应消息为"中止",或者协调者在第一阶段的询问超时之前无法获取所有参与者节点的响应消息时:

1、协调者节点向所有参与者节点发出"回滚操作(rollback)"的请求

2、参与者节点利用之前写入的Undo信息执行回滚,并释放整个事务期间占用的资源

3、参与者节点向协调者节点发送"回滚完成"消息

4、协调者节点收到所有参与者节点返回的"回滚完成"消息,取消事务

存在问题

1、死锁问题:假如在第一个阶段,TM向所有的RM发送Prepare消息,并等待RM的回执消息时,其中一台RM节点出现了问题,如宕机了,那么TM就会一直阻塞等待,产生死锁问题,所以如果我们的数据库连接池中的连接数量有限的情况下,会很容易发生死锁问题导致数据连接不可用,从而导致服务不可用,破坏了服务的可用性

2、数据不一致问题。在二阶段提交的阶段二中,当协调者向参与者发送commit请求之后,发生了局部网络异常或者在发送commit请求过程中协调者发生了故障,这会导致只有一部分参与者接受到了commit请求。而在这部分参与者接收到commit请求。而在这部分参与者接收到commit请求之后就会执行commit操作。但是其他部分未接到commit请求的机器则无法执行事务提交。于是整个分布式系统便出现了数据不一致性的现象

3、由于协调者重要性,一旦协调者发生故障。参与者会一直阻塞下去。尤其在第二阶段,协调者发生故障,那么所有参与者还都处于锁定事务资源的状态中,而无法继续完成事务操作。(如果是协调者挂掉,可以重新选举一个协调者,但无法解决因为协调者宕机导致的参与者处于阻塞状态的问题)

2、扩展:景点的X/OpenDTP事务模型

X/Open DTP(X/Open Distributed Transaction Processing Reference Model)是该组织定义的标准

2、三阶段提交3PC(Three-Phase-Commit)

由于二阶段提交存在阻塞死锁和单点故障等问题,所以在二阶段提交的基础上提出了三阶段提交解决方案

在理解了2PC的基础上我们去理解3PC,3PC在2PC的基础上增加了一个准备提交(prepare to commit)阶段,所以3PC提出了三个阶段去处理一个分布式事务:

canCommit阶段(第一阶段)

TM向所有的RM发送canCommit请求,询问是否可以执行事务提交操作。然后开始等待参与者的响应。各参与者向协调者反馈事务询问的响应,如果参与者认为自己可以顺利执行事务,就返回yes,否则返回no响应

preCommit阶段(第二阶段)

执行预提交:当TM从所有RM节点获得的消息都是"Yes"时

1、协调者节点向所有参与者节点发送"preCommit"的请求

2、参与者节点向协调者节点发送"ack"响应

3、协调者节点收到所有参与者节点反馈的"ack"响应后,确定下一阶段是否为提交或者是种植操作

中断事务:如果任一参与者节点响应消息为"中止"的时候或者"等待超时",都会去中断事务

doCommit阶段(第三阶段)

执行提交:当TM从所有RM节点获得的消息都为可执行时:

1、协调者节点向所有参与者节点发出"docommit"的请求

2、参与者节点向协调者节点发送"haveCommitted"响应

3、完成事务

中断事务:因为出现了异常,比如TM一方出现了问题,或者时TM各RM之间出现了故障

首先RM向所有的RM发送中断请求。然后RM接受到中断请求后,会利用其在第二阶段记录的undo信息来执行事务回滚操作,并释放资源。接下来RM在完成事务回滚之后,向TM发送ack消息。最后协调者接收到所有的ack消息后,中断事务

3PC存在的问题

在doCommit阶段,如果参与者无法及时接收到来自TM的doCommit或者回滚请求时,在等待超时之后,会继续进行事务的提交。所以,由于网络原因,TM发送的回滚响应没有及时被RM接收到,那么RM在等待超时之后执行了commit操作。这样就和其他接收到回滚命令并执行回滚的RM之间存在数据不一致的情况

3、解决方案之柔性分布式事务

状态机

和刚性分布式事务对比,柔性分布式事务就是保证高可用的前提下,保证了最终一致性,舍弃了强一致性,这种选择更适用于大多数场景。

什么是状态机

一种特殊的组织代码方式,用这种方式能够确保你的对象随时都知道自己所处的状态以及所能做的操作。它也是一种用来进行对象行为建模的工具,用于描述对象在它的生命周期内所经历的状态序列,以及如何响应来自外界的各种事件。例如:编写相关业务逻辑的时候经常也会需要处理各种事件和状态切换,如switch、if/else。在处理一些业务逻辑比较复杂的需求时,是否适用合用一个有限状态机来描述,如果可以把业务模型抽象成一个有限状态机,那么代码就会逻辑特别清晰,结构贴别规整。例如:一笔订单可能会有等待支付、支付中、已支付等状态,这其实就是一种状态机

在柔性分布式事务解决方案中,柔性分布式事务多是基于服务和应用层的,而2PC和3PC等刚性分布式事务解决方案都是基于DB层,

柔性分布式事务解决方案大致可以分为三类
  • 补偿型
  • 异步确保型
  • 最大努力通知型
1、TCC(补偿型)

RCC分别对应try、confirm、Cancel三种操作,

  • Try:资源预留

在调用主服务的时候,主服务首先预留资源,然后调用其他服务的try接口也预留资源,这个时候并不会执行真正的业务逻辑,只是简单的"冻结"了需要的资源

  • Confirm:确认执行

假如在Try阶段所有服务的资源都成功冻结,并且都返回了成功的响应,则主服务执行真正执行需要做的业务操作,并且调用其他服务的Confirm接口执行业务操作

  • Cancel:取消执行

假如在Try阶段存在服务响应了冻结资源失败,则需要释放这些冻结的资源,同理,主服务首先释放,然后调用其他服务的Cancel接口释放资源

TCC方案中,所有支持分布式事务的服务都必须由Try、Confirm、Cancel三个接口,这些相互调用基本上由一些TCC框架完成

img

2、基于本地消息表(异步确认型)

所有的服务只维护本地事务和表,将大的分布式事务分解成各个服务的本地事务,异步的去保证最终一致性

例如:用户买了一个东西,这个时候库存服务不仅维护一张库存表,还维护一张记录这一次操作的全局事务ID,服务发送/接受方和状态等信息的消息表;而账户服务不仅维护一张资金表,还维护一张记录处理其他服务的事务的消息表

img

扩展:基于本地消息表的分布式事务解决方案,我们可以扩展很多其他的优化方案,比如我可以在库存服务操作的时候,不去立马减去库存,而是像TCC那样先冻结部分库存,并将全局事务信息存放消息表,然后启动多个定时任务,一个去同步多个服务的事务信息,一个去检查分布式事务执行的情况,在考虑冻结的数据是真正执行还是进行回滚,因为我们实现的是最终一致性,所以我们可以有很多变种的解决方案

3、基于MQ(最大努力通知型)

RocketMQ

生产者发送事务消息到MQ,这个时候并不发送给消费者,这也是RocketMQ的其中一种消息类型的特性,消息发送到MQ上在没有执行消息确认之前,消息对于消费者是不可见

步骤1成功后,生产者执行本地事务,并提交

步骤2成功后,生产者确认事务消息,使得发送到MQ上的事务消息对于消费者可见

消费者获取到消息进行消费,消费完之后执行ack进行确认

可能存在的三个问题

回查:生产者本地事务成功后,发送事务确认消息到消费者上失败了,这个时候意味者消费者无法正常消费到这个消息。所以RocketMQ提供了消息回查机制,如果消息一直处于中间状态,MQ会发起重试去查询MQ上这个事务的处理状态。一旦发现事务处理成功,则把当前这条消息设置为可见

补偿:另外一个问题就是消费者确认拿到了这个消息,并且开始执行该事务,但一直操作失败,这个时候就需要消费者晚膳补偿逻辑了,因为生产者的事务已经提交了,而消费者需要利用重试或者补偿等逻辑务必保证自己的事务执行成功,最坏的情况可能就需要记录错误日志,并且人工介入了

幂等:证是由于MQ的特性,消费者可能会重复拿到同一个事务消息,因此消费者务必考虑幂等问题

img

总结

CAP理论和BASE理论的引出,到2PC和3PC等刚性分布式事务解决方案,再到TCC和本地消息表和基于MQ的柔性分布式事务解决方案,所有的方案都是随着互联网服务框架的反战而发展,所以这些解决方案都不是一成不变的,是可以扩展和优化甚至是结合使用的

如需要针对账户流水做强一致性分布式事务解决方案,那么就可以基于2PC或者3PC的思想实现自己的一套解决方案。或者对于一些不是要求强一致性的分布式事务,比如日志信息和非主要业务信息,就可以利用一些中间件或者中间表,也可以利用一些定时任务机制来实现补偿逻辑和最终一致性

阿里开源的分布式事务中间件Seata

posted @ 2021-05-27 17:55  zpdream  阅读(108)  评论(0)    收藏  举报