1.分布式事务—上-灵析社区

没晒干的咸鱼

一、简介

分布式一致性是分布式系统亟需解决的关键问题之一,根据过去一年的调查问卷,在微服务的实践中分布式事务是用户遇到的最大痛点。目前市面缺少经过洪荒流量验证的分布式事务组件,Seata 在阿里经济体内部经过了漫长的孵化,承载了双 11 洪荒流量,实践证明 Seata 是一款解决分布式数据一致性的的优秀组件。Seata 于 2019 年正式对外开源,开源后就受到了大家的热情追捧,一度蝉联 GitHub 活跃排名榜首。Seata 除了提供了独创的 AT 事务模式外,还扩展了 TCC、Saga 和 XA 事务模式,满足大家对于不同业务场景中的需求。相关详细信息可参考其官网 Seata官网

学习目标

  • 理解分布式事务在业务中的核心使用场景和常用解决方案
  • 理解 Seata AT 事务模式的核心原理
  • 掌握 Seata 作为分布式事务组件与 Spring Cloud 的整合
  • 如何扩展一个 RPC 框架
  • Seata 实战

二、为什么需要分布式事务?

分布式事务不是在新架构下产生的新问题,即使在单体应用中同样存在着分布式事务问题,典型的场景是单体应用执行方法中含有多个数据源。X/OPEN 对于这一问题,提出了含有三种角色的 DTP(Distributed Transaction Processing)模型并形成了 XA 规范来解决此问题。各厂商针对 XA 规范做了具体的实现,也就是大家常说的 XA 协议。在 Java 体系中基于 DTP 模型提出了 JTA 规范(参考 JSR 907), 定义了分布式事务中的事务管理器(TM)与资源管理器(RM)、应用程序(AP)等的 Java 接口。在Java EE 时代,应用服务器如 weblogic 充当了 TM 的角色,而传统关系数据库通过实现 XA 协议充当了 RM 的角色。

随着互联网的高速发展,庞大的用户群体和快速的需求变化已经成为了传统架构的痛点。在这种情况下,如何从系统架构的角度出发,构建出灵活、易扩展的系统来快速响应需求的变化,同时,随着用户量的增加,如何保证系统的稳定性、高可用性、可伸缩性等等,成为了系统架构面临的挑战。微服务基于此背景应运而生,微服务架构越来越来越成为一种架构趋势,其本质是分布式去中心化。但微服务架构绝不是银弹,它不一定是一种能支撑未来一二十年的架构,引入微服务架构时需要我们根据业务场景,系统复杂性和团队规模有步骤的进行。微服务架构的引入使分布式数据一致性问题更为突出,由原来的单体应用拆分出来几十甚至上百个微服务,如何保证服务间的一致性?当在一条较长的微服务调用链中,位于中间位置的微服务节点出现异常,如何保证整个服务的数据一致性?

分布式一致性的引入,一定不可避免带来性能问题,如何更高效的解决分布式一致性问题,一直是我们致力于解决此问题的关键出发点。在“一切都正常”的情况下,我们可以认为我们并不需要分布式事务。但系统很难满足这种理想状态,系统可能因为一个非法的参数校验无法将服务链路继续向下调用下去,系统可能出现令人反感的超时问题,我们不清楚被调用的服务是否真正的执行了,被调用服务可能正在部署,网络抖动亦或者节点宕机导致接口无法继续调用。这些问题普遍存在于我们的系统中,业务的本质体现在数据上,数据不一致的直接后果是可能产生资损,更严重的是如果不一致的数据不能被及时发现,业务再次基于此数据的进行相关逻辑操作,会进一步导致数据错上加错,最终很难溯源。

三、常见的分布式事务解决方案

从是否满足事务 ACID 特性上,我们可以将事务分为两大类:刚性事务和柔性事务。在常见解决方案中 XA 事务属于刚性事务解决方案,而其他的大多数解决方案如 TCC、Saga、消息最终一致性则属于柔性事务解决方案。以下将对几种常见的事务方案做简要的介绍:

消息最终一致性

消息最终一致性方案是在 Seata 问世之前,市面上应用最广泛的一种解决方案。它本身具有削峰填谷,可异步化的优点,更多的适应于可异步化的末端链路消息通知场景。但是它本身也存在着一些缺点:需要依赖可靠消息组件,消息的可靠性很重要,大多数的原生消息组件故障时很难降级;实时性比较差,要经过多次网络 IO 开销和持久化,遇到队列积压情形实时性不可控;无法保证隔离性,在已发送消息和消息消费之前,中间数据对外可见,无法满足事务 isolate 特性;只能向前重试不可向后回滚,消息消费无法成功时无法回滚消息生产侧的数据;无法保证多条消息间的数据一致性。

