-
分布式事务解决方案 -- TCC 笔记
TCC 一种成熟的分布式事务解决方案,可用于解决跨库操作的数据一致性问题。
TCC 是 Try - Confirm - Cancel 缩写,TCC 事务与传统的数据库事务不同,它存在于业务层面,由系统业务逻辑(事务管理器),进行事务控制。
TCC 将原业务服务,拆分为了三个操作。可将这三个操作,对应想象成三个方法,每个方法里有不同的业务代码。
- Try:检查预留资源
- Confirm:业务执行
- Cancel:回撤 Try 里执行的操作
Try#
在 TCC 中,这是一个初步操作,从业务逻辑上来说,这只是检查资源(数据是否正确或业务上是否可执行等)的操作,真正执行实际业务更改是在 Confirm 中。也就是说,Try 操作过后,与后续执行的 Confirm 操作一起(Try - Confirm),才真正构成一个完整的业务逻辑。但如果 Try 操作出现异常,则会执行 Cancel 操作。(Try - Cancel)。
TCC服务需要保证Try操作成功之后,Confirm操作一定能成功。
Confirm#
是对 Try 操作的一个补充。当 TCC事务管理器 决定 commit 全局事务时,就会逐个执行 Try 操作指定的 Confirm 操作,将 Try 未完成的事项最终完成。
Cancel#
是对 Try 操作的一个回撤。当 TCC 事务管理器决定 rollback 全局事务时,就会逐个执行 Try 操作指定的 Cancel 操作,将 Try 操作已完成的事项全部撤回。
执行流程图#
异常处理#
TCC 需要注意三种异常处理分别是幂等、空回滚、资源悬挂
幂等#
问题描述
网络数据包重复传输,或者异常事务的补偿执行,会导致 TCC 服务的 Try、Confirm或者Cancel操作被重复执行。
解决思路
创建事务控制表,记录事务当前执行的状态,每次执行前都查询该状态。
空回滚#
问题描述
空回滚指的是 Try 未执行,Cancel 执行。
结合 TCC执行流程图 来说:
- 当业务应用运行至 2 调用 Try 操作 中,服务A Try 操作里的代码发起请求,等待 服务B Try 操作结果
- 但 服务B 此时因网络原因,无法接收 Try 操作被请求调用
- 此时,业务系统中的事务协调器发现 服务A 的 Try 操作迟迟未响应,认为操作失败;则会去调用 服务A 的 Cancel 操作,去回撤 Try 操作
- 同时也会去调用 服务B 的 Cancel 操作,去回撤 服务B 的 Try 操作。如果此时 服务B 网络正常,则会去执行 Cancel 操作。于是发生了 Try 未执行,Cancel 执行。
解决思路
我们是允许空回滚的产生的,所以我们需要在 Cancel 操作中,识别出 Try 操作是否执行。而如何识别呢?
在事务控制表中,每个服务的 Try 操作是否执行,都应有对应记录。所以可在 Cancel 操作中去读取对应服务的 Try 执行记录,来判断是否执行过 Try 操作,从而判断出本次是否为空回滚,不释放任何资源。
资源悬挂#
问题描述
Cancel 比 Try 先执行。
接着空回滚流程来说:
- 当 服务B 执行完 Cancel 操作之后,先前 服务A 发起未抵达 服务B 的请求,被服务B接收
- 服务B 执行 Try 操作。于是发生了 Cancel 比 Try 先执行。
但事务协调器已经调用了 服务B 的 Cancel 操作,并且 服务B 也成功执行了 Cancel 操作。此时 服务B 中 Try 操作改变的资源将得不到 Confirm 的确认执行,或者 Cancel 回撤执行。这部分资源称为资源悬挂。
解决思路
在发生空回滚的场景下,事务控制表 记录已发生的 Cancel 控制记录,在调用 Try 时,判断是否存在此记录。
业务数据可见性控制#
TCC服务的一阶段Try操作会做资源的预留,在二阶段操作执行之前,如果其他事务需要读取被预留的资源数据,那么处于中间状态的业务数据该如何向用户展示,需要业务在实现时考虑清楚;通常的设计原则是 “宁可不展示、少展示,也不多展示、错展示”。
业务数据并发访问控制#
TCC服务的一阶段Try操作预留资源之后,在二阶段操作执行之前,预留的资源都不会被释放;如果此时其他分布式事务修改这些业务资源,会出现分布式事务的并发问题;
在实现TCC服务时,需要考虑业务数据的并发控制,尽量将逻辑锁粒度降到最低,以最大限度的提高分布式事务的并发性。
出处:https://www.cnblogs.com/jintangc/p/15135962.html