深度 | 为你解读 SOFA-DTX 分布式事务的设计演进路线上篇

简介: 我们将分为两篇来详细与大家解读,本篇为上篇。主要讲解 SOFA-DTX 如何满足支付业务的核心需求,保障分布式环境下交易一致性,并且在此之上的严苛的工程优化。

我们将分为两篇来详细与大家解读,本篇为上篇。主要讲解 SOFA-DTX 如何满足支付业务的核心需求,保障分布式环境下交易一致性,并且在此之上的严苛的工程优化。

image.png

随着互联网技术快速发展,数据规模增大,采用分布式数据库或者跨多个数据库的分布式微服务应用在中大规模企业普遍存在,而由于网络、机器等不可靠因素,数据一致性的问题很容易出现,与可扩展性、高可用容灾等要求并肩成为金融IT架构支撑业务转型升级的最大挑战之一。

在蚂蚁金服核心系统提出微服务化时,曾遇到了非常大的技术难题。首先是在服务拆分以后,面临跨服务的一致性问题;其次,支付宝当时的峰值交易量已经非常高了,在解决一致性问题的同时,还需要兼顾性能。

在当时,业界常用的分布式事务解决方案,通常能够实现跨服务一致性,但在热点数据的处理上,难以满足性能需求。

因此,蚂蚁金服微服务化过程中急需一种即能保证一致性,又能保证高性能的方案。当时经过一系列调研和讨论,最终选择了以 BASE 最终一致性思想为基础,在业务层实现两阶段提交的 TCC 分布式事务解决方案,该方案既能保证跨服务的最终一致,又能通过业务灵活加锁的方式大幅减少资源层加锁时间,高效处理热点问题。

随着蚂蚁金服业务不断丰富,业务逻辑越来越复杂,同时蚂蚁金融云上客户也越来越多,对分布式事务解决方案(简称 DTX,Distributed Transaction-eXtended)也不只是追求极限性能,也对接入便捷性、实时一致性有了要求。

我们将分为上下两篇文章,基于分布式事务在支付宝/蚂蚁金服核心金融场景下的演进路线,分享介绍在各阶段的关键设计考量和发展思路。

一. 分布式事务在蚂蚁金服的应用背景

1.1 支付宝 SOA 架构演进
最初的支付宝系统架构非常简单,是一个典型的 Web 系统,交易、支付、账务系统是没有分开的,全部集中在一个大工程里,底层用 DB 存储,Web 服务使用Spring 等框架开发,最上层使用 LB 转发用户流量。

image.png

随着代码规模和业务逻辑复杂度的增长,将所有的业务模块放在一个 Web 服务内的缺点日益凸显。因此推动了服务模块化拆分,演进成 SOA 架构,很好地解决了应用的伸缩性问题。

在这个架构中,交易系统、支付系统、账务系统分别独立出来了,它们之间的调用靠 RPC,除了服务模块化拆分,还有数据库的垂直拆分,根据业务系统功能,将数据库拆分为多个,每个系统使用独立的数据库。

image.png

1.2 数据一致性问题
数据垂直拆分后,减少了单库的性能瓶颈,但也带来了数据一致性的问题。如下图所示:

image.png

一笔转账业务需要跨交易、支付、账务三个系统才能完成整个流程,并且要求这三个系统要么一起成功,要么一起失败。如果有的成功,有的失败,就有可能造成数据不一致,那么这个业务行为肯定不会被用户所理解。所以,我们系统面临的问题就是 SOA 架构下的数据一致性。

二. 关键设计考量:金融级一致性要求与海量并发处理能力

考虑到在解决一致性问题的同时,还要兼顾海量并发处理能力,蚂蚁金服内部结合BASE 理论的思想,选择在业务层实现 2PC(两阶段事务提交)的方式来解决该问题。

BASE 理论是指 BA(Basic Availability,基本业务可用性);S(Soft state,柔性状态);E(Eventual consistency,最终一致性)。该理论认为为了可用性、性能与降级服务的需要,可以适当降低一点一致性的要求,即“基本可用,最终一致”。

一般来讲,在传统的单机数据库或者集中式存储数据库里,对于一致性的要求是实时一致;而对于分布式系统,服务与服务之间已经实现了功能的划分,逻辑的解耦,也就更容易弱化一致性,允许处理过程中,数据的短暂不一致,只需要保证数据最终达到一致。

