上周,分布式事务 Fescar 宣布进行品牌升级:
Thanks, Fescar ❤️,
Hello, Seata ?。
Seata 意为:Simple Extensible Autonomous Transaction Architecture,是一套一站式分布式事务解决方案。
项目地址:github.com/seata/seata
蚂蚁金服在 Seata 0.4.0 版本加入了 TCC 模式,后续也会持续输入。
为了帮助大家理解,分布式事务开源负责人绍辉进行了一次线下分享,详细讲述了分布式事务在蚂蚁金服的发展,希望可以帮助大家理解分布式事务,以下为分享的文字整理版本。
前言
今天的分享将从以下三个部分展开:分布式事务问题产生的背景、蚂蚁金服分布式事务以及分布式事务 Seata 的 Roadmap。
分享嘉宾:绍辉 蚂蚁金服 分布式事务开源负责人
1、分布式事务问题产生的背景
1.1、数据库的水平拆分
蚂蚁金服早期,业务量比较小,单库单表便能满足业务需求;但是随着业务的发展,单库单表数据库逐渐成为瓶颈。为了解决数据库的瓶颈问题,我们对数据库进行了水平拆分。拆分所带来的一个问题就是以前一个数据库上便能完成的写操作现在要跨多个数据库,由此带来了跨库事务问题。
1.2、业务的服务化拆分
蚂蚁金服早期是单系统架构,所有业务服务几乎都在少数几个 APP 中。随着业务的发展,业务越来越复杂,服务之间的耦合度也越来越高,故我们对系统进行了重构,服务按照功能进行解耦和垂直拆分。拆分之后所带来的问题就是一个业务活动原来只需要调用一个服务就能完成,现在需要调用多个服务才能完成,由此产生了跨服务事务问题。
1.3、转账案例说明数据一致性问题
数据库的水分拆分以及服务的垂直拆分,所带来的问题是一个业务活动通常要调用多个服务、访问多个数据库才能完成。
以金融业务场景下的转账场景为例,转账服务要完成以下操作:
- 调用交易系统服务创建交易订单;
- 调用支付系统记录支付明细;
- 调用账务系统执行 A 扣钱;
- 调用账务系统执行 B 加钱。
以上 4 个操作要跨 3 个系统,访问 4 个数据库。而网络、数据库、机器等都具有不可靠性,我们很难保证以上 4 个操作能 100% 全部成功。
在金融属性的业务中,不允许 A 账户的钱扣了,而 B 账户的钱没有加上的现象出现,所以我们必须想办法保证 1 ~ 4 这四个操作要么全部成功,要么全部失败;所以蚂蚁金服自主研发了分布式事务中间件,解决跨服务、跨数据库的数据一致性问题。
2、蚂蚁金服分布式事务
2.1、分布式事务理论基础
在介绍蚂蚁金服的分布式事务中间件之前,先介绍一些分布式事务的理论背景。
- 2PC
两阶段提交协议(Two Phase Commitment Protocol)是分布式事务最基本的协议。在两阶段提交协议中,有一个事务管理器和多个资源管理器,事务管理器分两阶段协调资源管理器。在第一阶段,事务管理器询问所有资源管理器准备是否成功。如果所有资源均准备成功,那么在第二阶段事务管理器会要求所有资源管理器执行提交操作;如果任一资源管理器在第一阶段返回准备失败,那么事务管理器会要求所有资源管理器在第二阶段执行回滚操作。通过事务管理器的两阶段协调,最终所有资源管理器要么全部提交,要么全部回滚,最终状态都是一致的。
- TCC
资源管理器有很多实现方式,其中 TCC(Try-Confirm-Cancel)是资源管理器的一种服务化的实现。TCC 是一种比较成熟的分布式事务解决方案,可用于解决跨数据库、跨服务业务操作的数据一致性问题。TCC 其 Try、Confirm、Cancel 3 个方法均由业务编码实现,故 TCC 可以被称为是服务化的资源管理器。
TCC 的 Try 操作作为一阶段,负责资源的检查和预留;Confirm 操作作为二阶段提交操作,执行真正的业务;Cancel 是二阶段回滚操作,执行预留资源的取消,使资源回到初始状态。
如下图所示,用户实现 TCC 服务之后,该 TCC 服务将作为分布式事务的其中一个资源,参与到整个分布式事务中。事务管理器分两个阶段协调 TCC 服务,在第一阶段调用所有 TCC 服务的 Try 方法,在第二阶段执行所有 TCC 服务的 Confirm 或者 Cancel 方法,最终所有 TCC 服务要么全部都是提交的、要么全部都是回滚的。
2.2、蚂蚁金服分布式产品介绍
蚂蚁金服从 2007 年开始做分布式事务,至今已经有 12 年历史。蚂蚁金服的分布式事务最初是采用 TCC 实现的,TCC 模式帮蚂蚁业务解决了各类金融核心场景下的数据一致性问题。
2007 年我们开始支持双十一,为了满足双十一的高性能需求,我们对分布式事务做了一系列的性能优化。
2013年,蚂蚁金服开始做单元化改造,分布式事务也开始支持 LDC、异地多活和高可用容灾,解决了机房故障情况下服务快速恢复的问题。
2014年,蚂蚁金服分布式事务中间件开始通过蚂蚁金融云对外输出,我们发展了一大批的外部用户;在发展外部客户的过程中,外部客户表示愿意牺牲一部分性能(无蚂蚁的业务规模)以换取接入便利性和无侵入性。
所以在 2015年,我们开始做无侵入的事务解决方案:FMT 模式和 XA 模式。
蚂蚁金服分布式事务中间件经过长期演进,目前积累了 TCC、FMT 和 XA 三种模式,具有丰富的应用场景。下面分别介绍这三种模式。
2.3、TCC 模式
蚂蚁金服的 TCC 模式和前面介绍 TCC 理论中提的 TCC 原理是一致的。不同的是,我们在整个分布式事务执行过程中,会去记录事务日志,一个分布式事务会产生一条主事务记录(对应发起方)和若干分支事务记录(对应 TCC 参与者)。记录事务日志的目的是,当分布式事务执行过程中出现异常中断时,事务恢复服务通过轮询事务日志,找出这个异常中断的事务,补偿执行该异常事务剩余未完成的动作,整个分布式事务的最终状态要么全部提交,要么全部回滚。
TCC 设计规范和注意事项:
用户在接入 TCC 时,大部分工作都集中在如何实现 TCC 服务上。经过蚂蚁金服多年的 TCC 应用实践,总结如下在 TCC 设计和实现过程中的注意事项:
1、业务操作分两阶段完成:
接入 TCC 前,业务操作只需要一步就能完成。但是在接入 TCC 之后,需要考虑如何将其分成两个阶段完成:把资源的检查和预留放在一阶段的 Try 操作中进行,把真正的业务操作的执行放在二阶段的 Confirm 操作中进行。
以下举例说明业务模式如何分成两阶段进行设计,举例场景:“账户 A 的余额中有 100 元,需要扣除其中 30 元”。
在接入 TCC 之前,用户编写 SQL:“update 账户表 set 余额 = 余额 -20 where 账户 = A”,便能一步完成扣款操作。
在接入 TCC 之后,就需要考虑如何将扣款操作分成两步完成:
- Try 操作:资源的检查和预留。
在扣款场景,Try 操作要做的事情就是先检查 A 账户余额是否足够,再冻结要扣款的 30 元(预留资源);此阶段不会发生真正的扣款。
- Confirm 操作:执行真正业务的提交。
在扣款场景下,Confirm 阶段做的事情就是发生真正的扣款,把 A 账户中已经冻结的 30 元钱扣掉。
- Cancel 操作:预留资源的释放。
在扣款场景下,扣款取消,Cancel 操作执行的任务是释放 Try 操作冻结的 30 元钱,使 A 账户回到初始状态。
2、并发控制
用户在实现 TCC 时,应当考虑并发性问题,将锁的粒度降到最低,以最大限度提高分布式事务的并发性。
以下还是以 A 账户扣款为例,“账户 A 上有 100 元,事务 T1 要扣除其中的 30 元,事务 T2 也要扣除 30 元,出现并发”。
在一阶段 Try 操作中,分布式事务 T1 和分布式事务 T2 分别冻结资金的那一部分资金,相互之间无干扰。这样在分布式事务的二阶段,无论 T1 是提交还是回滚,都不会对 T2 产生影响,这样 T1 和 T2 可以在同一笔业务数据上并行执行。
3、允许空回滚
如下图所示,事务协调器在调用 TCC 服务的一阶段 Try 操作时,可能会出现因为丢包而导致的网络超时。此时事务管理器会触发二阶段回滚,调用 TCC 服务的 Cancel 操作,而 Cancel 操作调用未出现超时。
TCC 服务在未收到 Try 请求的情况下收到 Cancel 请求,这种场景被称为空回滚。空回滚在生产环境经常出现,用户在实现 TCC 服务时,应允许空回滚的执行,即收到空回滚时返回成功。
4、防悬挂控制
如下图所示,事务协调器在调用 TCC 服务的一阶段 Try 操作时,可能会出现因网络拥堵而导致的超时。此时事务管理器会触发二阶段回滚,调用 TCC 服务的 Cancel 操作,Cancel 调用未超时。在此之后,拥堵在网络上的一阶段 Try 数据包被 TCC 服务收到,出现二阶段 Cancel 请求比一阶段 Try 请求先执行的情况,此 TCC 服务在执行晚到的 Try 之后,将永远不会再收到二阶段的 Confirm 或者 Cancel,造成 TCC 服务悬挂。
用户在实现 TCC 服务时,要允许空回滚,但是要拒绝执行空回滚之后 Try 请求,要避免出现悬挂。
5、幂等控制
无论是网络数据包重传,还是异常事务的补偿执行,都会导致 TCC 服务的 Try、Confirm 或者 Cancel 操作被重复执行;用户在实现 TCC 服务时,需要考虑幂等控制,即 Try、Confirm、Cancel 执行一次和执行多次的业务结果是一样的。
2.4、FMT 模式
FMT(Framework-managed transaction)框架管理事务,是一种无侵入的事务解决方案。该模式下,分布式事务框架会托管所有的事务操作,事务的一阶段和二阶段操作均由框架自动生成,用户 SQL 将作为分布式事务的一阶段,而二阶段由框架自动生成“提交/回滚”操作。
1、FMT 一阶段
FMT 的一阶段是分布式事务框架自动生成的,分布式事务框架会在一阶段拦截业务的 SQL 语句,在业务执行前,将业务 SQL 修改前的数据保存成原快照(undo log);在业务 SQL 执行之后,将更新的业务数据保存成新快照(redo log),最后用表名+主键值的方式生成行锁,来做分布式事务的并发控制。
解析 SQL 语义的目的是为了便于找到业务要更新的业务数据;而提取表元数据的目的为了找到业务表的主键和唯一性约束键,便于生成行锁。
2、FMT 二阶段
FMT 模式下,一阶段主要是为了保持 undo log、redo log 等中间数据,保持这些中间数据的目的是为了生成二阶段的操作。
- FTM 二阶段提交
二阶段提交操作是自动生成的,由于业务 SQL 在一阶段已经提交至数据库,故二阶段提交只需删除一阶段保存的中间数据(undo log、redo log和行锁)。
- FMT 二阶段回滚
二阶段回滚操作也是自动生成的,目的是使用 undo log 回滚一阶段业务 SQL 更新的业务数据。具体操作步骤是:
首先,需要校验脏写。校验脏写的方式使用 redo log 与数据库当前值进行对比,如果两份数据完全一致则说明没有出现脏写,如果两份数据不一致,则说明出现脏写;如果出现脏写,就需要转人工处理,不能再使用 undo log 回滚业务数据。
然后,还原业务数据。如果未出现脏写,则使用 undo log 回滚业务数据,使业务数据恢复到初始的值。
最后,删除中间数据。业务数据还原之后,便可以将中间数据(undo log、redo log和行锁)全部删掉,完成回滚操作。
2.5、XA 模式
XA 模式是另外一种无侵入的分布式事务解决方案,不同于 FMT 的是,XA 模式下,所有一阶段和二阶段都由数据库来完成。蚂蚁分布式事务框架在一阶段调用数据的一阶段 XA 接口(xa prepare),在二阶段调用数据的二阶段 XA 接口(xa commit/xa rollback)。
XA 模式有以下特点:
- 主流的数据库均支持 XA 写,覆盖面广;
- 与蚂蚁自研数据库 Oceanbase 深度定制,解决 XA 事务性能问题;
- 借助数据库的 MVCC 特性,实现了分布式 MVCC 和全局一致性读。
3、蚂蚁金服投入分布式事务 Seata 社区共建
2019 年 1 月,基于技术积累,阿里巴巴中间件团队发起了开源项目 Fescar(Fast & EaSy Commit And Rollback, Fescar),和社区一起共建分布式事务解决方案。Fescar 为解决微服务架构下的分布式事务问题交出了一份与众不同的答卷。而 Fescar 的愿景是让分布式事务的使用像本地事务的使用一样简单和高效。最终的目标是希望可以让 Fescar 适用于所有的分布式事务场景。
为了达到适用于更多的分布式事务业务场景的目标,蚂蚁金服加入 Fescar 社区共建,加入了 TCC 模式。
蚂蚁金服的加入引发了社区核心成员的讨论,为了达到适用于所有的分布式事务业务场景的目标,也为了社区更中立、更开放、生态更加丰富,社区核心成员们决定进行品牌升级,改名 Seata。Seata 意为:Simple Extensible Autonomous Transaction Architecture,是一套一站式分布式事务解决方案。
自开源以来,Seata 一直受益于社区的贡献。截止目前,分布式事务 Seata 已经拥有超过 7000 的 Star ,超 55 位 Contributors,开发者们的加入,使得社区的生态更加丰富也更有活力。
2019 年 5 月,Seata 将加入服务端 HA 集群支持,从此,Seata 可以达到生产环境使用的标准。
欢迎对分布式事务有热情的开发者们加入社区的共建中来,为 Seata 带来更多的想象空间。
总结
- 现场视频以及详细 PPT 回顾地址:tech.antfin.com/activities/…
- GitHub 地址:github.com/seata/seata
公众号:金融级分布式架构(Antfin_SOFA)