深入理解TCC原理以及使用案例tcc-transaction

分布式理论

数据库事务的ACID特性

数据库事务特性包括原子性(Atomicity )、一致性( Consistency )、隔离性或独立性( Isolation)和持久性(Durabilily)。以下单为例,在单体的电商系统中,调用下单服务,整个服务操作在同一事务中完成。下单成功以后,扣减库存,生成订单操作会同时生效,数据都会写入库存数据库,订单数据库。流程如下:

在分布式系统中,如果只是将下单服务进行微服务化,即将下单服务拆分为调用订单服务API和商品服务API。微服务拆分后,订单服务API对应独立的订单数据库,商品服务API对应独立的商品数据库。如果,直接按照单体服务中的的流程,只是将原来的本地方法替换微服务API的话,其流程图如下:

在下单服务中,使用RPC或者HTTP请求调用商品服务API和订单服务API,类似client(客户端)调用server(服务器)方式。此时,如果出现如下两个问题:

  1. 网络异常,client调用server服务调用成功,但是因为网络异常,client显示调用失败。
  2. 服务器宕机,client调用server服务调用成功,但是server服务器宕机了,client显示调用失败。

流程图如下:

因为,如上两个原因,会造成商品与订单数据的数据状态不一致,情况如下:

  1. 订单服务因为网络异常或者服务器宕机,订单数据回滚在订单数据库中没有生成数据,而商品服务调用成功,商品数据库中的库存扣减成功。
  2. 订单服务调用成功,生成的订单数据写入了订单数据库,而商品服务因为网络异常或者服务器宕机而调用失败,商品数据库中的库存没有变化。

CAP理论以及数据库的两阶段提交(2PC)

如何解决直接拆分为微服务后,数据状态不一致的问题呢?这时,就需要引入分布式事务了。首先,会想到通过数据库的两阶段提交(2PC)方式实现分布式事务,这个属于数据库层面的实现方案。根据CAP理论(详细可以参考深入理解CAP理论和适用场景),2PC属于CP模式

对数据库分布式事务有了解的同学一定知道数据库支持的2PC,又叫做 XA Transactions。其中,XA 是一个两阶段提交协议,该协议分为以下两个阶段:

  • 第一阶段:事务协调器要求每个涉及到事务的数据库预提交(precommit)此操作,并反映是否可以提交。
  • 第二阶段:事务协调器要求每个数据库提交数据。

其中,如果有任何一个数据库否决此次提交,那么所有数据库操作的数据都会回滚。此种方式会有严重的性能影响,并且也有如下几个缺点:

  1. 同步阻塞问题!执行过程中,所有参与节点都是事务阻塞型的。当参与者占有公共资源时,其他第三方节点访问公共资源也都处于阻塞状态。
  2. 单点故障!由于协调者的重要性,一旦协调者发生故障,参与者会一直阻塞下去。尤其在第二阶段,协调者发生故障,那么所有的参与者还都处于锁定事务资源的状态中,而无法继续完成事务操作。
  3. 数据不一致!在二阶段提交的过程中,当协调者向参与者发送commit请求之后,发生了网络异常或者服务器宕机,这会导致部分数据提交成功,部分提交失败,造成数据不一致。

TCC模式事务

由于2PC模式,可用性不高,并且性能很差,而且也属于一个长事务过程。TCC模式起始有点类似2PC,TCC它本质是一种补偿的思路,它把事务运行过程分成 Try,Confirm / Cancel 两个阶段,Try,Confirm ,Cancel三个步骤操作内容如下:

  • Try 阶段主要是对业务系统做检测及资源预留。
  • Confirm 阶段主要是对业务系统做确认提交,Try阶段执行成功后就执行Confirm阶段。
  • Cancel 阶段主要是在业务执行错误,在Try阶段执行失败后,用于执行Try 阶段执行的业务取消,预留资源释放。

并且,TCC模式,对比2PC模式,其事务控制的粒度更小,在Try,Confirm ,Cancel三个步骤中,服务相互调用时,服务之间事务是单独控制的。以tcc-transaction(具体的代码实例可以参考tcc-transaction-使用指南1.2.x)为例具体地介绍TCC模式。

tcc-transaction执行流程

在tcc-transaction中,在业务层面定义了Transaction事务的概念,在Try,Confirm / Cancel 两个阶段执行的时候,不同的服务API控制自己的事务。并且,基于数据库事务的REQUIRED传播属性(即如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务, 则按NESTED属性执行.它使用了一个单独的事务, 这个事务拥有多个可以回滚的保存点),两阶段执行时,都是使用的同一个事务。执行流程图如下:

tcc-transaction幂等处理

在Try,Confirm / Cancel 两个阶段执行逻辑时,可能会抛出一些内置的业务异常,例如,超时异常SocketTimeoutException,乐观锁异常OptimisticLockException,以及一些其他的自定义的业务异常。这时,只要这两个阶段重试几次,有可能就会成功。所以,在这两阶段的业务代码要满足幂等性处理。

发布了40 篇原创文章 · 获赞 2 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/new_com/article/details/105520543
TCC