2.1 分布式金融核心的零差错容忍保证:TCC 模型
如本文开篇所述,蚂蚁金服大部分系统的分布式事务解决方案以BASE最终一致性思想为基础,在业务层实现两阶段提交的 TCC(Try-Confirm-Cancel)分布式事务解决方案,以确保在一致性问题的保障和性能方面达到最佳平衡。

TCC 分布式事务模型包括三部分,如下所示

  • 主业务服务:主业务服务为整个业务活动的发起方,服务的编排者,负责发起并完成整个业务活动。
  • 从业务服务:从业务服务是整个业务活动的参与方,负责提供 TCC 业务操作供主业务服务调用
  • 初步操作 Try:完成所有业务检查,预留必须的业务资源。
  • 确认操作 Confirm:真正执行的业务逻辑,不作任何业务检查,只使用 Try 阶段预留的业务资源。因此,只要 Try 操作成功,Confirm 必须能成功。另外,Confirm 操作需满足幂等性,保证一笔分布式事务有且只能成功一次。
  • 取消操作 Cancel:释放 Try 阶段预留的业务资源。同样的,Cancel 操作也需要满足幂等性。
  • 业务活动管理器:业务活动管理器管理控制整个业务活动,包括记录维护 TCC全局事务的事务状态和每个从业务服务的子事务状态,并在业务活动提交时调用所有从业务服务的Confirm 操作,在业务活动取消时调用所有从业务服务的Cancel 操作。

image.png

2.1.1 TCC 与 XA 对比 --- 并发性优势

TCC 把两阶段拆分成了两个独立的阶段,通过资源业务锁定的方式进行关联。资源业务锁定方式的好处在于,既不会阻塞其他事务在第一阶段对于相同资源的继续使用,也不会影响本事务第二阶段的正确执行。

image.png

XA 模型的并发事务

image.png

TCC 模型的并发事务

从上面的对比可以发现,TCC 模型相比 XA 模型进一步减少了资源锁的持有时间。XA 模型下,在 Prepare 阶段是不会把事务1所持有的锁资源释放掉的,如果事务2和事务1争抢同一个资源,事务2必须等事务1结束之后才能使用该资源。

而在 TCC 里,因为事务1在 Try 阶段已经提交了,那么事务2可以获得互斥资源,不必再等待事务1的 Confirm 或 Cancel 阶段执行完。也就是说,事务2的 Try 阶段可以和事务1的 Confirm 或 Cancel 阶段并行执行,从而获得了一个比较大的并发性能提升。

2.2 保证大规模交易下的并发性能:极致性能优化
2010年开始,每年天猫双十一大促的峰值交易量成倍增加,如下图所示:

image.png

折线上的点代表着每一年的交易峰值(TPS)。前面讲过,每次分布式事务需要协调多个参与方,因此,每年交易峰值乘以一个倍数才是分布式事务框架要协调最终达到一致性状态的峰值分支事务数量。事实上,对分布式事务服务来说,2017年已经达到每秒百万级水平。

回顾历年大促,2013年对支付宝的交易处理是对我们挑战很大的一年,2013年双11提出的峰值目标,按照当时大促准备的计算和存储资源,是无法完成的,怎么办?

只能选择继续优化事务框架,让它尽量少消耗资源,并去获得一个更大的性能提升。我们根据 TCC 模型的特点以及日常开发时积累的经验,针对性能问题,做了以下两类优化。

2.2.1 极致性能优化之同库模式
之前的业务活动管理器是一个单独的服务,每次启动业务活动、登记业务操作、提交或回滚业务活动等操作都需要与业务活动管理器交互,并且交互次数与参与者个数成正相关。

因此,为了追求极限性能,将业务活动管理器的远程存储替换成本地存储,如下所示:

image.png

减少了 RPC 的调用,同时还会做一些减少存储次数的优化,从而获得性能收益。

通过这种方式,减少 RPC 调用耗时,大幅降低事务执行时间。同时还针对支付、账务等访问频繁的特殊从业务服务,优化处理过程,不再创建单独的分支事务记录,而是将这个信息与主事务记录合并,在创建主事务记录的同时,记录分支事务,最大程度减少与数据库的交互次数,从而获得更高的性能收益。

