分布式事务解决方案:7种常见解决方案汇总

分布式事务解决方案:7种常见解决方案汇总_第1张图片

二阶段提交协议

为了解决分布式事务的问题,出现了很多协议,如2PC(二阶段提交协议)、3PC(三阶段提交协议)

在二阶段提交协议中有一个事务管理器和多个资源管理器。事务管理器分两阶段协调资源管理器。

一阶段:事务管理器告诉资源管理器准备执行事务,并锁住需要的资源。当准备完成后,资源管理器向事务管理器报告已准备就绪。
二阶段:如果所有资源管理器都准备成功,第二阶段事务管理器回要求所有的资源管理器执行提交操作。如果任一资源管理器在第一阶段返回准备失败,那么事务管理器回要求所有的资源管理器在第二阶段执行回滚操作。
分布式事务解决方案:7种常见解决方案汇总_第2张图片
二阶段看起来能提供原子性操作,但是不幸的是,二阶段提交还是有几个缺点的

  1. 2PC是一个同步阻塞协议,资源管理器在执行的过程中会锁定资源。其他第三方节点想访问这些资源的时候不得不处于阻塞状态
  2. 一阶段有超时机制,在第一阶段事务管理器没有收到资源管理的响应,或者资源管理器挂了。超时就会判端事务失效,向所有资源管理器发送回滚命令。但二阶段只能不断重试
  3. 事务管理器存在单点风险,如果发生故障,则资源管理器会一直阻塞下去。

基于2PC的问题,人们又提出了3PC的概念,但是很少被使用,没研究过,大家可以看看其他文章

XA规范

XA规范是X/Open 组织针对二阶段提交协议的实现做的规范。目前几乎所有的主流数据库都对XA规范提供了支持

分布式事务解决方案:7种常见解决方案汇总_第3张图片

XA规范的特点是:

  1. 对代码无侵入,开发比较快速
  2. 对资源进行了长时间的锁定,并发程度比较低

TCC

TCC这种方案应该是在企业中应用最广泛的一种方案,在业务层面实现分布式事务。TCC是Try、Confirm、Cancel三个词语的缩写。

TCC主要分为3个操作。
Try:一阶段,负责业务资源检查和预留
Confirm:二阶段提交操作,所有的Try都成功了,则执行Confirm操作。Confirm真正执行业务,使用Try预留的资源
Cancel:二阶段回滚操作,只有一个Try失败了,则走到Cancel操作。Cancel释放Try预留的资源

分布式事务解决方案:7种常见解决方案汇总_第4张图片
TCC的特点为:

  1. 并发程度高,在业务层面锁定资源
  2. 开发量大,一个业务员需要提供Try/Confirm/Cancel三个方法

SAGA

SAGA是一种补偿协议,在SAGA模式下,分布式事务有多个参与者。在分布式事务的执行过程中,依次执行各参与者的正向操作,如果所有正向操作都执行成功,那么分布式事务提交。如果任何一个正向操作失败,则会执行前面各参与者的回滚操作,将事务状态回到初始状态
分布式事务解决方案:7种常见解决方案汇总_第5张图片

SAGA特点为

  1. 并发度高,不需要长期锁定资源
  2. 开发量大,需要定义正向操作和补偿操作
  3. 不能保证隔离型

本地消息表

本地消息表这个解决方案是eBay 的系统架构师丹 · 普利切特(Dan Pritchett)在 2008 年发表于 ACM 的论文中提出的

分布式事务解决方案:7种常见解决方案汇总_第6张图片
我们以买书为例说一下大致流程

  1. 账号服务扣减账户余额,同时写入一条消息(状态为进行中),注意扣减账户余额和写消息在一个本地事务中
  2. 账号服务轮询消息表,将进行中的消息发送到消息队列
  3. 仓库服务收到消息后,扣减相应的库存。扣减完成后将结果通过给账号服务,账号服务将消息的状态更新为已完成(或者删除)
  4. 当消息发送失败,或者消息消费失败时,会不断重试,因此仓库服务要保证消费的幂等性。

通过这种方案就能达到事务的最终一致性,这种不断重试的思路,也体现了我们后面要提到的最大努力通知

本地消息表特点为:

  1. 需要创建额外的消息表,不断对消息表轮询

RocketMQ事务消息

在本地消息表方案中,生产者需要额外创建本地消息表,还要对本地消息进行轮询。RocketMQ在4.3之后的版本正式支持事务消息,该事务消息的本质是把本地消息表放在RocketMQ上,解决生产端消息发送和本地事务执行的原子性问题
分布式事务解决方案:7种常见解决方案汇总_第7张图片
RocketMQ实现分布式事务的流程如下

  1. producer向mq server发送一个半消息
  2. mq server将消息持久化成功后,向发送方确认消息已经发送成功,此时消息并不会被consumer消费
  3. producer开始执行本地事务逻辑
  4. producer根据本地事务执行结果向mq server发送二次确认,mq收到commit状态,将消息标记为可投递,consumer会消费该消息。mq收到rollback则删除半消息,consumer将不会消费该消息,如果收到unknow状态,mq会对消息发起回查
  5. 在断网或者应用重启等特殊情况下,步骤4提交的2次确认有可能没有到达mq server,经过固定时间后mq会对该消息发起回查
  6. producer收到回查后,需要检查本地事务的执行状态
  7. producer根据本地事务的最终状态,再次提交二次确认,mq仍按照步骤4对半消息进行操作

看到这,可能有人会问了,我们先执行本地事务,执行成功后再发送消息,这样不也可以保证生产端消息发送和本地事务执行的原子性?

其实这样做还是有可能会造成数据不一致的问题。假如本地事务执行成功,发送消息,由于网络延迟,消息发送成功,但是回复超时了,抛出异常,本地事务回滚。但是消息其实投递成功并被消费了,此时就会造成数据不一致的情况

那消息投递到mq server,consumer消费失败怎么办?

如果是消费超时,重试即可。如果是由于代码等原因真的消费失败了,此时就得人工介入,重新手动发送消息,达到最终一致性。

最大努力通知

做过微信充值或者支付宝充值的小伙伴对这个方案应该比较熟悉,因为最大努力通知这种方案在充值系统中经常被使用。充值系统通过不断的重试将充值结果推送给账户系统。因此账户系统接收充值结果的系统要保持幂等。另外充值充值系统还要提供回查接口,让账户系统主动校验充值的状态。
分布式事务解决方案:7种常见解决方案汇总_第8张图片
从微信支付的开发文档就可以看到微信支付用到了最大努力通知
https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7
分布式事务解决方案:7种常见解决方案汇总_第9张图片

Seata AT模式

这是阿里开源的事务框架Seata中主推的事务模式。Seata AT是一种无侵入的事务解决方案。事务的一阶段和二阶段均由框架自动生成。用户SQL作为分布式事务的一阶段,而二阶段由框架自动生成提交/回滚操作。和XA模式很类似

Seata AT模式特点:

  1. 对代码无侵入,开发速度较快
  2. 需要用全局锁来保证隔离性,并发程度较低

参考博客

[1]https://segmentfault.com/a/1190000040321750
[2]https://zhuanlan.zhihu.com/p/141645172

你可能感兴趣的:(分布式事务,分布式,java,开发语言)