您的位置:首页 > 其它

分布式事务的有关思考

2018-01-24 15:58 295 查看
首先从一个问题入手,在本人实际工作中,为实现一项功能,有以下事务操作:
public Object transactionMethod(){
A.operate1();
A.operate2();
B.operate();
return success;
}


在没有进行数据库拆库前,A表和B表都属于同一个库,这项功能只要保证在一个事务中执行即可。但进行数据库拆库后,A和B属于不同的数据库,需要对上面的代码进行改造,操作B时需要通过远程调用实现。虑以下几种情况:

try{
begin transaction;
A.operate1
A.operate2//这一步置中间状态标志位
commit transaction;
}catch(Exception e){
rollback;
return fail;
}
try{
if(call remote B success){
update 中间状态标志位置成功
return success;
}else{
回滚A,update 中间状态标志位置失败(事务操作)
return fail;
}

}catch Exception(Exception e){
//走到这里一般来说是网络异常了
撤销B的操作(由于此时不知道B是否已经进行了修改)
回滚A,update中间状态标志位置失败(事务操作)
return fail;
}


1,需要明确知道调用B的结果后A才能返回,即A对B强依赖:A的操作完成后,设置一个中间状态,然后提交事务,远程调用B成功后,更新这个中间状态,返回成功,其伪代码大致如下:

下面针对上面的伪代码思考几个问题:

a,如果执行A的事务操作过程中系统A崩溃或者异常了,这时事务还没有提交,返回失败,数据正常!

b,如果A事务执行成功后,系统A崩溃,如何处理?这种情况下,需要通过补偿job定时扫描这个中间状态标志位,回滚A的操作,然后更新其标志位状态为失败。这里有个问题,正常的操作(调用B之前)也可能被这个job扫描出来,为了避免这种情况,需要引入一个时间戳。job只扫描时间戳与当前时间间隔一定范围的数据。

c,如果调用B的过程中,系统A挂了,如何处理?这种情况下首先同b,由补偿job去扫描这个中间状态,不同的是首先得调用B的撤销接口(因为不知道此时B的状态,统一撤销掉),如果撤销成功,执行A的回滚操作,再更新中间标志位为失败。如果调用撤销接口失败,重试。如果更新标志位过程中或者回滚过程中出现异常,重试,但此时需要B的撤销接口支持幂等!所以这种情况下,需要B的撤销接口有以下功能,如果没有对应的记录,需要返回撤销成功,同时要支持幂等!

d,如果网络异常,撤销B的操作失败了,如何处理?此时返回失败,由补偿job去执行,同c。如果回滚A,update中间出现了异常,依靠job,同c。

e,如果撤销B的操作代价很大,可能耗时比较长,同时远程同步调用B的撤销接口还得处理下接口的异常以及网络超时,放在此处影响性能,如何处理?撤销B的操作改为发送MQ消息,由B进行消费。一般来说我们把消息到MQ中间件,由中间件来保证消费成功。只要发送MQ消息成功,即可执行下一步操作。如果此时发送MQ异常,则交由job去重试,捞出中间状态的数据,执行发送mq和回滚A,update中间状态的操作,此时需要B接口支持米等!

2,A对B的依赖性不是那么强,比如A系统操作完成后,需要将变更的数据同步给系统B,此时如何处理?

系统A先执行事务操作,在这个事务中,将要变更的数据写入到数据表中,置一个状态,由job定时将数据同步给B,同步成功后,更新状态。

3,假如A要完成的功能很复杂,存在这样的调用链A----->B----->C----->D----->E---->F---->G,其中G之前的都成功了,调用G的时候失败了,这时如果直接依次调用各个系统的撤销接口,代价是很大的,如何处理?

采用发送mq消息和job结合的处理方式,这是所有提供撤销接口的系统都需要支持米等!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  分布式系统事务