分布式事务详解

来源:互联网 发布:ubuntu 休眠 编辑:程序博客网 时间:2024/05/17 22:02

到目前为止,分布式事务仍是个技术性难题,仍没有一个银弹能完美的解决跨事务的问题,本文结合自己实际应用中的案例和一些成熟设计与大家探讨下。


 

方式一:

XA协议(二阶段提交)

XA协议是解决跨事务问题的最老的解决方案,它将对事务的管理拆分为“事务管理器TM”和“资源管理器RM”,由TM控制RM完成二阶段提交,主流关系型数据库中以Oracle为代表。

优点:开发方便,只需要当作2个数据源分别去操作业务数据既可,对XA的调用可以丢给容器去控制。

缺点:只有部分关系型数据库支持XA,而且支持的程度有好有坏,NoSqlDB都不支持。XA带来的性能上的损耗是灾难性的,2个数据源做XA经验值会损耗一半。


 

方式二:

串行调用+业务补偿

这个是我们项目现在使用的解决方案,场景如下:工单流程处理时需要先处理我们自己系统的业务资源,处理成功后需要调用一个webservice接口通知另一个系统同步处理他们的资源,要保证这两部分处理同时成功和失败。

解决方法:先处理系统A并提交A的事务,然后通知系统B处理并提交事务;如果系统B也反馈成功,本次处理结束;如果系统B返回失败,触发系统A的业务补偿机制逆向实现回退功能。

该方法需要充分评估系统提交的先后顺序,将容易逆向恢复、成功率较低的系统放在前面先调用,减少回滚的概率。

优点:比较好理解,不受数据库和软件框架的限制。

缺点:1逆向回滚的代码实现起来往往比正向的要复杂的多,而且不是所有的业务都可以补偿的。2串联越长,需要考虑的场景越多,补偿越复杂。

 

 

方式三:

分布式本地化+最终一致性

这里需要引入一个概念,凭证,就像我们去Costa或者星爸爸那里买咖啡,并不是一手交钱一手交货的,而是一手交钱一手交小票,那张小票就是咖啡的凭证。

分布式事务本地化,就是把跨系统的事务处理中非本地事务凭证化。

首先,对于系统A来说将对系统B的操作以本地表的形式凭证化,我只要保证系统A的业务处理与对B凭证的处理在本地事务中同时成功同时失败就可以(这个关系型数据库中非常容易实现)。

然后,通过轮询+MQ的方式将凭证转化为对系统B真正的数据处理,同时系统B自己成功处理过的凭证需要本地记录下,避免因为系统A的重复调用导致数据不准确(多给一杯咖啡是小事,多给100WRMB程序员要进监狱的)。

最后,系统B要定期的将已处理的凭证同步给系统A,让A修改凭证的消费状态,减少无效轮询,防止重复调用。

优点:设计严谨并且非常优美的解决了分布式事务的问题,也不需要补偿代码,结点和复杂度的提高并不会给架构带来太大的冲击,性能、开发量、扩展等各方面平衡性比较强没有明显短板,这也是分布式事务最典型的处理案例。

缺点:方案的前提是系统B业务上能“忍受”最终一致性这一前提,没有这一前提一切都是空谈。(例如我们项目上要求A的业务处理完毕后立刻要对B新的数据进行处理,这不符合最终一致性,所以放弃了这个方案)。

 

 

其它方案:

网上还了结果TCC事务处理,try+confirm+cancel,本质上也是跨应用版的XA,是二阶段提交+业务补偿的一个变种,也存在开发繁琐这一缺点,这里不再细说。

 

总结:

通过任何方案来解决分布式事务都要给自己留好最后一个后门“人工补偿”,因为我们不能保证事物都是按照我们想象中的那样运行的(例如补偿机制有bug、系统宕机、网络丢包等等),所以一定要多留下一个痕迹供我们最后去人工排查。同理代码实现上一定要“幂等性”,因为你无法控制客户端重复请求的情况,“幂等性”可以保证大家不要“把话聊死”,在做一些数据同步和补救时“幂等性”会显得尤为重要。

还是那句话,分布式事务没有银弹,只能根据自己的业务特点取其平衡,按照不控制、部分控制、全部控制的性能优先级结合自己的业务需求来选择和设计属于自己产品的分布式事务。