APP下载

《分散式事务解决方案》

消息来源:baojiabao.com 作者: 发布时间:2024-05-15

报价宝综合消息《分散式事务解决方案》

将一个整体模组拆分为多个微服务,某些业务场景需要同时操作多个原子服务的资料,分散式事务就是用来保证多个原子服务资料来源一致性的解决方案。

分散式事务产生的原因?

数据库分库分表:由于单表的资料量巨大需要分库分表,分库分表之后,此时一个操作可能涉及访问多个数据库,为了保证资料一致性,就需要用到分散式事务。 应用SOA化:指的是业务的服务化,将一个整体的系统拆分为多个子系统,每个子系统都有自己的数据库,为了保证资料一致性,就需要用到分散式事务。

常见的分散式解决方案

1. 两阶段提交/XA(2PC)

两阶段提交的算法思路可以概括为:参与者将操作成功或失败的结果通知协调者,再由协调者根据所有参与者的反馈情况决定个参与者是否要提交操作还是中止操作。

第一阶段:请求阶段(投票阶段)

协调者向所有的参与者传送事务执行请求,并等待参与者反馈事务执行结果。事务参与者收到请求之后,本地执行事务,但不提交。参与者将自己事务执行情况反馈给协调者,同时等待协调者的下一步通知。第二阶段:提交阶段(执行阶段)

在第一阶段协调者的询盘之后,各个参与者会回复自己事务的执行情况,这时候存在三种可能:

所有的参与者回复能够正常执行事务。协调者向各个参与者传送commit通知,请求提交事务。参与者收到事务提交通知之后,执行commit操作。参与者向协调者返回事务commit结果资讯。一个或多个参与者回复事务执行失败。协调者等待超时。协调者向各个参与者传送事务rollback通知,请求回滚事务。参与者收到事务回滚通知之后,执行rollback操作。参与者向协调者返回事务rollback结果资讯。两阶段提交的缺点

同步阻塞:执行过程中,所有参与者的节点都是事务阻塞型的。当参与者占用公共资源时,其他第三方节点访问公共资源不得不处于阻塞状态。单点故障:由于协调者的重要性,一旦协调者发生故障,参与者会一直阻塞,尤其时在第二阶段,协调者发生故障,那么所有的参与者都处于锁定事务资源的状态中,而无法继续完成事务操作。(如果是协调者挂掉,可以重新选举一个协调者,但是无法解决因为协调者宕机导致的参与者处于阻塞状态的问题)资料不一致:在第二阶段中,当协调者想参与者传送commit请求之后,发生了局部网络异常或者在传送commit请求过程中协调者发生了故障,这会导致只有一部分参与者接收到了commit请求。而在这部分参与者接到commit请求之后就会执行commit操作。但是其他部分未接收到commit请求的节点则无法提交事务。于是,整个分散式系统就出现了资料不一致的现象。两阶段提交无法解决的问题

当协调者和参与者同时出现故障时,两阶段提交无法保证事务的完整性。如果调者在发出commit讯息之后宕机,而唯一接收到commit讯息的参与者同时也宕机了。那么即使协调者通过选举协议产生了新的协调者,这条事务的状态也是不确定的,因为没人知道事务是否已经被提交。

2. 三阶段提交

第一阶段:CanCommit

协调者向参与者传送事务执行请求CanCommit,参与者如果可以提交就返回YES响应,否则就返回NO响应。

第二阶段:PreCommit

协调者根据参与者反馈的结果来决定是否继续执行事务的PreCommit操作,根据协调者反馈的结果,有以下两种可能:

假如协调者收到参与者的反馈结果都是YES,那么就会执行PreCommit操作。传送预提交请求:协调者向参与者传送PreCommit请求,并进入Prepared阶段。事务预提交:参与者接收到PreCommit请求后,执行事务操作。响应反馈:事务操作执行成功,则返回ACK响应,然后等待协调者的下一步通知。假如有任何一个参与者向协调者传送了NO响应,或者等待超时之后,协调者没有收到参与者的响应,那么就中断事务。传送中断请求:协调者向所有参与者传送中断请求。中断事务:参与者收到中断请求之后(或超时之后,仍未收到协调者的请求),执行事务中断操作。第三阶段:DoCommit

执行提交传送提交请求:协调者收到ACK之后,向所有的参与者传送DoCommit请求。事务提交:参与者收到DoCommit请求之后,提交事务。响应反馈:事务提交之后,向协调者传送ACK响应。完成事务:协调者收到ACK响应之后,完成事务。中断事务 在第二阶段,协调者没有收到参与者传送的ACK响应,那么就会执行中断事务。3. 补偿事务(TCC)

