分布式事务常见解决方案

分布式事务的目的是保障分布式存储中数据一致性,而跨库事务会遇到各种不可控制的问题,如个别节点宕机,像单机事务一样的ACID是无法奢望的

在这里插入图片描述

一、 二阶段提交(2PC)(XA Transactions)

在分布式系统中,每个节点虽然可以知晓自己的操作是成功或者失败,却无法知道其他节点的操作的成功或失败。当一个事务跨越多个节点时,为了保持事务的ACID特性,需要引入一个作为协调者的组件来统一掌控所有节点(称作参与者)的操作结果并最终指示这些节点是否要把操作结果进行真正的提交
在这里插入图片描述

1. 工作流程
1)第一阶段:

	(1)调者会问所有的参与者结点,是否可以执行提交操作

	(2)各个参与者开始事务执行的准备工作:如:为资源上锁,预留资源	 

	(3)参与者响应协调者,如果事务的准备工作成功,则回应“可以提交”,否则回应“拒绝提交”

2)第二阶段:

	(1)如果所有的参与者都回应“可以提交”,那么,协调者向所有的参与者发送“正式提交”的命令。参与者完成正式提交,并释放所有资源,然后回应“完成”,协调者收集各结点的“完成”回应后结束这个Global Transaction

	(2)如果有一个参与者回应“拒绝提交”,那么,协调者向所有的参与者发送“回滚操作”,并释放所有资源,然后回应“回滚完成”,协调者收集各结点的“回滚”回应后,取消这个Global Transaction	
2. 缺陷:
1)同步阻塞问题。执行过程中,所有参与节点都是事务阻塞型的。当参与者占有公共资源时,其他第三方节点访问公共资源不得不处于阻塞状态

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

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

4)二阶段无法解决的问题:协调者再发出commit消息之后宕机,而唯一接收到这条消息的参与者同时也宕机了。那么即使协调者通过选举协议产生了新的协调者,这条事务的状态也是不确定的,没人知道事务是否被已经提交

5)XA是资源层面的分布式事务,强一致性,在两阶段提交的整个过程中,一直会持有资源的锁。TCC是业务层面的分布式事务,最终一致性,不会一直持有资源的锁。

二、三阶段提交(3PC)

在这里插入图片描述

1. 相比2PC改变:
1)引入超时机制。同时在协调者和参与者中都引入超时机制

2)在第一阶段和第二阶段中插入一个准备阶段。保证了在最后提交阶段之前各参与节点的状态是一致的。也就是说,除了引入超时机制之外,3PC把2PC的准备阶段再次一分为二,这样三阶段提交就有CanCommit、PreCommit、DoCommit三个阶段
2. 流程:
1)CanCommit阶段

	3PC的CanCommit阶段其实和2PC的准备阶段很像。协调者向参与者发送commit请求,参与者如果可以提交就返回Yes响应,否则返回No响应

	(1)事务询问 协调者向参与者发送CanCommit请求。询问是否可以执行事务提交操作。然后开始等待参与者的响应

	(2)响应反馈 参与者接到CanCommit请求之后,正常情况下,如果其自身认为可以顺利执行事务,则返回Yes响应,并进入预备状态。否则反馈No

2)PreCommit阶段	

	协调者根据参与者的反应情况来决定是否可以记性事务的PreCommit操作。根据响应情况,有以下两种可能。假如协调者从所有的参与者获得的反馈都是Yes响应,那么就会执行事务的预执行

	(1)发送预提交请求 协调者向参与者发送PreCommit请求,并进入Prepared阶段

	(2)事务预提交 参与者接收到PreCommit请求后,会执行事务操作,并将undo和redo信息记录到事务日志中
	
	(3)响应反馈 如果参与者成功的执行了事务操作,则返回ACK响应,同时开始等待最终指令

	假如有任何一个参与者向协调者发送了No响应,或者等待超时之后,协调者都没有接到参与者的响应,那么就执行事务的中断

	(1)发送中断请求 协调者向所有参与者发送abort请求

	(2)中断事务 参与者收到来自协调者的abort请求之后(或超时之后,仍未收到协调者的请求),执行事务的中断

3)doCommit阶段(该阶段进行真正的事务提交,也可以分为以下两种情况)			

	执行提交

	(1)发送提交请求 协调接收到参与者发送的ACK响应,那么他将从预提交状态进入到提交状态。并向所有参与者发送doCommit请求

	(2)事务提交 参与者接收到doCommit请求之后,执行正式的事务提交。并在完成事务提交之后释放所有事务资源

	(3)响应反馈 事务提交完之后,向协调者发送Ack响应

	(3)完成事务 协调者接收到所有参与者的ack响应之后,完成事务

	中断事务(协调者没有接收到参与者发送的ACK响应(可能是接受者发送的不是ACK响应,也可能响应超时),那么就会执行中断事务)

	(1)发送中断请求 协调者向所有参与者发送abort请求

	(2)事务回滚 参与者接收到abort请求之后,利用其在阶段二记录的undo信息来执行事务的回滚操作,并在完成回滚之后释放所有的事务资源

	(3)反馈结果 参与者完成事务回滚之后,向协调者发送ACK消息

	(4)中断事务 协调者接收到参与者反馈的ACK消息之后,执行事务的中断
