分布式事物

目录

1.单机下 的事物

2.分布式事物

3.保证分布式事物的几种方案

3.1两阶段提交(2PC)

3.2三阶段提交(3PC)

3.3 TCC

3.4本地消息表

3.5消息事务


1.单机下 的事物

在单机单节点的情况下,单个逻辑下执行多个操作,这些操作要么全部成功,要么全部失败,这就是事物。

2.分布式事物

在实际的企业生产环境中,不可能一直是单节点的操作,当涉及到多节点的处理时,我需要操作多个分布于不同节点的数据库,这种情况就是分布式事物了。

举个例子,大家耳熟能详的下单业务。

 一个下单动作,减库存之后,生成订单,这两个操作必须全部成功,不可能说我生成了一条新的订单,但是库存扣失败了,也不可能说我库存扣成功了,但是订单没生成,这些都会导致数据的不一致性。

那么如何保证多数据源的一致性,就涉及到了分布式事物。

3.保证分布式事物的几种方案

保证分布式事物,可以从数据库层面,业务层面下手,我们先看如何从数据库层面着手:

3.1两阶段提交(2PC)

2PC是一种强一致性设计,包含两个角色:事物协调者,事物参与者。

第一阶段:

事物协调者会向所有参与者结点发送prepare请求,接受到prepare请求的参与者会先占据资源并各自执行本地事物相关的更新,做完更新后不会马上提交事务,而是返回消息给事务协调者表示自己已经准备好可以提交事务了。

第二阶段:

第一种情况,如果第一阶段所有的事物参与者返回的都是正向反馈消息,那么事务协调者会向所有参与者发送提交事务的请求,接收到提交请求的事物参与者各自提交本地事务,完成提交后释放本地资源并返回消息给协调者。等所有事务参与者都返回成功,协调者返回事务完成。

 第二种情况,如果第一阶段有一个事务参与者返回失败消息,说明该节点的本地事务执行失败,需要立即回滚,只要有一个参与者本地事务执行失败,其他所有的参与者都需立即回滚本地事务,接收到回滚信息的参与者将本地事务回滚并返回消息给事务协调者,分布式事物执行失败。

 两阶段提交弊端

在第一阶段,如果某个参与者一直没有响应,在超过了指定超时时间后,协调者会返回事务执行失败,没问题,我们接着分析,其他的参与者会一直等待所有的结点都准备完毕,在这个过程中,各参与者都是占据了资源的,会有性能问题存在。

在第二阶段,如果因为局部网络发生故障导致有些参与者没有收到提交信息,有些参与者收到了信息,(参与者发送回滚命令也是如此),那么这个时候,我们只能不断的去重试,向失联的参与者发送信息,直到他们响应,如果不这样不停的去重试,那么就会造成数据的不一致性。

协调者是单个结点,如果协调者发生了故障的话,我们来分析一下会有什么问题:

1.如果协调者在第一阶段发送prepare命令之前挂了,说明事务没有开始,不影响。

2.如果协调者在第一阶段发送prepare命令之后挂了,此时参与者阻塞在那,等待协调者后续的消息,并且占据了公共资源,阻塞了系统的其他操作。

3.如果协调者在第二阶段发送提交命令或回滚命令之前挂了,那么所有参与者都会阻塞

4.如果协调者在第二阶段发送提交命令或回滚命令之后挂了,很大概率上参与者接收命令都会执行各自的事物并释放资源,极端情况下,因为局部网络问题,部分参与者没有接收到命令而阻塞住。

3.2三阶段提交(3PC)

三阶段提交是在2pc的基础上增加了CanCommit的阶段,在2pc阶段,如果参与者迟迟等不到协调者的消息,那么会一直阻塞等待着,在3pc阶段引入了超时机制,如果超出时间了,参与者自动提交本本地事务,不需要一直等协调者的通知。

第一阶段(CanCommit)

在2pc,第一阶段是准备阶段,此时参与者是做了本地事务更新的,但是没有提交本地事务,在3pc,协调者只是询问参与者的状态,即结点是否在线,这样不会一上来就直接锁定资源,如果在提交命令前协调者发生故障,则所有的节点都会处于阻塞状态,资源得不到释放,确定各结点状态正常,进入第二阶段。