XA

XA 标准提出后的 20 多年间未能得到持续的演进,在学术界有协议优化和日志协同处理等相关的研究,在工业界使用XA落地方案的相对较少,主要集中在应用服务器的场景。XA方案要求相关的厂商提供其具体协议的实现,目前大部分关系数据库支持了 XA 协议,但是支持程度不尽相同,例如,MySQL 在 5.7 才对 xa_prepare 语义做了完整支持。XA 方案被人诟病的是其性能,其实更为严重的是对于连接资源的占用,导致在高并发未有足够的连接资源来响应请求成为系统的瓶颈。在微服务架构下 XA 事务方案随着微服务链路的扩展成为一种反伸缩模式,进一步加剧了资源的占用。另外 XA 事务方案要求事务链路中的 resource 全部实现 XA 协议方可使用,若其中某一资源不满足,那么就无法保证整个链路的数据一致性。

TCC

TCC 方案要求用户根据业务场景实现 try,confirm,cancel 三个接口,由框架根据事务所处的事务阶段和决议来自动调用用户实现的三个接口。从概念上 TCC 框架可以认为是一种万能框架,但是其难点是业务对于这三个接口的实现,开发成本相对较高,有较多业务难以做资源预留相关的逻辑处理,以及是否需要在预留资源的同时从业务层面来保证隔离性。因此,这种模式比较适应于金融场景中易于做资源预留的扣减模型。

Saga

有了 TCC 解决方案为什么还需要 Saga 事务解决方案?上文提到了 TCC 方案中对业务的改造成本较大,对于内部系统可以自上而下大刀阔斧的推进系统的改造,但对于第三方的接口的调用往往很难推动第三方进行 TCC 的改造,让对方为了你这一个用户去改造 TCC 方案而其他用户并不需要,需求上明显也是不合理的。要求第三方业务接口提供正反接口比如扣款和退款,在异常场景下必要的数据冲正是合理的。另外,Saga 方案更加适应于工作流式的长事务方案并且可异步化。

上面提到了4种常用的分布式事务解决方案,Seata 集成了TCC、Saga 和 XA 方案。另外,Seata 还提供了独创的 AT 强一致分布式事务解决方案。下文将对 AT 方案进行简要的介绍。

三、AT 事务模式

一个分布式事务有全局唯一的 xid,由若干个分支事务构成,每个分支事务有全局唯一的 branchId。上图展示了在一个分支事务中 RM 与 TC 的交互过程。其中主要包含的交互动作如下:

  • branchRegister

分布式事务一阶段执行,分支事务在 commit 之前与 TC 交互获取 全局锁 和返回 branchId。全局锁为 Seata 应用锁等同于修改数据记录的行锁,若获取锁失败将会进行锁重试,此处提供了两种重试策略是否持有数据库连接重试全局锁,默认为释放数据库连接。若成功,则抢占全局锁并返回 branchId,若重试到最大次数失败,则发起全局事务的回滚,对已完成的分支事务执行回滚。

  • branchReport

分布式事务一阶段执行,本地事务 commit 之后与 TC 交互,上报本地事务已完成标识。目前 branchReport 动作已经在 1.0 版本做了相关的优化,本地事务 commit 不上报,本地事务rollback 上报。经过优化分布式事务的整体性能在 globalCommit 场景下最低提升25%,最高提升50%。本地事务 rollback 上报可以帮助 TC 快速决策需要回滚的分支事务。

  • branchCommit

分布式事务二阶段执行,在形成 globalCommit 决议后执行。AT 模式中此步骤异步执行来提升其性能,可以认为分布式事务 globalCommit 决议提交到TC 释放完全局锁就已经完成了整个分布式事务的处理。branchCommit 在 AT 模式主要用于删除一阶段的 undo_log,TC 下发到 RM 后并不是立即执行,而是通过定时任务 + SQL 批量合并的方式来提升其处理性能。

  • branchRollback

分布式事务二阶段执行,在形成 globalRollback 决议后执行。RM 收到 branchRollback 请求,取 undo_log 表中对应的branchId 记录解析 rollback_info 字段,对现有数据和 undo log 后镜像对比,对比不同则分支事务回滚失败。对比成功则根据前镜像构造 SQL 并执行反向操作和删除 undo log。

阅读量:2025

点赞量:0

收藏量:0