vlambda博客
学习文章列表

分布式事务是啥?常用的解决方案有哪些?

所谓事务就是为了保证数据的完整性和一致性,事务管理是应用开发中必不可少的技术。要明确一点的是在传统单体项目中我们操作的事务都是本地事务所谓本地事务,是指该事务仅在当前工程内有效

总的来说事务是逻辑上一组操作,组成这组操作的各个逻辑单元,要么一起成功,要么一起失败。


关于事务的隔离级别(读未提交、读已提交、可重复读)和传播行为(REQUIREDSUPPORTSMANDATORYREQUIRES_NEW….)此处不做过多的描述。 


分布式事务

传统的一个工程内为了保证数据的一致性,使用地事务。本地事务只能解决同一工程中的事务问题,而现在的场景更加复杂,关系到两个甚至多个工程模块,怎么保证要么都成功,要么都失败?

分布式事务指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上。

简单的说,就是一次大的操作由不同的小操作组成,这些小的操作分布在不同的服务器上,且属于不同的应用,分布式事务需要保证这些小操作要么全部成功,要么全部失败


1

分布式事务场景





1. 不同微服务之间会有分布式事务问题  

2. 同一工程不同数据库            

3. 不同微服务不同数据库


远程调用:网络超时 远程服务器宕机


2
分布式事务解决方案

分布式事务是企业集成中的一个技术难点,也是每一个分布式系统架构中都会涉及到的一个东西,特别是在微服务架构中,几乎可以说是无法避免。

主流的解决方案如下:


1. TCC编程模式


2. 消息事务+最终一致性


1

TCC补偿式事务

是一种编程式分布式事务解决方案。

TCC 其实就是采用的补偿机制,其核心思想是:针对每个操作,都要注册一个与其对应的确认和补偿(撤销)操作。TCC模式要求从服务提供三个接口:Try、Confirm、Cancel。

1.Try:主要是对业务系统做检测及资源预留


2.Confirm:真正执行业务,不作任何业务检查;只使用Try阶段预留的业务资源;Confirm操作满足幂等性


3.Cancel:释放Try阶段预留的业务资源;Cancel操作满足幂等性


整个TCC业务分成两个阶段完成:

 

第一阶段:主业务服务分别调用所有从业务的try操作,并在活动管理器中登记所有从业务服务。当所有从业务服务的try操作都调用成功或者某个从业务服务的try操作失败,进入第二阶段。

第二阶段:活动管理器根据第一阶段的执行结果来执行confirm或cancel操作。如果第一阶段所有try操作都成功,则活动管理器调用所有从业务活动的confirm操作。否则调用所有从业务服务的cancel操作。

举个例子,假如 Bob 要向 Smith 转账100元,思路大概是:

我们有一个本地方法,里面依次调用

1. 首先在 Try 阶段,要先检查Bob的钱是否充足,并把这100元锁住,Smith账户也冻结起来


2. 在 Confirm 阶段,执行远程调用的转账的操作,转账成功进行解冻


3. 如果第2步执行成功,那么转账成功,如果第二步执行失败,则调用远程冻结接口对应的解冻方法 (Cancel)

缺点:

1. Canfirm和Cancel的幂等性很难保证


2. 这种方式缺点比较多,通常在复杂场景下是不推荐使用的,除非是非常简单的场景,非常容易提供回滚Cancel,而且依赖的服务也非常少的情况


3.这种实现方式会造成代码量庞大,耦合性高。而且非常有局限性,因为有很多的业务是无法很简单的实现回滚的,如果串行的服务很多,回滚的成本实在太高

不少大公司里,其实都是自己研发 TCC 分布式事务框架的,专门在公司内部使用。国内开源出去的:ByteTCC,TCC-transaction,Himly。


2

消息事务+最终一致性

基于消息中间件的两阶段提交往往用在高并发场景下,将一个分布式事务拆成一个消息事务(A系统的本地操作+发消息)+B系统的本地操作,其中B系统的操作由消息驱动,只要消息事务成功,那么A操作一定成功,消息也一定发出来了,这时候B会收到消息去执行本地操作,如果本地操作失败,消息会重投,直到B操作成功,这样就变相地实现了A与B的分布式事务。

虽然上面的方案能够完成A和B的操作,但是AB并不是严格一致的,而是最终一致的,我们在这里牺牲了一致性,换来了性能的大幅度提升。当然,这种玩法也是有风险的,如果B一直执行不成功,那么一致性会被破坏,具体要不要玩,还是得看业务能够承担多少风险。适用于高并发最终一致。






扫码关注我们

获取更多技术架构知识干货哟~