TCC是支付宝提出的分布式事务解决方案,是 try、confirm、cancel 的缩写。
与2PC二阶段提交机制类似,区别在于层面不同,2PC是在数据库层面解决数据库之间的分布式事务,TCC是在应用层面解决分布式系统中的分布式事务。
工作流程
每个分布式事务的参与者都需要实现3个接口:try、confirm、cancel(confirm 对应2PC的事务提交,cancel 对应2PC的事务回滚)。例如一个转账业务,服务A负责扣钱、服务B负责加钱,A、B都要实现这3个接口。
TCC 分为2个阶段:
-
准备阶段
调用方调用各个服务的 try 接口,各个服务执行资源检查和锁定,看自己是否有能力完成,如果允许,则锁定资源。
-
提交阶段
如果各个服务的 try 接口都返回了 yes,则进入提交阶段,调用方调用各个服务的 confirm 接口,各个服务对 try 阶段锁定的资源进行处理。
如果 try 阶段有一方返回了 no,或者超时,调用方调用各个服务的 cancel 接口,释放锁定的资源。
对于异常情况,处理方法是不断重试,不管 confirm 失败了,还是 cancel 失败了,都不断重试。
流程示例
以转账为例,有2个服务,扣钱服务、加钱服务,都各自实现 try、confirm、cancel 接口。
现在账号 A 要给账号 B 转 30 元。
-
阶段1:准备阶段
调用者调用2个服务的 try 接口。
扣钱服务的try接口中对 A 的余额进行验证,如果可用余额不少于30元,则冻结30元,返回 yes,否则返回 no。
加钱服务的try接口对 B 账号进行验证,例如是否合法可用,是则返回 yes,否则返回 no。
这个阶段保证了扣钱可扣、加钱可加。
-
阶段2:提交阶段
try 接口如果都返回了 yes,调用者调用2个服务的 confirm 接口。
扣钱服务实际扣减A的30元,加钱服务实际给B加30元。
try 接口如果有返回 no 或者超时的,调用者调用2个服务的 cancel 接口。
扣钱服务解冻A的30元,加钱服务什么也不用做。
异常控制
下面几个是典型的异常情况:
-
空回滚
现象是 try 没被执行,就调用了 cancel。
例如在某些异常情况下,调用 try 时出现异常,try 接口实际没有被调用,自然没有返回 yes,那么会按照正常流程进入第2阶段,调用 cancel 接口,这就形成了空回滚。
解决方法:让 cancel 能够识别出这是一个空回滚,可以记录事务执行状态,cancel 中判断 try 是否执行了。
-
幂等
提交阶段异常时可能会重复调用 confirm 和 cancel,所以要实现幂等,保证多次执行效果一致。
解决方法:记录事务执行状态,如果执行过了,就不再执行。
-
悬挂
现象是先执行了 cancel,后执行的 try,造成资源没人释放。
例如调用 try 时网络拥堵超时,被认为失败,然后调用 cancel,这时事务相当于结束了,但后来网络好点之后 try 开始执行了,锁定了相关资源,因为事务已经结束,confirm、cancel 都不会再调用了,就造成资源悬挂,无法释放。
解决方法:还是记录事务执行状态,try 执行时判断 cancel 是否执行了。
小结
TCC是应用层面的分布式事务解决方案,类似2PC,也是2阶段提交,分为准备阶段、提交阶段。
实现时需要注意空回滚、幂等、悬挂问题,可以通过记录事务状态来解决。
优点:
-
可靠性高
-
实时性高
缺点:
-
开发复杂度高
-
因为事务状态管理,需要多次DB操作,性能有一定损耗
TCC框架:
-
https://github.com/changmingxie/tcc-transaction
-
https://github.com/seata/seata