3. 解决问题:
1)单点故障问题

2)减少阻塞	

一旦参与者无法及时收到来自协调者的信息之后,会默认执行commit,而不会一直持有事务资源并处于阻塞状态
4. 缺陷:
1)一致性问题:由于网络原因,协调者发送的abort响应没有及时被参与者接收到,那么参与者在等待超时之后执行了commit操作。这样就和其他接到abort命令并执行回滚的参与者之间存在数据不一致的情况	

三、Try Confirm Cancel(TCC)(TCC框架github地址:https://github.com/changmingxie/tcc-transaction)

在这里插入图片描述

1. 概念
一个完整的TCC业务由一个主业务服务和若干个从业务服务组成,主业务服务发起并完成整个业务活动,TCC模式要求从服务提供三个接口:Try、Confirm、Cancel

1)Try:完成所有业务检查,预留必须业务资源

2)Confirm:真正执行业务,不作任何业务检查;只使用Try阶段预留的业务资源;Confirm操作满足幂等性

3)Cancel:释放Try阶段预留的业务资源;Cancel操作满足幂等性
2. 业务流程:
(1)第一阶段:主业务服务分别调用所有从业务的try操作,并在活动管理器中登记所有从业务服务。当所有从业务服务的try操作都调用成功或者某个从业务服务的try操作失败,进入第二阶段

(2)第二阶段:活动管理器根据第一阶段的执行结果来执行confirm或cancel操作。如果第一阶段所有try操作都成功,则活动管理器调用所有从业务活动的confirm操作。否则调用所有从业务服务的cancel操作
3. 与2PC比较
1)位于业务服务层而非资源层

2)没有单独的准备(prepare)阶段,Try操作兼备资源操作与准备能力

3)Try操作可以灵活选择业务资源的锁定粒度

4)开发成本较高

5)XA是资源层面的分布式事务,强一致性,在两阶段提交的整个过程中,一直会持有资源的锁。 TCC是业务层面的分布式事务,最终一致性,不会一直持有资源的锁
4. 缺点
1)Canfirm和Cancel的幂等性很难保证

2)这种方式缺点比较多,通常在复杂场景下是不推荐使用的,除非是非常简单的场景,非常容易提供回滚Cancel,而且依赖的服务也非常少的情况

3)这种实现方式会造成代码量庞大,耦合性高。而且非常有局限性,因为有很多的业务是无法很简单的实现回滚的,如果串行的服务很多,回滚的成本实在太高	

四、本地信息表(异步确保一致性)(可靠事件模式)

在这里插入图片描述

1. 基本思路:
1)消息生产方,需要额外建一个消息表,并记录消息发送状态。消息表和业务数据要在一个事务里提交,也就是说他们要在一个数据库里面。然后消息会经过MQ发送到消息的消费方。如果消息发送失败,会进行重试发送。

2)消息消费方,需要处理这个消息,并完成自己的业务逻辑。此时如果本地事务处理成功,表明已经处理成功了,如果处理失败,那么就会重试执行。如果是业务上面的失败,可以给生产方发送一个业务补偿消息,通知生产方进行回滚等操作

3)生产方和消费方定时扫描本地消息表,把还没处理完成的消息或者失败的消息再发送一遍。如果有靠谱的自动对账补账逻辑,这种方案还是非常实用的
2. 优点
1)一种非常经典的实现,避免了分布式事务,实现了最终一致性
3. 缺点
1)消息表会耦合到业务系统中,如果没有封装好的解决方案,会有很多杂活需要处理

五、MQ事务消息(比如RocketMQ)(可靠事件模式)

有一些第三方的MQ是支持事务消息的,比如RocketMQ,他们支持事务消息的方式也是类似于采用的二阶段提交,但是市面上一些主流的MQ都是不支持事务消息的,比如 RabbitMQ 和 Kafka 都不支持
在这里插入图片描述

1. 思路:
1)首先,发送一个事务消息,这个时候,RocketMQ将消息状态标记为Prepared,注意此时这条消息消费者是无法消费到的

2)接着,执行业务代码逻辑,可能是一个本地数据库事务操作

3)最后,确认发送消息,这个时候,RocketMQ将消息状态标记为可消费,这个时候消费者,才能真正的保证消费到这条数据

