分布式架构设计基础知识|如何选择分布式事务形态(TCC,SAGA,2PC,基于消息最终一致性等等)
各种形态的分布式事务 分布式事务有多种主流形态,包括:
- 基于消息实现的分布式事务
- 基于补偿实现的分布式事务
- 基于TCC实现的分布式事务
- 基于SAGA实现的分布式事务
- 基于2PC实现的分布式事务
何时选择单机事务? 这个相信大家都很清楚,在条件允许的情况下,我们应该尽可能地使用单机事务,因为单机事务里,无需额外协调其他数据源,减少了网络交互时间消耗以及协调时所需的存储IO消耗,在修改等量业务数据的情况下,单机事务将会有更高的性能。
但单机数据库由于 业务逻辑解耦等因素进行了数据库垂直拆分、或者由于单机数据库性能压力等因素进行了数据库水平拆分之后,数据分布于多个数据库,这时若需要对多个数据库的数据进行协调变更,则需要引入分布式事务。
分布式事务的模式有很多种,那究竟要怎么选择适合业务的模式呢?以下我们将从使用场景、性能、开发成本这几个方面进行分析。
何时选择基于消息实现的事务? 基于消息实现的事务适用于分布式事务的提交或回滚只取决于事务发起方的业务需求,其他数据源的数据变更跟随发起方进行的业务场景。
举个例子,假设存在业务规则:某笔订单成功后,为用户加一定的积分。
在这条规则里,管理订单数据源的服务为事务发起方,管理积分数据源的服务为事务跟随者。
从这个过程可以看到,基于消息队列实现的事务存在以下操作:
- 订单服务创建订单,提交本地事务
- 订单服务发布一条消息
- 积分服务收到消息后加积分
- 编写订单服务里订单创建的逻辑
- 编写积分服务里增加积分的逻辑
何时选择利用补偿实现的事务? 但是基于消息实现的事务并不能解决所有的业务场景,例如以下场景:某笔订单完成时,同时扣掉用户的现金。
这里事务发起方是管理订单库的服务,但对整个事务是否提交并不能只由订单服务决定,因为还要确保用户有足够的钱,才能完成这笔交易,而这个信息在管理现金的服务里。这里我们可以引入基于补偿实现的事务,其流程如下:
- 创建订单数据,但暂不提交本地事务
- 订单服务发送远程调用到现金服务,以扣除对应的金额
- 上述步骤成功后提交订单库的事务
以上流程比基于消息队列实现的事务的流程要复杂,同时开发的工作量也更多:
- 编写订单服务里创建订单的逻辑
- 编写现金服务里扣钱的逻辑
- 编写现金服务里补偿返还的逻辑
(题外话:阿里GTS也是利用补偿实现,只不过补偿代码自动生成,无需业务干预,同时接管应用数据源,禁止业务修改处于全局事务状态中的记录。)
何时选择利用TCC实现的事务 然而基于补偿的事务形态也并非能实现所有的需求,如以下场景:某笔订单完成时,同时扣掉用户的现金,但交易未完成,也未被取消时,不能让客户看到钱变少了。
这时我们可以引入TCC,其流程如下:
- 订单服务创建订单
- 订单服务发送远程调用到现金服务,冻结客户的现金
- 提交订单服务数据
- 订单服务发送远程调用到现金服务,扣除客户冻结的现金
以上流程比基于补偿实现的事务的流程要复杂,同时开发的工作量也更多:
- 订单服务编写创建订单的逻辑
- 现金服务编写冻结现金的逻辑
- 现金服务编写扣除现金的逻辑
- 现金服务编写解冻现金的逻辑
何时选择利用SAGA实现的事务? SAGA可以看做一个异步的、利用队列实现的补偿事务。
其适用于无需马上返回业务发起方最终状态的场景,例如:你的请求已提交,请稍后查询或留意通知 之类。
将上述补偿事务的场景用SAGA改写,其流程如下:
- 订单服务创建最终状态未知的订单记录,并提交事务
- 现金服务扣除所需的金额,并提交事务
- 订单服务更新订单状态为成功,并提交事务
其业务编码工作量比补偿事务多一点,包括以下内容:
- 订单服务创建初始订单的逻辑
- 订单服务确认订单成功的逻辑
- 订单服务确认订单失败的逻辑
- 现金服务扣除现金的逻辑
- 现金服务补偿返回现金的逻辑
因此该形式适用于不需要同步返回发起方执行最终结果、可以进行补偿、对性能要求较高、不介意额外编码的业务场景。
但当然SAGA也可以进行稍微改造,变成与TCC类似、可以进行资源预留的形态。
2PC事务 其适用于参与者较少,单个本地事务执行时间较少,并且参与者自身可用性很高的场景,否则,其很可能导致性能下降严重。
并非一种事务形态就能打遍天下 通过分析我们可以发现,并不存在一种事务形态能解决所有的问题,我们需要根据特定的业务场景选择合适的事务形态。甚至于有时需要混合多种事务形态才能更好的完成目标,如 上面提到的 订单、积分、钱包混合的场景:订单的成功与否需要依赖于钱包的余额,但不依赖于积分的多少,因此可以混合基于消息的事务形态以加积分 及 基于补偿的事务形态以确保扣钱成功,从而得到一个性能更好,编码量更少的形态。
然而目前很多框架都专注于某单一方面的事务形态,如TCC单独一个框架,可靠消息单独一个框架,SAGA单独一个框架,他们各自独立,容易导致以下问题:
- 由于前期只采用了其中一种类型事务的框架,因为工具目前只有锤子,引入其他工具又涉及测试、阅读代码等过程,因此把所有问题都看做钉子,导致性能偏低或者实现不够优雅
- 由于不同框架管理事务的形态可能不一致,导致不能很好的协调工作,如某一个TCC框架和另一个基于消息的事务框架无法很好融合。
- 一个框架包含多种事务形态,一个框架搞定所有类型的事务
- 多种事务形态可混合使用
- 高性能,若不启用框架的幂等功能,对业务数据库的额外消耗仅为写入25字节的一行
- 可选的框架幂等实现(包括调用次序错乱处理),大幅减轻业务开发工作量
- 业务代码可实现完全无入侵
- 支持嵌套事务
- 无需额外部署协调者,不同APP的服务协调自身发起的事务
- 分布式事务ID可关联业务ID,业务类型,APPID,便于监控各个业务的分布式事务执行情况
总结 不同业务场景应按需引入不同的事务形态,在条件允许的情况下,个人建议按照如下次序选择对应的事务形态:
单机事务》基于消息的事务》基于补偿的事务》TCC事务
【分布式架构设计基础知识|如何选择分布式事务形态(TCC,SAGA,2PC,基于消息最终一致性等等)】因SAGA事务的形态需要配合较为明显的前端业务交互变更,个人建议在单一事务执行过程较长、存在较多子事务,并且无法使用基于消息的事务形态时使用。
推荐阅读
- PMSJ寻平面设计师之现代(Hyundai)
- 基于微信小程序带后端ssm接口小区物业管理平台设计
- 爱琐搭配(喜欢复古、冷淡,像这种双环设计的气质耳环)
- 别墅庭院设计,不同的别墅庭院设计也给人视觉上完全不一样的!
- 数据库设计与优化
- 深入浅出谈一下有关分布式消息技术(Kafka)
- 设计模式-代理模式-Proxy
- 程序员|【高级Java架构师系统学习】毕业一年萌新的Java大厂面经,最新整理
- [译文]Domain|[译文]Domain Driven Design Reference(四)—— 柔性设计
- 《精进》读书笔记(四十八)