分布式事务解决方案

分布式事务解决方案
  • 之前一节分析了分布式事务的问题以及理论模型,并且基于CAP理论,我们知道对于数据一致性问题有AP和CP俩种方案。但是在我们实际项目中局域CP的强一致性在数据库性能与系统处理能力上会有很大的制约。所以我们实际项目中更多的采取柔性事务。“柔性事务”是遵循BASE理论来实现的事务模型,他有两个特性:基本可用,柔性状态(状态段时不一致)。
TCC补偿方案
  • TCC(Try Confirm Cancel)是一种比较老的分布式一致性解决方案,他实际上是将完整的业务拆分成一下三个步骤。

    • Try:对数据进行校验或者资源的预留
    • Confirm:确认真正执行的任务,只操作Try阶段的预留资源
    • Cancel:取消执行,释放Try阶段预留的资源。
  • TCC是一种两阶段提交的思想,第一阶段通过Try进行准备工作,第二阶段Confirm/Cancel表示Try阶段操作的确认和回滚。在分布式事务场景中,每个事务实现TCC后,就作为其中一个资源,参与到整个分布式事务中。主业务服务咋第一阶段中分别调用所有TCC服务的Try方法,最后根据第一阶段的执行情况来决定第二阶段的Confirm或者Concel。用如下图说明:

在这里插入图片描述

  • 对应TCC的工作机制,我们可以用一个简单的例子,在直播礼物的业务场景下,设计到两个事务的操作:

    • 在账户服务中,对用户账户余额的扣除
    • 在礼物产品服务中,对指定礼物可申请购买的个数进行扣减
  • 这两个事务操作在微服务架构下分别对应的是两个不同的微服务,以及独立的数据库操作,在TCC的工作机制中,首先对账户服务和礼物产品服务分别提供Try,Confirm,Cancel三个方法

    • 在账户服务的Try方法中对实际申购金额进行冻结,Confirm方法吧Try方法冻结的资金进行实际扣减,Cancel方法吧Try方法冻结的资金进行解除冻结
    • 在礼物服务中Try方法将本次申请购买的礼物部分数量先进行冻结,Confirm方法将Try方法冻结的额度进行扣减,Cancel方法将Try方法中冻结的额度进行释放。
  • 在主业务流程中中分别调用这两个服务对外提供的包含事务的处理方法(自己扣减,礼物产品扣减)来实现时间的业务功能的时候,我们会先调用Try方法来做资源的预留,如果这两个方法处理都正常,TCC事务协调就会调用Confirm方法对预留资源进行实际应用。否则TCC事务协调器接受到反馈任何一个服务Try方法处理失败,就会调用各个服务的Candel方法进行回滚,从而保证数据的一致性。.

基于可靠性消息的最终一致性方案
  • 基于可靠性消息的最终一致性在互联网公司某一些业务场景下使用的分布式数据一致性解决方案,他主要利用消息中间件(Kafka,RocketMQ,RabbitMQ)这种可靠机制来实现数据一致性的保证。还是以我们礼物的支付场景为例,用户完成订单支付后,不需要同步等待支付结果,可以继续做其他事情,但是对于系统来说,大部分还是发起支付后,等到第三方支付平台提供异步支付结果通知,在根据结果来设置该订单的支付状态,并且如果是支付成功的状态,大部分业务场景可能还会给用户一定的积分,或者其他奖励。
  • 所以当系统接收第三方回调支付结果时候,需要更新支付状态,更新账户服务积分余额,这里涉及到几个服务数据一致性问题。从这个场景中发现这里的数据一致性并不要求实时性,所以我们可以采用基于可靠的消息最终一致性方案来保证支付服务,账户服务的数据一致性。
  • 如下图,支付收到杰哥后,先更新支付订单状态,在发送一天MQ,账户服务会监听到指定队列的消息并进行相应处理,完成数据同步。

在这里插入图片描述

  • 上图中,支付服务的本地事务(修改支付状态等)与发送消息这个操作必须是原子性的,具体有如下操作:
  • 先发送消息,在执行数据库事务,这种情况可能会出现消息发送成功但是本地事务更新失败的情况,仍然会导致数据不一致的问题