如果确认消息发送失败了怎么办?RocketMQ会定期扫描消息集群中的事务消息,如果发现了Prepared消息,它会向消息发送端(生产者)确认。RocketMQ会根据发送端设置的策略来决定是回滚还是继续发送确认消息。这样就保证了消息发送与本地事务同时成功或同时失败
2. 优点:
(1)实现了最终一致性,不需要依赖本地数据库事务
3. 缺点:
(1)实现难度大,主流MQ不支持,没有.NET客户端,RocketMQ事务消息部分代码也未开源	

六、 MQ非事务消息(加独立消息服务、或者本地事务表)

另外一种实现,并不是所有的mq都支持事务消息。也就是消息一旦发送到消息队列中,消费者立马就可以消费到。此时可以使用独立消息服务、或者本地事务表
在这里插入图片描述

1. 思路:
1)将消息先发送到一个我们自己编写的一个"独立消息服务"应用中,刚开始处于prepare状态

2)业务逻辑处理成功后,确认发送消息,这个时候"独立消息服务"才会真正的把消息发送给消息队列

3)消费者消费成功后,ack时,除了对消息队列进行ack(图中没有画出),对于独立消息服务也要进行ack,"独立消息服务"一般是把这条消息删除。而定时扫描prepare状态的消息,向消息发送端(生产者)确认的工作也由独立消息服务来完成
2. 对于"本地事务表",其实和"独立消息服务"的作用类似,只不过"独立消息服务"是需要独立部署的,而"本地事务表"是将"独立消息服务"的功能内嵌到应用中

七、柔性事务:最大努力通知(补偿?)

最大努力通知型( Best-effort delivery)是最简单的一种柔性事务,适用于一些最终一致性时间敏感度低的业务,且被动方处理结果 不影响主动方的处理结果。典型的使用场景:如银行通知、商户通知等。最大努力通知型的实现方案,一般符合以下特点:
在这里插入图片描述

1. 不可靠消息:业务活动主动方,在完成业务处理之后,向业务活动的被动方发送消息,直到通知N次后不再通知,允许消息丢失(不可靠消息)
2. 定期校对:业务活动的被动方,根据定时策略,向业务活动主动方查询(主动方提供查询接口),恢复丢失的业务消息

八、Sagas长事务

在Sagas事务模型中,一个长事务是由一个预先定义好执行顺序的子事务集合和他们对应的补偿子事务集合组成的。典型的一个完整的交易由T1、T2、…、Tn等多个业务活动组成,每个业务活动可以是本地操作、或者是远程操作,所有的业务活动在Sagas事务下要么全部成功,要么全部回滚,不存在中间状态
在这里插入图片描述

1. 实现机制
1)每个业务活动都是一个原子操作

2)每个业务活动均提供正反操作

3)任何一个业务活动发生错误,按照执行的反顺序,实时执行反操作,进行事务回滚

4)回滚失败情况下,需要记录待冲正事务日志,通过重试策略进行重试

5)冲正重试依然失败的场景,提供定时冲正服务器,对回滚失败的业务进行定时冲正

6)定时冲正依然失败的业务,等待人工干预
2. 优点
1)Sagas长事务模型支持对数据一致性要求比较高的场景比较适用,由于采用了补偿的机制,每个原子操作都是先执行任务,避免了长时间的资源锁定,能做到实时释放资源,性能相对有保障。
3. 缺点
1)Sagas长事务方式如果由业务去实现,复杂度与难度并存
4. Sagas长事务模型框架

在这里插入图片描述

1)开发人员:业务只需要进行交易编排,每个原子操作提供正反交易

2)配置人员:可以针对异常类型设定事务回滚策略(哪些异常纳入事务管理、哪些异常不纳入事务管理);每个原子操作的流水是否持久化(为了不同性能可以支持缓存、DB、以及扩展其它持久化方式);以及冲正选项配置(重试次数、超时时间、是否实时冲正、定时冲正等);

3)Sagas事务框架:提供事务保障机制,负责原子操作的流水落地,原子操作的执行顺序,提供实时冲正、定时冲正、事务拦截器等基础能力;

4)Sagas框架的核心是IBusinessActivity、IAtomicAction。IBusinessActivity完成原子活动的enlist()、delist()、prepare()、commit()、rollback()等操作;IAtomicAction主要完成对状态上下文、正反操作执行。

参考网址

7.0 柔性事务:可靠消息最终一致性(一系列)

分布式事务:不过是在一致性、吞吐量和复杂度之间,做一个选择

分布式事务之一:整体介绍

聊聊分布式事务,再说说解决方案

分布式事务一(总结的一些列文章,很好,待读)

分布式事务解决办法

MySQL分布式事务(新鲜玩法,待读)

分布式系统数据一致性的6种方案(几个公司的解决方案,待读)

注:文章是经过参考其他的文章然后自己整理出来的,有可能是小部分参考,也有可能是大部分参考,但绝对不是直接转载,觉得侵权了我会删,我只是把这个用于自己的笔记,顺便整理下知识的同时,能帮到一部分人。
ps : 有错误的还望各位大佬指正,小弟不胜感激

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值