下面是同库模式优化前后的时序图对比:

image.png

优化前时序图

image.png

优化后时序图

其中,绿色方块表示本地数据库访问。可以发现,优化后减少了:(2+n) 次 PRC 延迟 + 1次数据库访问延迟(n表示参与者个数)。

2.2.2 极致性能优化之异步化
从理论上来说,只要业务允许,事务的第二阶段什么时候执行都可以,因此资源已经被业务锁定,不会有其他事务动用该事务锁定的资源。如下图所示:

image.png

这就是 TCC 分布式事务模型的二阶段异步化功能,各从业务服务的第一阶段执行成功以后,主业务服务就可以提交完成,框架会保证正确记录事务状态,然后再由框架异步的执行各从业务服务的第二阶段,从而比较完整的诠释最终一致性。

大促的尖峰时刻是从零点开始的几十秒或一分钟之内,在这个时刻的交易,我们会把二阶段的操作从同步转成异步,在冲高的那一刻,二阶段就停止了,一阶段正常扣款,等着交易零点三十分或者夜里一点开始回落的时候,我们才开始打开二阶段,集中做二阶段的动作。

优化后,Confirm 阶段在空闲时段异步执行:

假设 Confirm 阶段与 Try 阶段耗时相同,单个事务耗时减少50%

数据库资源消耗减少50%

本篇总结

为了满足支付业务的核心需求,保障分布式环境下交易一致性。蚂蚁基于 BASE 思想,在业务层实现了 TCC 模型,并且为了业务发展的需求,优化了其工程实践,实现海量并发处理能力,让它的性能可以达到比业界其它产品高很多的状态。

接着,上层业务系统的复杂、业务种类的丰富等等业务需求向分布式事务提出了更高的要求,我们如何思考和应对的呢?下一篇我们将与大家详谈

了解更多:金融级云原生架构解决方案 SOFA

通过十多年的探索与实践,我们积累了大量的架构设计原则、最佳实践和产品服务案例,并构建了一整套金融级云原生架构解决方案,这套架构叫做 SOFA(Scalable Open Financial Architecture,分布式事务 DTX 亦是其重要的组成部分),源自蚂蚁内部分布式架构实践,是一整套完整的金融级中间件产品技术和演进式架构转型服务体系,已经向国内金融机构开放。

蚂蚁金服期望逐步向社区开放(链接如下):

https://github.com/alipay

开源 SOFA 体系内的各个组件,帮助大家更加敏捷稳妥地实现金融级云原生架构。我们也非常欢迎来自技术社区和各行业的伙伴能够参与共同探讨、交流和共建,使其更加完善和稳固,满足更多金融级架构转型升级的需求。

