5.5.5 长事务解决方案:Saga
Saga 是一种长事务解决方案,也是一种分布式事务协议。
Saga 核心思想是将长事务拆分为多个本地短事务,由 Saga 事务协调器协调,如果正常结束那就正常完成,如果某个步骤失败,则根据相反顺序一次调用补偿操作。Saga 把一个业务流程,分解为多个步骤,每个步骤对应一个本地事务,每个步骤都有一个补偿操作,Saga 首先按流程顺序,执行流程中的每一个步骤(本地事务),如果出现异常,就会按流程顺序反向,执行补偿操作,用于回滚或撤销之前的操作。
Saga 这种特性非常适用于流程长、流程多且需要保证事务最终一致性的业务操作。
在 Saga 模式下,分布式事务内有多个参与者,每个参与者都是正向补偿服务。上图中的 T1~Tn 就是「正向调用」,C1~Cn 是「补偿调用」,正向调用和补偿调用是一一对应的关系。假设有 n 个被调用方服务,T1 就是对服务一的调用,接着 T2 是对服务方二的调用,T3 是对服务方三的调用。如果这个时候返回了失败,那么就需要进行回滚,此时就会调用 T2 的对应补偿 C2,调用 T1 的对应补偿 C1,使得分布式事务回到初始状态。
小结
相较于 TCC 对于业务改造较大,如果遗漏系统或者流程较复杂的系统,可以尝试使用 Saga 模式进行改造,另外 Saga 模式一阶段就会提交本地事务,无锁、长流程情况下可以保证性能。
总结 Saga 模式的优势:
- 一阶段提交本地数据库事务,无锁,高性能。
- 参与者可以采用事务驱动异步执行,高吞吐。
- 补偿服务即正向服务的“反向”,易于理解,易于实现。
5.5.6 使用 Seata 框架处理分布式事务
Seata(Simple Extensible Autonomous Transaction Architecture,简单可扩展自治事务框架),是蚂蚁金服和阿里巴巴共同开源的分布式事务解决方案。
Seata 提供了一种简单、可扩展和自主的事务解决方案,可以支持微服务架构中的分布式事务处理。
Seata 在阿里巴巴经历了多年”双 11“洗礼,通过多年的沉淀和积累, Seata 如今已成为分布式事务首选框架,它提供了多种事务模型支持,如 TCC、SAGA 和 XA 以及本文要介绍的 AT 模式。
1. Seata 角色
在 Seata 的架构设计中存在三种角色,即事务管理者、资源管理者以及事务协调者。
- 事务管理者(TM):负责驱动事务协调者(TC)执行全局事务的开启、提交和回滚操作,其通常由发起全局事务的业务充当。
- 资源管理者(RM):按照事务协调者(TC)的指令,执行分支事务的分支的提交、回滚操作,并向事务协调者汇报当前分支事务的状态,其一般由全局事务中涉及的所有分支业务充当。
- 事务协调者(TC):维护全局事务的状态,执行全局事务的提交和回滚操作,启动资源管理者(RM)执行分支事务的提交和回滚操作,由 Seata-Server 充当事务的协调者。
2. AT 模式
AT 模式是基于二阶段提交协议演变而来,它与二阶段提交协议一样分为两个阶段来实现多个分支事务之间的原子提交。与二阶段提交协议不同的事, AT 模式缩小了分支事务资源锁定的范围。
第一阶段
AT 模式在第一阶段任务执行完成后,会释放分支事务资源,不需要像二阶段提交协议一样,要等到整个协议完成协商之后才能释放资源,具体的执行逻辑如下。
- 第一阶段,TM 向 TC 注册全局事务,TC 会为本地全局事务生成唯一的 XID 并返回给 TM,后续 TM 与 RM 交互的请求都会携带 XID。
- 当 RM 收到 TM 执行的指令后,需要先获得本地锁才能执行分支事务并记录 Undo 和 Redo 日志。RM 根据 Undo 日志锁涉及的数据向 TC 申请该数据的全局锁,在获得全局锁之后, RM 会提交本地事务并释放本地锁。
- 最后 RM 向 TC 和 TM 上报分支事务的执行状态,完成第一阶段工作。
总体流程如下图所示:
第二阶段
第二阶段,在 TM 调用 RM 的过程中,如果存在一个或多个 RM 处理发生异常的情况,则 TM 向 TC 发送 Rollback 指令, TC 将向所有的 RM 发送 Rollback 指令,在等待所有的 RM 反馈之后,删除全局事务,释放全局锁。
在 RM 收到 Rollback 指令后, RM 依然拥有事务数据的全局锁,但没有获得本地锁,因此 RM 需要重新获取本地锁, 然后根据第一阶段记录的 Undo 日志对数据进行回溯,即将数据恢复至全局事务开启之前的状态, 提交重做事务后,释放本地锁。
最后 RM 向 TC 上报分支事务状态, TC 释放全局锁,完成本次全局事务的回滚。流程图如下:
3. AT 模式与二阶段提交协议的区别
AT 模式与两阶段提交最大的区别在于,AT 模式缩小了分支事务资源的锁定范围。虽然 AT 模式提高了系统的工作效率,但其复杂度也相应提高了,由于 AT 模式在第一阶段就提交了分支事务,因为第二阶段也需要进行相应的变化,具体总结如下:
- 第一阶段提交分支事务,释放本地锁和事务资源,以提高系统的工作效率。
- 增加了 Undo 日志,用于第二阶段的回滚
- 第二阶段提交异步化,进一步提高了正常工作的协商效率
- 第二阶段回滚操作需要根据 Undo 日志,对第一阶段已提交的事务进行反向补偿操作。
Seata AT Demo 示例
Seata 的官方示例中包含了多种分布式事务模型的演示代码,AT 模式的 Demo 例子给出了一个典型的分布式事务场景。
Demo 中的采购交易系统一个典型的微服务架构,其中有库存、账户、订单三个子服务,子服务之间相互独立,并遵循存储隔离(Data Storage Segregation, DSS) 原则。
在这个系统中,一个采购交易的流程:
- 扣减商品库存
- 扣减用户账号余额
- 生成采购订单
很明显,以上 3 个步骤必须要么全部成功,要么全部失败,否则系统的数据就会发生错乱。使用 AT 模式解决这个问题的思路其实很简单,一句话概括就是:在分布式事务过程中,记录待修改的数据修改前和修改后的值到 undo_log 表,如果交易中出现异常,就通过表中的数据做回滚,返回事务发生之前的状态。
由于演示代码细节较多,感兴趣的读者可以从 Github 库中下载学习。