TCC是一种比较成熟的分散式事务解决方案,可用于解决跨库操作的资料一致性问题,适用于公司内部对一致性、实时性要求较高的业务场景。其中Try、Confirm、Cancel 3个方法均由业务编码实现。其中Try操作为第一阶段,负责资源的检查和预留;Confirm操作为第二阶段,执行真正的业务操作;Cancel时执行取消(回滚)操作。 业务实现TCC服务之后,该TCC服务将作为分散式事务的其中一个资源,参与到整个分散式事务中;事务管理器分两阶段协调的TCC服务,第一阶段呼叫所有TCC服务的Try方法,在第二阶段执行所有TCC服务的Confirm或者Cancel方法。

实现TCC服务时注意事项

业务操作分两阶段完成 接入TCC前,业务操作只需要一步就能完成,但是在接入TCC之后,需要考虑如何将其分成2阶段完成,把资源的检查和预留放在一阶段的Try操作中进行,把真正的业务操作的执行放在二阶段的Confirm操作中进行。TCC服务要保证第一阶段Try操作成功之后,二阶段Confirm操作一定能成功。

2. 允许空回滚事务协调器在呼叫TCC服务的一阶段Try操作时,可能会出现因为丢包而导致的网络超时,此时事务协调器会触发二阶段回滚,呼叫TCC服务的Cancel操作;TCC服务在未收到Try请求的情况下收到Cancel请求,这种场景被称为空回滚;TCC服务在实现时应当允许空回滚的执行。

3. 防悬挂控制事务协调器在呼叫TCC服务的一阶段Try操作时,可能会出现因网络拥堵而导致的超时,此时事务协调器会触发二阶段回滚,呼叫TCC服务的Cancel操作;在此之后,拥堵在网络上的一阶段Try资料包被TCC服务收到,出现了二阶段Cancel请求比一阶段Try请求先执行的情况;使用者在实现TCC服务时,应当允许空回滚,但是要拒绝执行空回滚之后到来的一阶段Try请求。

4. 幂等控制无论是网络资料包重传,还是异常事务的补偿执行,都会导致TCC服务的Try、Confirm或者Cancel操作被重复执行;使用者在实现TCC服务时,需要考虑幂等控制,即Try、Confirm、Cancel 执行一次和执行多次的业务结果是一样的。

举例,假入 Bob 要向 Smith 转账,思路大概是: 1. 首先在 Try 阶段,要先呼叫远端界面把 Smith 和 Bob 的钱给冻结起来。 2. 在 Confirm 阶段,执行远端呼叫的转账的操作,转账成功进行解冻。 3. 如果第2步执行成功,那么转账成功,如果第二步执行失败,则呼叫远端冻结界面对应的解冻方法 (Cancel)。

4. 本地讯息表

基于本地讯息的最终一致性方案的最核心做法就是在执行业务操作的时候,记录一条讯息资料到DB,并且讯息资料的记录与业务资料的记录必须在同一个事务内完成,这是该方案的前提核心保障。在记录完成后讯息资料后,后面我们就可以通过一个定时任务到DB中去轮询状态为待发送的讯息,然后将讯息投递给MQ。这个过程中可能存在讯息投递失败的可能,此时就依靠 重试机制 来保证,直到成功收到MQ的ACK确认之后,再将讯息状态更新或者讯息清除;而后面讯息的消费失败的话,则依赖MQ本身的重试来完成,其最后做到两边系统资料的最终一致性。基于本地讯息服务的方案虽然可以做到讯息的最终一致性,但是它有一个比较严重的弊端,每个业务系统在使用该方案时,都需要在对应的业务库建立一张讯息表来储存讯息。

5. MQ事务讯息

RocketMQ中介软件能够支援一种事务讯息机制,确保本地操作和传送讯息的异步处理达到本地事务的结果一致。

第一阶段,生产者在执行事务之前,首先向MQ传送一个Prepare讯息(讯息储存在broker中,不会被讯息者看到),RocketMQ能够拿到讯息的地址。第二阶段,生产者执行本地事务操作。第三阶段,确认讯息传送,通过第一阶段拿到的地址取访问讯息,并修改状态。

注:如果确认讯息传送失败, RocketMQ会定期扫描讯息丛集中的事务讯息 ,如果发现了Prepare讯息,它会向讯息的传送者确认本地事务是否已执行成功,然后再根据业务实现的策略决定时继续传送还是回滚(讯息生产者需要设定监听)。

6. 最大努力通知

举例,订单支付之后,支付宝向商户推送支付结果,如果商户没有回复Success,支付宝会重复推送N次支付结果。

感谢您耐心看完了文章...

关注作者:JAVA高阶程序员

我会不定期在微头条发放:(Java工程化、分散式架构、高并发、高效能、深入浅出、微服务架构、Spring、MyBatis、Netty、源代码分析)等技术学习资料,以及Java进阶学习路线图。

2019-06-30 16:48:00

相关文章