swan
Swan 是美团的分布式事务事务,推行 SOA 的过程,会带来大量的服务拆分和数据库拆分,造成跨服务调用和跨库操作的需求日益增多。这些场景下单次业务操作通常需要更新多个数据库、调用上下游多个服务才得以完成,单库事务已不再能解决问题,需要引入分布式事务解决方案
TCC
一个 TCC 事务由三个方法组成,分别是 Try、Confirm 和 Cancel,三者分别定义了针对某种资源的预留(也叫冻结或者锁定)、提交和回滚逻辑,其中只有一阶段 Try 操作是可能需要加锁的,Confirm 与 Cancel 方法体应该尽可能简单,仅仅涉及资源的提交和回滚逻辑,不再做任何资源锁定、重复检查等逻辑。
TCC 三个方法描述:
- Try:资源预留/锁定操作,是事务发起方调用服务提供方的try方法来锁定业务需要的所有资源。这里通常会通过加锁,完成资源的隔离(一阶段成功之后每个 TCC 事务都已经拥有了自己独占的一份资源)
- Confirm:确认执行业务逻辑操作,这里使用的资源一定是 Try 阶段预留的业务资源。在 TCC 事务机制中认为,如果在try阶段能正常的预留资源,那么 Confirm 和 Cancel 阶段一定能完整正确的提交。Confirm 阶段也可以看成是对 Try 阶段的一个补充,Try + Confirm 一起组成了一个完整的业务逻辑。在一篇文章中看到过这么一个说法,我觉得挺对的:TCC机制将传统事务机制中的业务逻辑一分为二,拆分后保留的部分即为初步操作(Try);而分离出的部分即为确认操作(Confirm),被延迟到事务提交阶段执行。(不再需要加锁,直接提交资源修改)
- Cancel:取消执行业务逻辑。但这和普通的补偿性事务不一样的地方是,Cancel 阶段并没有真正的回滚业务(因为 Try 阶段并没有真正执行业务),而是释放之前锁定的资源。(不再需要加锁,直接解锁占用的资源)
以“扣钱”场景为例,在接入 TCC 前,对 A 账户的扣钱,只需一条更新账户余额的 SQL 便能完成,这时候数据库表结构中可能仅有一个字段 amount 字段用来标识余额
但是在接入 TCC 之后,用户就需要考虑如何将原来一步就能完成的扣钱操作,拆成两阶段,实现成三个方法,并且保证一阶段 Try 成功的话 二阶段 Confirm 一定能成功。因此,需要在表中新增一个字段 frozen_amount 字段用来标识当前冻结的余额。
Try:可用余额 100,冻结 30,此时 amount = 70,frozen_amount = 30,同时记录流水表,对前端用户显示文案:当前余额 100,其中 30 处于冻结状态
Confirm:先检查流水表状态是否正常,如果正常,将 frozen_amount 扣减 30,此时 amount = 70,frozen_amount = 0,逻辑完成,对前端用户显示文案:支付成功!
Cancel:说明一阶段异常,先检查下流水表看是否正常完成一阶段预留逻辑,如果正常则将 frozen_amount 扣减 30,加回到 amount 字段中,此时 amount = 100,frozen_amount = 0,逻辑完成,对前端用户显示文案:取消支付成功!
Saga
Saga 模式是一种分布式异步事务,保证最终一致性事务,隶属于柔性事务的一种。
每个 Saga 由一系列 sub-transaction Ti (子事务、也叫分支事务)组成,每个 Ti 都有对应的补偿动作 Ci,补偿动作用于撤销 Ti 造成的结果。可以看到,和 TCC 相比,Saga 没有“资源预留”动作,它的 Ti 就是直接提交到库。Saga 事务的执行顺序有两种:
(正常情况下,一阶段流程)T1, T2, T3, …, Tn
(异常情况下,一阶段 + 补偿流程)T1, T2, …, Tj, Cj,…, C2, C1,其中 0 < j <= n
Saga 定义了两种恢复策略:
backward recovery,逆序回滚:即上面提到的第二种执行顺序,其中 j 是发生错误的 sub-transaction,这种做法的效果是撤销掉之前所有成功的 sub-transation,使得整个 Saga 的执行结果撤销。
forward recovery,向前重试:适用于必须要成功的场景,执行顺序是类似于这样的:T1, T2, …, Tj (失败), Tj (重试),…, Tn,其中 j 是发生错误的 sub-transaction,该情况下不需要 Ci。
.svg)
如果创建订单异常,一般情况下,我们期望能够继续重试业务逻辑,而不是直接丢弃订单向用户返回失败,则整个流程应该向前继续重试:
createOrder(异常) -> createOrder -> payOrder -> deliverProduct
总会出现一些特殊情况比如 Ci 的代码有 bug、服务长时间崩溃等,这个时候就需要人工介入了,Swan 会有最大重试次数这个阈值,超出次数会熔断告警,用户可以在管理平台人工处理。
TXC
全称叫 (Taobao Transaction Constructor),是阿里中间件团队 2014 年在集团内部推出的分布式事务解决方案,与 TCC 不同的是,TXC 通过框架代理底层数据源、拦截具体的 SQL 语句、存储数据操作前后快照、引入全局锁保证事物间写隔离、框架自动生成反向补偿 SQL 语句等方法,真正做到对上层业务屏蔽底层分布式事务细节,使得用户可以像使用本地事务一样去使用分布式事务,最大程度降低了分布式事务的使用成本
服务发起方向 TC 集群注册开启全局事务,TC 集群返回给事务发起方一个全局事务 XID
发起方调用业务请求,流量流经的 GroupDataSource 和 ShardDataSource 均作为该全局事务的参与方
每当流量经过一个数据源时,都会在本地存储前后镜像数据,并向 TC 集群注册分支和全局事务锁
TC 集群侧维护了该全局事务下的所有分支事务记录
发起方会视业务请求是否抛异常向 TC 集群要求提交或者回滚全局事务
TC 集群回调每个数据源完成二阶段补偿
可以看出,多数据源跨库事务其实是将单数据源跨库事务包装成了一个个参与方,并通过 TC 集群完成二阶段补偿,其实内部是复用的单数据源跨库事务的功能,在此基础上新增了同 TC 集群交互的通信模块
.svg)