begin transaction;
sendMsg();
updateStatus();
commit transaction;
  • 先执行数据库事务操作,在发送消息,这种情况可能会出现MQ响应超时导致异常,从而将本地事务回滚,消息可能已经发送成功,也会存在数据不一致问题。
begin transaction;
updateStatus();
sendMsg();
commit transaction;
  • 以上情况也有很多成熟的解决方案,以RocketMQ为例,他提供了事务消息模型,下图:

在这里插入图片描述

  • 如上图流程

    • 生产者发送一个事务消息到消息队列上,消息队列只记录这条消息的数据,此时消费者无法消费这条消息
    • 生产者执行具体的业务逻辑,完成本地事务操作
    • 接着生产者更具本地事务执行结果发送一条确认消息给消息队列服务器,如果本地事务执行成功,则发送Commit消息,表示第一步中发送的消息可以被消费,否则,消息队列服务器会吧第一步存储的消息删除。
    • 如果生产者在执行本地事务过程中因为某些原因一直没有未给消息队列服务器发送确认消息,那么消息队列服务器会定时主动回查生产者获取本地事务的执行结果,然后根据回查结果来决定这条消息是否需要投递给消费者。
    • 消息队列服务器上存储的消息被生产者确认后,消费者就可以消费这条消息了,消息消费完成后发送一个确认标识给消息队列服务器,标识该消息已经成功投递。
  • 在RocketMQ事务消息模型中,事务是由生产者来完成的,消费者不需要考虑,因为消息队列可靠性投递机制的存在,如果消费者没有签收改消息,那么消息队列服务器会重复发送,从而实现生产者的本地数据和消费者的本地数据在消息队列的机制下达到最终一致。

  • 如上流程可以看到,在RocketMQ的事务消息模型中,最核心的机制应该是事务回查,实际上查询模型在很多类似场景中可以应用。在分布式系统中,由于网络通信的存在,服务之间的远程通信除了成功和是吧以外,还存在一种未知状态,比如,网络超时。服务提供者可以提供一个接口来查询业务的执行状态,服务调用方可以通过调用这个接口来得知之前的操作结果并进行相应处理。

最大努力通知型
  • 最大努力通知型和基于可靠消息的最终一致性方案的实现是类似的,是一种比较简单的柔性事务处理方案,也比较适合于对数据一致性要求不那么高的场景,最典型的使用场景是统一支付系统(公司对接支付宝,微信,等支付系统的统一支付系统)

在这里插入图片描述

  • 以下是在用户的角度来分析最大努力通知型的处理过程

    • 用户先创建一个支付订单,然后调用支付宝发起支付请求
    • 支付宝唤醒支付页面完成支付操作,支付宝同样会针对改用户创建一个支付交易,并且根据用户支付结果记录支付状态。
    • 支付完成后触发一个回调通知给用户,用户收到通知后,根据结果修改本地支付订单的状态,并且返回一个处理状态给支付宝
    • 针对这个订单,在理想状态下支付宝的校验状态和用户的交易状态会在通知完成后达到最终一致性。但是由于网络的不确定性,支付结果通知可能会失败或者丢失,导致用户端支付订单的状态是未知的。所以最大努力通知型的作用就体现出了
    • 如果用户端在收到支付结果通知后没有返回一个SUCCESS状态码,那么这个支付结果回调请求会以衰减的重试机制(逐渐拉大通知间隔)继续触发,比如1min,5min,10min,30min…一直到最大通知次数。如果达到指定次数后商户还没有返回确认状态–查支付宝
    • 支付宝提供了一个交易结果查询接口,可以根据这个支付订单号码去查询支付状态,然后根据返回结果来更新用户支付订单状态,可以做定时任务,或者直接人工干预。
  • 以上流程发现,所谓最大努力通知型,就是在用户端如果没有返回一个消息确认时候,支付宝会不断重试,直到收到消息或者达到最大重试次数。

  • 最大努力通知型和 事务消息模型基本类似,在消费者没有向消息中间件服务器发送确认之前,这个消息会被重复投递,确保消息可靠性。

上一篇:分布式事务理论模型
下一篇:分布式事务框架seata

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值