分布式事务中Tcc模式常见问题(幂等、空回滚、悬挂)解决

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

TCC的异常场景
在分布式系统中随时随地都需要面对网络超时网络重发和服务器宕机等问题。所以分布式事务框架作为搭载在分布式系统之上的一个框架型应用也绕不开这些问题。具体而言有以下常见问题

幂等处理
空回滚
资源悬挂
这些异常的应对需要TCC框架的支持和解决方案。

幂等处理
产生原因
因为网络抖动等原因分布式事务框架可能会重复调用同一个分布式事务中的一个分支事务的二阶段接口。所以分支事务的二阶段接口Confirm/Cancel需要能够保证幂等性。如果二阶段接口不能保证幂等性则会产生严重的问题造成资源的重复使用或者重复释放进而导致业务故障。多次执行cancel confirm 重试操作

在这里插入图片描述
从上图中红色部分可以看到如果当TC调用参与者的二阶段方法时发生了异常(TC本身异常或者网络异常丢失结果)。此时TC无法感知到调用的结果。为了保证分布式事务能够走到终态此时TC会按照一定的规则重复调用参与者的二阶段方法。

应对策略
对于幂等类型的问题通常的手段是引入幂等字段进行防重放攻击。对于分布式事务框架中的幂等问题同样可以祭出这一利器。我们可以通过增加一张事务状态控制表来实现这个表的关键字段有以下几个

主事务ID
分支事务ID
分支事务状态
其中1和2构成表的联合主键来唯一标识一笔分布式事务中的一条分支事务。3用来标识该分支事务的状态一共有3种状态

INIT(I) - 初始化
CONFIRMED© - 已提交
ROLLBACKED® - 已回滚
幂等记录的插入时机是参与者的Try方法此时的分支事务状态会被初始化为INIT。然后当二阶段的Confirm/Cancel执行时会将其状态置为CONFIRMED/ROLLBACKED。

当TC重复调用二阶段接口时参与者会先获取事务状态控制表的对应记录查看其事务状态。如果状态已经为CONFIRMED/ROLLBACKED那么表示参与者已经处理完其分内之事不需要再次执行可以直接返回幂等成功的结果给TC帮助其推进分布式事务。增加了幂等记录的写入和读取判断后时序图如下(蓝色部分)

在这里插入图片描述

空回滚
产生原因
先来说定义当没有调用参与方Try方法的情况下就调用了二阶段的Cancel方法Cancel方法需要有办法识别出此时Try有没有执行。如果Try还没执行表示这个Cancel操作是无效的即本次Cancel属于空回滚如果Try已经执行那么执行的是正常的回滚逻辑。

在这里插入图片描述
如上图所示红色部分的一阶段Try可能失败。

首先发起方在调用参与者之前会向TC申请开始一笔分布式事务。然后发起方调用参与者的一阶段方法在调用实际发生之前一般会有切面拦截器感知到此次Try调用然后写入一条分支事务记录。紧接着在实际调用参与者的Try方法时发生了异常。异常原因可以是发起方宕机网络抖动等。

总而言之就是Try方法没有执行成功然而此时这笔分布式事务和分支事务已经落库。有两种情况会触发分布式事务的回滚

发起方认为当前分布式事务无法成功主动通知TC回滚
TC发现分布式事务超时被动触发回滚
触发回滚操作后TC会对该分布式事务关联的分支事务调用其二阶段Cancel。在执行Cancel时Try还未执行成功触发空回滚。如果不对空回滚加以防范的话可能会造成资源的无效释放。即在没有预留资源的情况下就释放资源造成故障。

应对策略
可以发现要应对空回滚的问题就需要让参与者在二阶段的Cancel方法中有办法识别到一阶段的Try是否已经执行。

很显然可以继续利用事务状态控制表来实现这个功能。

前面提到过为了保证幂等性当Try方法被成功执行后会插入一条记录标识该分支事务处于INIT状态。所以后续当二阶段的Cancel方法被调用时可以通过查询控制表的对应记录进行判断。如果记录存在且状态为INIT就表示一阶段已成功执行可以正常执行回滚操作释放预留的资源如果记录不存在则表示一阶段未执行本次为空回滚不释放任何资源。

时序图如下所示

在这里插入图片描述

资源悬挂try - cancel --try 循环执行cancel在try之前
产生原因
悬挂顾名思义是有一些资源被悬挂起来后续无法处理了。那么什么场景下才会出现这种现象呢

上一节中提到过空回滚指的是当一阶段Try未执行成功而二阶段Cancel就因TC回滚整个分布式事务而被调用。

但是考虑一种极端情况当分布式事务到终态后参与者的一阶段Try才被执行此时参与者会根据业务需求预留相关资源。预留资源只有当前事务才能使用然而此时分布式事务已经走到终态后续再没有任何手段能够处理这些预留资源。至此就形成了资源悬挂。

这种一阶段比二阶段执行的还晚的情况看似不可能但是仔细考虑RPC调用的时序其实这种情况在复杂多变的网络中是完全可能的下面的时序展示了这种可能性

发起方通过RPC调用参与者一阶段Try但是发生网络阻塞导致RPC超时
RPC超时后TC会回滚分布式事务(可能是发起方主动通知TC回滚或者是TC发现事务超时后回滚)调用已注册的各个参与方的二阶段Cancel
参与方空回滚后发起方对参与者的一阶段Try才开始执行进行资源预留从而形成悬挂
使用时序图来描述红色部分为产生资源悬挂的关键步骤

在这里插入图片描述

应对策略
资源悬挂的本质原因在于一阶段和二阶段的执行顺序没有被严格地保证。所以相应的解决方案还是通过读取事务状态控制表的事务状态。

前面在幂等方案的讨论中说过

幂等记录的插入时机是参与者的Try方法此时的分支事务状态会被初始化为INIT。然后当二阶段的Confirm/Cancel执行时会将其状态置为CONFIRMED/ROLLBACKED。

由于悬挂的产生背景是一阶段方法根本就未执行所以此时事务控制记录是不存在的需要在二阶段中处理ROLLBACK的情况(因为超时后触发回滚不可能存在二阶段为CONFIRM)。

处理方案为在判断为空回滚的场景下(体现在对应一阶段事务控制记录不存在)插入一条状态为ROLLBACKED的控制记录。

那么下次当一阶段Try抵达执行的时候首先会尝试插入状态为INIT的事务控制记录。如果插入失败表示当前分支事务的记录已经存在Try无需继续执行。有几种可能性会导致此情形

一阶段Try重复请求网络抖动情况可能发生可以理解为命中幂等
二阶段插入了防悬挂记录一阶段不可继续执行
时序图描述如下蓝色部分为防止资源悬挂增加的检查项

在这里插入图片描述

三种异常总结
前面讨论了分布式事务三种典型的异常类型它们的解决方案都依赖于一张事务状态控制表。我们来尝试总结一下它们各自的特点。

幂等
问题TC重复调用二阶段
解决事务状态控制记录作为控制手段只有存在INIT记录时才执行存在CONFIRMED/ROLLBACKED记录时不再执行

空回滚
问题TC回滚事务调用二阶段但一阶段尚未执行
解决事务状态控制记录作为控制手段无记录时即为空回滚

资源悬挂
问题TC回滚事务调用二阶段完成空回滚后一阶段执行成功
解决事务状态控制记录作为控制手段二阶段发现无记录时插入记录一阶段执行时检查记录是否存在

共通点
核心的解决方案就是事务状态控制表
幂等控制作为最基础的异常处理手段资源悬挂的前置条件是空回滚所以发生空回滚时会插入一条状态为ROLLBACKED的控制记录

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6