探索分布式事务解决方案:八种方案解析
前面已经学习了分布式事务的基础理论CAP 理论和 BASE 理论,以理论为基础,针对不同的分布式场景业界常见的解决方案有2PC、TCC、可靠消息最终一致性、最大努力通知等方案,**以下 总结8 种常见的解决方案,取名八奇技**。帮助大家在实际的分布式系统中更好地运用事务。。
二阶段提交协议(Two-phase commit protocol),简称 2PC。2PC是将整个事务流程分为两个阶段:
2是指两个阶段,P是指准备阶段,C是指提交阶段
在计算机中部分关系数据库如Oracle、MySQL支持两阶段提交协议,如下图:
注意:必须在最后阶段释放锁资源
下图展示了2PC的两个阶段,分成功和失败两个情况说明:
2PC优缺点:
优点
缺点:
3PC,即Three-Phase Commit,是一种分布式事务协议,用于在分布式系统中确保多个参与者之间的事务操作的一致性和可靠性。它是在两阶段提交(2PC)协议的基础上发展而来,解决了2PC协议可能出现的悬挂事务问题。
3PC协议将提交操作分为三个阶段,分别是准备阶段、提交准备阶段和提交阶段,每个阶段都有对应的操作和协议。
准备阶段(CanCommit):
提交准备阶段(PreCommit):
提交阶段(DoCommit/DoAbort):
3PC协议相对于2PC协议的改进在于增加了一个准备阶段,使得参与者在准备阶段就能够知道是否可以提交事务,从而避免了悬挂事务问题。然而,3PC协议仍然存在着协调者单点故障、消息丢失等问题,因此在实际应用中并不常见,一般更多地使用2PC、Saga等分布式事务解决方案
TCC是Try、Confirm、Cancel三个词语的缩写,TCC要求每个分支事务实现三个操作:预处理Try、确认Confirm、撤销Cancel。Try操作业务检查及资源预留,Confirm做业务确认操作,Cancel实现一个与Try相反的操作即回滚操作。TM首先发起所有的分支事务的try操作,任何一个分支事务的try操作执行失败,TM将会发起所有分支事务的Cancel操作,若try操作全部成功,TM将会发起所有分支事务的Confirm操作,其中Confirm/Cancel操作若执行失败,TM会进行重试。
TCC分为三个阶段
TCC需要注意三种异常处理
空回滚
在没有调用 TCC 资源 Try 方法的情况下,调用了二阶段的 Cancel 方法,Cancel 方法需要识别出这是一个空回滚,然后直接返回成功。
出现原因:是当一个分支事务所在服务宕机或网络异常,分支事务调用记录为失败,这个时候其实是没有执行Try阶段,当故障恢复后,分布式事务进行回滚则会调用二阶段的Cancel方法,从而形成空回滚。
解决思路是
关键就是要识别出这个空回滚。思路很简单就是需要知道一阶段是否执行,如果执行了,那就是正常回滚;如果没执行,那就是空回滚。
幂等
TCC二阶段提交重试机制不会引发数据不一致,要求 TCC 的二阶段 Try、Confirm 和 Cancel 接口保证幂等,这样不会重复使用或者释放资源。如果幂等控制没有做好,很有可能导致数据不一致等严重问题。
解决思路在上述“分支事务记录”中增加执行状态,每次执行前都查询该状态。
悬挂
悬挂就是对于一个分布式事务,其二阶段 Cancel 接口比 Try 接口先执行。
出现原因:在 RPC 调用分支事务try时,先注册分支事务,再执行RPC调用,如果此时 RPC 调用的网络发生拥堵,通常 RPC 调用是有超时时间的,RPC 超时,TM就会通知RM回滚该分布式事务,可能回滚完成后,RPC 请求才到达参与者真正执行,而一个 Try 方法预留的业务资源,只有该分布式事务才能使用,该分布式事务第一阶段预留的业务资源就再也没有人能够处理了,对于这种情况,我们就称为悬挂,即业务资源预留后没法继续处理。
解决思路:如果二阶段执行完成,那一阶段就不能再继续执行。在执行一阶段事务时判断在该全局事务下,“分支事务记录”表中是否已经有二阶段事务记录,如果有则不执行Try。
TCC优缺点:
TCC的优点:
TCC的缺点
4. 分布式补偿事务(Saga)
Saga是一种长事务的解决方案,它将一个大的分布式事务拆分成多个较小的本地事务,并通过异步消息传递来串联这些本地事务。每个本地事务执行成功后,会发送消息触发下一个事务的执行。如果某个本地事务失败,Saga会执行一系列补偿操作,保持数据的一致性。
分布式补偿事务(Saga) 优缺点
优点
缺点
在选择使用Saga模式时,需要仔细考虑业务场景是否适合最终一致性,以及是否能够有效地实现和管理补偿逻辑。对于需要高度一致性保证的场景,可能需要考虑其他事务管理机制。Saga模式在适当的情况下可以为分布式系统带来灵活性和容错性,但需要慎重考虑其复杂性和实现难度。
5. 可靠消息最终一致性
可靠消息最终一致性方案:是指当事务发起方执行完成本地事务后并发出一条消息,事务参与方(消息消费者)一定能够接收消息并处理事务成功,此方案强调的是只要消息发给事务参与方最终事务要达到一致。
此方案是利用消息中间件完成,如下图:
事务发起方(消息生产方)将消息发给消息中间件,事务参与方从消息中间件接收消息,事务发起方和消息中间件之间,事务参与方(消息消费方)和消息中间件之间都是通过网络通信,由于网络通信的不确定性会导致分布式事务问题。
可靠消息最终一致性方案要解决以下几个问题
1. 本地事务与消息发送的原子性问题
本地事务与消息发送的原子性问题即:事务发起方在本地事务执行成功后消息必须发出去,否则就丢弃消息。即实现本地事务和消息发送的原子性,要么都成功,要么都失败。本地事务与消息发送的原子性问题是实现可靠消息最终一致性方案的关键问题。 先来尝试下这种操作,先发送消息,再操作数据库:
; transation
这种情况下无法保证数据库操作与发送消息的一致性,因为可能发送消息成功,数据库操作失败立马想到第二种方案,先进行数据库操作,再发送消息:
; transation
这种情况下貌似没有问题,如果发送MQ消息失败,就会抛出异常,导致数据库事务回滚。但如果是超时异常,数据库回滚,但MQ其实已经正常发送了,同样会导致不一致。
2. 事务参与方接收消息的可靠性
事务参与方必须能够从消息队列接收到消息,如果接收消息失败可以重复接收消息。
3. 消息重复消费的问题
由于网络2的存在,若某一个消费节点超时但是消费成功,此时消息中间件会重复投递此消息,就导致了消息的重复消费。要解决消息重复消费的问题就要实现事务参与方的方法幂等性
6. 本地消息表方案
本地消息表这个方案最初是eBay提出的,此方案的核心是通过本地事务保证数据业务操作和消息的一致性,然后通过定时任务将消息发送至消息中间件,待确认消息发送给消费方成功再将消息删除。
下面以注册送积分为例来说明: 共有两个微服务交互,用户服务和积分服务,用户服务负责添加用户,积分服务负责增加积分。
交互流程
; transation
这种情况下,本地数据库操作与存储积分消息日志处于同一个事务中,本地数据库操作与记录消息日志操作具备原子性。
思考:如何保证将消息发送给消息队列呢?
如何保证消费者一定能消费到消息呢?
积分服务接收到”增加积分“消息,开始增加积分,积分增加成功后向消息中间件回应ack,否则消息中间件将重复 投递此消息。由于消息会重复投递,积分服务的”增加积分“功能需要实现幂等性
7.最大努力通知原则
最大努力通知也是一种基于消息的分布式事务解决方案,但它不保证 100% 的消息传递成功。它的工作原理是:
解决方案思想不同
可靠消息一致性,发起通知方需要保证将消息发出去,并且将消息发到接收通知方,消息的可靠性关键由发起通知方来保证。
两者的业务应用场景不同
技术解决方向不同
8. 分布式锁
在某些业务场景,使用分布式锁是确保多个分布式节点不会同时操作同一资源的有效方法。这一机制可以通过使用像Redis、ZooKeeper等分布式协调服务来实现
应用场景: 在电商秒杀活动中,为了防止超卖现象,需要确保同一时间只有一个请求能够对库存数量进行修改。这时,可以使用Redis作为分布式锁的后端存储,以确保秒杀活动的进行顺利和公平。
推荐场景: 当需要协调多个节点对共享资源进行访问控制时,分布式锁是一个非常有效的解决方案。例如,在分布式系统中,多个节点需要同时对同一资源进行读取或更新操作时,为了保证数据的一致性和避免竞态条件,可以使用分布式锁来进行并发控制。
本网站的文章部分内容可能来源于网络和网友发布,仅供大家学习与参考,如有侵权,请联系站长进行删除处理,不代表本网站立场,转载者并注明出处:https://jmbhsh.com/toutiao/34347.html