目录
相关文章
|
数据库 SQL Cloud Native
深度 | 为你解读 SOFA-DTX 分布式事务的设计演进路线下篇
众所周知,蚂蚁金服在近几年除了支付业务以外,还发展出了很多复杂的金融业务,比如财富、保险、银行等。同时,在2014年蚂蚁金服全面开启了金融云时代,也会对外赋能合作方和客户。
1479 0
|
SQL 存储 Cloud Native
深度| 一篇文章为你解读SOFA-DTX 分布式事务的设计演进路线
小蚂蚁说: 本文介绍了蚂蚁金服在分布式事务上,经过多年发展,服务于内外部大量不同业务,沉淀出的一整套包含TCC、FMT、XA模型的分布式事务解决方案。并且在持续对外输出的过程中,进一步打磨产品体验,适应各种严苛的金融级场景和机构需求,比如跨机房跨地域的容灾业务连续性保障能力等。
5787 0
|
3月前
|
NoSQL Java Redis
太惨痛: Redis 分布式锁 5个大坑,又大又深, 如何才能 避开 ?
Redis分布式锁在高并发场景下是重要的技术手段,但其实现过程中常遇到五大深坑:**原子性问题**、**连接耗尽问题**、**锁过期问题**、**锁失效问题**以及**锁分段问题**。这些问题不仅影响系统的稳定性和性能,还可能导致数据不一致。尼恩在实际项目中总结了这些坑,并提供了详细的解决方案,包括使用Lua脚本保证原子性、设置合理的锁过期时间和使用看门狗机制、以及通过锁分段提升性能。这些经验和技巧对面试和实际开发都有很大帮助,值得深入学习和实践。
太惨痛: Redis 分布式锁 5个大坑,又大又深, 如何才能 避开 ?
|
5月前
|
NoSQL Redis
基于Redis的高可用分布式锁——RedLock
这篇文章介绍了基于Redis的高可用分布式锁RedLock的概念、工作流程、获取和释放锁的方法,以及RedLock相比单机锁在高可用性上的优势,同时指出了其在某些特殊场景下的不足,并提到了ZooKeeper作为另一种实现分布式锁的方案。
135 2
基于Redis的高可用分布式锁——RedLock
|
5月前
|
缓存 NoSQL Java
SpringBoot整合Redis、以及缓存穿透、缓存雪崩、缓存击穿的理解分布式情况下如何添加分布式锁 【续篇】
这篇文章是关于如何在SpringBoot应用中整合Redis并处理分布式场景下的缓存问题,包括缓存穿透、缓存雪崩和缓存击穿。文章详细讨论了在分布式情况下如何添加分布式锁来解决缓存击穿问题,提供了加锁和解锁的实现过程,并展示了使用JMeter进行压力测试来验证锁机制有效性的方法。
SpringBoot整合Redis、以及缓存穿透、缓存雪崩、缓存击穿的理解分布式情况下如何添加分布式锁 【续篇】
|
28天前
|
存储 NoSQL Java
使用lock4j-redis-template-spring-boot-starter实现redis分布式锁
通过使用 `lock4j-redis-template-spring-boot-starter`,我们可以轻松实现 Redis 分布式锁,从而解决分布式系统中多个实例并发访问共享资源的问题。合理配置和使用分布式锁,可以有效提高系统的稳定性和数据的一致性。希望本文对你在实际项目中使用 Redis 分布式锁有所帮助。
87 5
|
2月前
|
NoSQL Java 数据处理
基于Redis海量数据场景分布式ID架构实践
【11月更文挑战第30天】在现代分布式系统中,生成全局唯一的ID是一个常见且重要的需求。在微服务架构中,各个服务可能需要生成唯一标识符,如用户ID、订单ID等。传统的自增ID已经无法满足在集群环境下保持唯一性的要求,而分布式ID解决方案能够确保即使在多个实例间也能生成全局唯一的标识符。本文将深入探讨如何利用Redis实现分布式ID生成,并通过Java语言展示多个示例,同时分析每个实践方案的优缺点。
65 8
|
2月前
|
NoSQL Redis
Redis分布式锁如何实现 ?
Redis分布式锁通过SETNX指令实现,确保仅在键不存在时设置值。此机制用于控制多个线程对共享资源的访问,避免并发冲突。然而,实际应用中需解决死锁、锁超时、归一化、可重入及阻塞等问题,以确保系统的稳定性和可靠性。解决方案包括设置锁超时、引入Watch Dog机制、使用ThreadLocal绑定加解锁操作、实现计数器支持可重入锁以及采用自旋锁思想处理阻塞请求。
61 16
|
2月前
|
缓存 NoSQL PHP
Redis作为PHP缓存解决方案的优势、实现方式及注意事项。Redis凭借其高性能、丰富的数据结构、数据持久化和分布式支持等特点,在提升应用响应速度和处理能力方面表现突出
本文深入探讨了Redis作为PHP缓存解决方案的优势、实现方式及注意事项。Redis凭借其高性能、丰富的数据结构、数据持久化和分布式支持等特点,在提升应用响应速度和处理能力方面表现突出。文章还介绍了Redis在页面缓存、数据缓存和会话缓存等应用场景中的使用,并强调了缓存数据一致性、过期时间设置、容量控制和安全问题的重要性。
44 5
|
3月前
|
缓存 NoSQL Java
大数据-50 Redis 分布式锁 乐观锁 Watch SETNX Lua Redisson分布式锁 Java实现分布式锁
大数据-50 Redis 分布式锁 乐观锁 Watch SETNX Lua Redisson分布式锁 Java实现分布式锁
75 3
大数据-50 Redis 分布式锁 乐观锁 Watch SETNX Lua Redisson分布式锁 Java实现分布式锁