第二阶段(PreCommit)

第二阶段与2pc的准备阶段相似,参与者接收到消息后,执行本地事务更新,但是不会提交事务并返回完成消息给协调者。

第三阶段(DoCommit)

如果在第二阶段,所有参与者都是正向反馈,表明各节点的本地事务已经成功提交,协调者就会发出提交请求,接收到提交请求的各节点则提交本地事务并返回消息给协调者。

 

如果在第二阶段,有一个参与者返回失败,则表明分布式事物失败,此时协调者发送回滚命令,各参与者收到消息后执行回滚操作,并返回消息给协调者。

 我们来分析一下,如果在第二阶段之后,参与者等待协调者的提交命令,但是协调者挂了,我们知道,3pc引入了超时机制,过了超时时间,则参与者自动进行本地事务提交,但是我们不知道是要提交事务还是要回滚事务呀,这就会导致数据的不一致。并且多了一个阶段就多了一个交互过程,性能也会下降。

3.3 TCC

上述两种方式是从数据库层面入手,TCC是从业务层面设计的,逻辑和2pc,3pc差不多。因为是业务层实现,与业务耦合度高,实现起来比较复杂。

TCC(try-confirm-cancel),try是资源预留和锁定,confirm是确认操作,真正提交事务,cancel是撤销操作,把尚未提交的事物撤销。

TCC的思路类似于2PC,有个事务管理者的角色类似于协调者,负责记录事务的全局状态并提交或回滚事务。

既然TCC是从业务角度设计的,那么我们也跟业务挂钩,来分析一下TCC到底是什么,如下所示:

这个是开头我们讲过的下单逻辑,在主逻辑里面涉及到扣库存和生成订单两个操作,这两个操作要么同时成功,要么同时失败,所以主逻辑里面要用到分布式事物。

第一步try:

try阶段是做资源预留的,在这一步我们不是真的要去做数据库的更新,我们先看库存数量-1是否大于等于0,如果库存-1<0那么接下来的操作肯定失败,我们设置一个预减库存字段,将该字段设置为1表明我后面要对库存做减法操作了,对于订单表,我们生成一条订单,并新增字段订单状态,try阶段将该字段设置为checked。

第二步confirm:

如果上述两个try操作成功了,可以进行真正的更新了,将库存数量-1,并将预减库存字段设置为0,订单表的字段状态设置为success。

第三步cancel :

如果上述两个try有一个失败了,那我们就将预减库存字段设置为0,库存数量不变,订单表的字段状态设置为fail。

注意:要完成TCC分布式事物,必须得借助分布式事物框架,否则我们要知道其他系统是否try成功,他们状态如何,在业务层面代码十分复杂。

3.4本地消息表

将业务的执行与本地消息表放在一个本地事务里面,主业务发起调用后,生成本地消息表,消息表的状态为未完成,如果下游系统调用成功,则将状态改为成功,如果失败,后台会有定时任务去推进这条消息的执行,重新去调用下游系统。所以下游系统需要在业务上做好幂等的设计。防止重复调用。

3.5消息事务

如下所示:

1.上游系统将对下游接口的调用信息放入到消息事务中,放入成功的消息状态为待确认

2.上游系统开始执行本地事务,执行成功后,向消息事务发送消息,消息事务将该消息状态设置为已确认,如果本地事务执行失败了,该消息就被删除。

3.下游系统从消息事务中消费消息,消息获取到后,执行本地事务,执行本地事务成功的话,该条消息状态设置为成功,失败的话,该消息的状态还是确认状态。此时消息事务将消息重新投递到下游系统继续处理。

RocketMQ中间件就很好的完成了这样的功能。更多详情可以自己网上搜集资料看看,这里就不多说了。

 综上来说,一般业务系统对数据的一致性要求不是特别高的情况下,可以使用本地消息表,以及基于RocketMQ中间件的消息事务的方式来完成分布式事物,从而保证数据的最终一致性。

可以参考链接:

面试必问:分布式事务六种解决方案 - 知乎

终于有人把“TCC分布式事务”实现原理讲明白了! - JaJian - 博客园

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值