分布式事务总结

277 阅读7分钟

分布式事务常用解决方案

一、基于两阶段提交协议的方案

两个阶段:准备阶段和提交阶段

  • 准备阶段 事务管理器给每个参与者发送Prepare消息,每个参与者在本地执行事务,并写本地的undo 日志,此时事务是没有提交的。

  • 提交阶段 如果事务管理器接受到了参与者执行失败或者超时的消息时,直接给每个参与者发送回滚消息。否则,发送提交消息。参与者执行事务管理器发送的指令,并释放锁资源。

具体解决方案

传统的2PC方案是在数据库层面实现的,Mysql和Oracle都支持2PC协议。

方案一、XA方案

有三种角色: AP:应用程序 RM:资源管理器 TM:事务管理器

DTP模型定义TM和RM之间的接口规范叫XA,基于数据库的XA协议来实现2PC又叫XA方案。 三个角色交互方式如下:

  1. TM向AP提供应用程序接口,AP通过TM提交及回滚事务。 2.TMJ交易中间件通过XA接口来通知RM数据库事务的开始、结束 提交 回滚等。

XA方案的问题:

  1. 需要数据库支持XA协议。
  2. 资源锁需要等两个阶段结束才能释放,性能较差。
方案二、Seata方案

seata是阿里中间件团队发起的开源项目fescar,后改名为seata。 主要优点:性能较好,且长时间不占用连接接资源。因为是在应用层解决分布式事务问题。它以高效的对业务0入侵的方式解决微服务场景下面临的分布式事务问题。

有三个组件: TC:事务协调器 独立中间件,独立运行。 TM:事务管理器 服务开启一个全局事务,并向tc发起全局提交或回滚指令。 RM:资源管理器 控制分支事务,负责分支注册、状态汇报,并接受tc的指令,驱动分支事务的提交或回滚。

seata实现2pc和传统2pc的区别: 1 传统方案是通过xa协议实现,RM实际上是数据库自身,而seata的RM是以jar包形式作为中间件部署在应用程序一侧的。 2 传统2pc方案,事务性资源的锁都要保持到第二阶段才能释放,而seata的做法是在第一阶段就将本地事务提交,这样可以整体提高效率。

seata执行流程

TCC

介绍: TCC是 try,confirm,cancel三个词语的缩写,tcc要求每个分支事务实现三个操作:预处理try,确认confirm,撤销cancel。 try阶段做业务检查及资源预留,confirm做业务确认操作,cancel实现一个与try相反的操作即可即回滚。

流程: TM首先发起所有分支事务的try操作,任何一个分支事务的try操作失败,TM将会发起所有分支的cancel操作,如果try操作全部成功,TM将会发起所有分支事务的confirm操作。如果confirm或cancel操作失败,则会进行重试。因此需要实现幂等性。

解决方案

Hmily框架是一个高性能分布式事务TCC开源框架,支持dubbo、spring cloud等rpc框架进行分布式事务。 Hmily利用aop对参与分布式事务的本地方法与远程方法进行拦截处理,通过多方拦截,事务参与者能够透明的调用到另一方的try confirm cancel方法;传递事务上下文,并记录事务日志,并可以进行重试等。

它不需要事务协调服务,但需要一个数据库来进行日志存储。

可能存在的问题 空回滚:没有执行try,就执行了cancel 幂等:因为三个阶段都是有三个独立线程执行,并有重试机制,所以需要保证幂等。办法就是在分支事务中记录执行状态,每次执行前查询该状态。 悬挂:二阶段cancel比try先执行。原因是rpc调用分支事务try时,此时rpc调用网络发生拥挤,导致超时,此时,tm会通知rm回滚该事务,可能回滚完后,rpc请求才到参与者,真正执行。 办法就是:如果第二个阶段已经执行完成,那么第一个阶段不要执行了。

TCC和2PC对比 2pc是一般是在数据库层面解决问题,而tcc是在应用层面处理。好处是可以自定义数据操作的粒度,降低锁冲突,提高吞吐量。 不足地方:面对应用入侵性非常强,每个分支都需要实现try,confirm,cancel三个操作。实现难度大。

二、可靠消息最终一致性

介绍 当事务发起方执行完本地事务后发出一条消息,事务参与者(消费者)一定能够接受到消息并处理事务成功。 强调的是消息很可靠,并且事务达到的最终一致性。

需要保证本地事务和发送消息的原子性 也就是说本地数据库操作成功,发送消息必须成功。 消息发送发采用 先进行数据库操作,然后发送消息,但是如果当发送消息超时异常,其实消息已经发送出去了,但是数据库却回滚了,此时就会导致数据不一致。

事务参与方接受消息的可靠性需要保证 如果接受消息失败,可以重复接受消息。

消息重复消费的问题 当某个消费节点消费超时但是消费成功,消息中间件会重复投递消息,就导致了消息的重复消费。 此时,就需要实现参与方的方法幂等性。

具体方案

本地消息表方案

最初由ebay提出,核心是通过本地事务保证数据库操作和消息的一致性,然后通过定时任务将消息发送至消息中间件。

RocketMQ事务消息方案

rocketmq4.3版本支持事务消息。

大致流程如下: 1 mq发送方先发送一条消息给mq server,此时消息不可以被消费。 2 mqserver 发送消息给发送方表示接受到了。 3 发送方执行本地事务,执行成功发送commit消息给mqserver,此时,该消息才可被消费 失败的话发送 rollback消息给mqserver,mqserver删除消息。 4 mqserver 如果一直未收到确认消息,回查mq发送方检查事务状态。 5 mq订阅方订阅消息,接受到消息,执行后续流程。

三 最大努力通知方案

例如 账户系统和充值系统 1 账户系统调用充值接口,充值系统完成支付处理。 2 充值系统的充值结果通知账户系统。 3 账户系统更新充值状态 ‘成功’/‘失败’。 4 充值系统还需要提供可以查询充值结果的接口,供账户系统调用。

在这个案例中,充值系统通过一定机制最大努力通知账户系统。包括:

  • 消息接受方如果没有接收到通知,充值系统会进行重试通知。
  • 消息校对机制:充值系统提供接口供查询充值结果。

最大努力通知方案和可靠消息一致性的区别

  • 可靠消息一致性方案中,消息的可靠性关键有发起通知方来保证。而,最大努力通知,则相反。因为接受通知方需要主动调用发起通知方的接口来查询业务处理结果。

  • 前者关注的是交易后的通知事务,即将结果可靠的通知出去。后者关注的是交易过程中的事务一致性,以异步的方式完成交易。

具体方案

  • 方案1: 发起通知方将消息发送给mq,mq会将消息发送给接收方。如果接受失败,则mq会重试。
  • 方案2: 发起通知方将消息发送给mq,mq将消息投递给一个专门的应用程序,由该程序在发送通知给接收方。

两个不同点

  • 方案1主要应用在内部应用之间的通知

  • 方案2主要用于外部应用的通知。