分布式事务的终极方案:从2PC到Saga模式,Seata在金融级场景的落地实践

大家好,聊下分布式事务

在单体应用时代,@Transactional一个注解就能搞定一切,ACID(原子性、一致性、隔离性、持久性)那是数据库给我们的承诺。但自从微服务大行其道,数据库被拆得七零八落,这个承诺就失效了。

特别是在金融场景,你转账转丢了 100 块钱,用户能把你系统投诉到下架。

很多同学在面试时满嘴 CAP、BASE 理论,真到了落地,要么是用性能极差的 XA 两阶段提交,要么就是写了一堆难以维护的补偿代码。

So 接下来带大家彻底把Seata2PCSaga这些概念揉碎了,看看在 2026 年的今天,我们到底该怎么做分布式事务。


一、 痛苦的根源:当 ACID 遇到网络延迟

在聊方案前,先对齐一下认知。

为什么微服务下事务这么难?本质是因为网络是不可靠的,而数据库是独立的

场景很简单:

  1. 服务 A(订单服务)扣减库存。
  2. 服务 A调用服务 B(支付服务)扣款。
  3. 服务 B扣款成功,但网络超时,服务 A以为失败了,回滚了库存。

结果:库存没少,钱扣了。这就是典型的数据不一致

在金融场景下,我们追求的往往不是强一致性(那会把系统吞吐量拖垮),而是最终一致性。我们要保证,无论中间发生了什么异常,钱和货,最终必须对得上账。


二、 上古神兽与现代兵器:从 2PC 到 Seata

1. 2PC (XA):强一致性的代价

两阶段提交(2PC)是数据库层面的标准。

  • 第一阶段(Prepare):协调者问所有参与者:“你们能提交吗?”参与者锁定资源,写 Undo/Redo Log,但不提交。
  • 第二阶段(Commit/Rollback):如果大家都说 Yes,协调者喊“提交”;只要有一个 No,全部“回滚”。

痛点: 这玩意儿在微服务里就是性能杀手。它在 Prepare 阶段会长时间持有数据库锁。如果你的微服务链路长,整个系统的吞吐量会瞬间跌到谷底。而且,它还有“单点故障”和“数据不一致”(Commit 阶段挂了)的风险。

2. Seata AT 模式:无侵入的“黑科技”

阿里开源的 Seata(Simple Extensible Autonomous Transaction Architecture)之所以火,是因为它的AT 模式实在是太好用了。

它的核心逻辑是:拦截 SQL,自动生成反向 SQL。

你不需要改业务代码,只需要加上@GlobalTransactional。Seata 代理了你的 DataSource,在第一阶段提交前,它会解析你的 SQL,保存“更新前镜像”和“更新后镜像”到undo_log表。

如果需要回滚,它就用镜像生成反向 SQL 还原数据。

这里有个逻辑图,大家先看懂 Seata AT 的原理:


三、 核心实战:Seata 在 Spring Boot 3 中的落地

光说不练假把式。我们来看代码。环境基于Java 21+Spring Boot 3.2+Seata 2.0

示例 1: 依赖配置 (Gradle Kotlin DSL)

别再用老掉牙的 Maven XML 了,跟上时代。

// build.gradle.kts dependencies { implementation("org.springframework.boot:spring-boot-starter-web") implementation("com.alibaba.cloud:spring-cloud-starter-alibaba-seata:2023.0.0.0-RC1") // 注意版本兼容 implementation("org.mybatis.spring.boot:mybatis-spring-boot-starter:3.0.3") // 必须引入 JDBC 驱动 runtimeOnly("com.mysql:mysql-connector-j") }

示例 2: 业务入口 - 全局事务发起

这是最简单的部分,也是 AT 模式的魅力。

package com.howell.arch.transaction.service; import io.seata.spring.annotation.GlobalTransactional; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service public class OrderService { private final StockClient stockClient; private final PaymentClient paymentClient; private final OrderMapper orderMapper; public OrderService(StockClient stockClient, PaymentClient paymentClient, OrderMapper orderMapper) { this.stockClient = stockClient; this.paymentClient = paymentClient; this.orderMapper = orderMapper; } /** * 下单核心逻辑 * @GlobalTransactional: 开启 Seata 全局事务 * name: 事务名称,方便在 Seata 控制台追踪 * rollbackFor: 指定回滚异常 */ @GlobalTransactional(name = "create-order-tx", rollbackFor = Exception.class) @Transactional // 本地事务也要开启 public void createOrder(OrderDTO orderDto) { // 1. 本地落单 Order order = new Order(); order.setUserId(orderDto.userId()); order.setAmount(orderDto.amount()); order.setStatus("CREATED"); orderMapper.insert(order); // 2. 远程扣减库存 (RPC 调用) // 如果这里失败,Seata 会感知到异常,触发 Phase 2 的回滚 var stockResult = stockClient.decrease(orderDto.productId(), orderDto.count()); if (!stockResult.isSuccess()) { throw new RuntimeException("库存扣减失败: " + stockResult.getMessage()); } // 3. 远程扣款 (RPC 调用) // 模拟:如果这里抛出异常,Order 表的数据会被 Seata 自动回滚,库存也会被回滚 var payResult = paymentClient.pay(orderDto.userId(), orderDto.amount()); if (!payResult.isSuccess()) { throw new RuntimeException("支付失败: " + payResult.getMessage()); } } }

运行结果说明: 当paymentClient.pay抛出异常时,Seata TC 会通知 OrderService 和 StockService 进行回滚。Order 表刚插入的数据会被删除,Stock 表扣减的库存会被加回来。这一切对业务代码是透明的。


四、 进阶:当 AT 模式搞不定时 —— Saga 模式

AT 模式虽然爽,但在金融场景或者长事务场景下,它有致命弱点:

  1. 全局锁:AT 模式为了隔离性(写隔离),需要持有全局锁。如果热点数据(比如爆款商品的库存)并发极高,AT 模式会因为争抢全局锁导致性能雪崩。
  2. 非数据库资源:如果你的服务不是操作数据库,而是调用第三方的 API(比如调用银行接口转账),Seata 没法去解析 SQL 生成 undo_log。

这时候,Saga 模式登场。

Saga 的核心思想是:把长事务拆分成多个本地短事务,由状态机引擎协调。每个操作都有一个对应的“补偿操作”

  • 正向操作:T1 -> T2 -> T3
  • 如果 T2 失败:执行 C2 -> C1 (C 代表 Compensate 补偿)

示例 3: Saga 状态机定义 (JSON 方式)

Seata 的 Saga 模式通常使用 JSON 来定义状态机流程。这比写代码更直观,也支持可视化编排。

{ "Name": "financialTransferSaga", "Comment": "金融转账Saga事务", "StartState": "DeductMoney", "Version": "1.0", "States": { "DeductMoney": { "Type": "ServiceTask", "ServiceName": "accountService", "ServiceMethod": "deduct", "CompensateState": "CompensateDeductMoney", "Next": "DepositMoney", "Input": [ "$.[businessKey]", "$.[amount]" ] }, "CompensateDeductMoney": { "Type": "ServiceTask", "ServiceName": "accountService", "ServiceMethod": "compensateDeduct", "Input": [ "$.[businessKey]", "$.[amount]" ] }, "DepositMoney": { "Type": "ServiceTask", "ServiceName": "accountService", "ServiceMethod": "deposit", "Input": [ "$.[targetAccount]", "$.[amount]" ] } } }

示例 4: Saga 模式的业务实现 (Java)

在 Java 代码中,你需要实现正向方法和补偿方法。

package com.howell.arch.transaction.saga; import org.springframework.stereotype.Service; import java.math.BigDecimal; @Service("accountService") public class AccountService { /** * 正向操作:扣款 * 这里的入参通常需要包含业务流水号,用于幂等控制 */ public boolean deduct(String businessKey, BigDecimal amount) { System.out.println("Saga正向操作:扣除账户余额,流水号:" + businessKey); // 实际业务逻辑:update account set balance = balance - amount where ... // 必须记录流水,防止重复扣款 return true; } /** * 补偿操作:回滚扣款 * 当后续步骤失败时,Seata Saga 引擎会调用此方法 */ public boolean compensateDeduct(String businessKey, BigDecimal amount) { System.out.println("Saga补偿操作:返还账户余额,流水号:" + businessKey); // 实际业务逻辑:update account set balance = balance + amount where ... // 关键点:必须处理“空回滚”和“防悬挂” return true; } /** * 正向操作:入账 */ public boolean deposit(String targetAccount, BigDecimal amount) { System.out.println("Saga正向操作:目标账户入账"); // 模拟失败,触发回滚 if (amount.compareTo(new BigDecimal("10000")) > 0) { throw new RuntimeException("金额过大,触发风控拦截"); } return true; } }

运行结果说明: 当deposit方法抛出异常时,Saga 引擎会捕获该异常,并自动查找状态机定义,反向调用compensateDeduct方法,打印出“Saga补偿操作:返还账户余额…”,从而保证数据的最终一致性。


五、 深度解析:Saga 模式的逻辑视图

Saga 模式不仅是代码,更是一种架构风格。它分为编排式 (Orchestration)协同式 (Choreography)。Seata 采用的是编排式,有一个中心化的 Coordinator。


六、 生产环境的“雷区”与“邪修”方案

架构师的价值不在于会用工具,而在于知道什么时候不用工具,或者怎么魔改工具。

1. 只有新手才相信“自动回滚”

在金融场景,Saga 的补偿方法(Compensate)极其重要,但也是最容易出 Bug 的地方。 这里有三个必须解决的难题:

  • 空回滚:Seata 第一阶段还没执行(比如网络丢包),直接触发了第二阶段回滚。你的补偿代码去“还钱”,结果发现根本没“扣钱”。结果:用户白赚一笔。
  • 幂等性:网络抖动,Seata 调用了两次扣款,或者调用了两次补偿。结果:资金错乱。
  • 防悬挂:二阶段的回滚(Compensate)比一阶段的正向操作(Try/Action)先到了(网络拥堵导致)。处理了回滚后,正向操作才到。结果:先退款了,然后又扣款了。

示例 5: 解决“空回滚”与“防悬挂”的通用模板

我通常会设计一张transaction_control表来记录每个分支事务的状态。

package com.howell.arch.transaction.utils; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; @Component public class TccAnsSagaGuardian { /** * 这是一个通用的幂等与防悬挂控制逻辑 */ @Transactional public boolean isActionAllowed(String xid, String branchId, String opType) { // 1. 查询当前事务分支记录 var record = repository.findByXidAndBranchId(xid, branchId); // 防悬挂检查:如果是正向操作(Action),但发现已经有回滚(Rollback)记录 if ("ACTION".equals(opType) && record != null && "ROLLBACK".equals(record.getStatus())) { return false; // 拒绝执行 } // 幂等检查:如果当前操作已经执行过 if (record != null && opType.equals(record.getStatus())) { return false; // 已经执行过,直接返回成功 } // 插入或更新状态 upsertStatus(xid, branchId, opType); return true; } }

2. “邪修”架构:本地消息表 + 最终一致性

如果你觉得 Seata 太重,或者不想引入一个新的中间件(TC Server 也是要维护的),那么我推荐一个“返璞归真”的方案——本地消息表。这是 eBay 最早提出的方案,也是我认为最稳健的方案。

思路

  1. 业务数据和“消息记录”在同一个本地事务里提交到数据库。
  2. 有一个定时任务(或监听 Binlog)不断轮询消息表,把消息投递到 MQ。
  3. 下游消费 MQ,成功后回调删除消息。

这种方案完全不需要分布式事务管理器,依赖的是本地事务 + 重试

示例 6: 本地消息表逻辑 (伪代码)

@Transactional public void placeOrder(Order order) { // 1. 存业务数据 orderRepository.save(order); // 2. 存消息数据 (状态:待发送) // 这一步必须和上一步在同一个 @Transactional 下 LocalMessage msg = new LocalMessage(); msg.setTopic("order-created"); msg.setContent(toJson(order)); msg.setStatus("PENDING"); messageRepository.save(msg); } // 事务提交后,业务和消息同时落地,绝不会丢失。

逻辑图解:

七、 架构师思维总结

写了这么多,最后做个总结。作为架构师,在做技术选型时,不要只看“新”和“热”。

  1. 能不分就不分:如果业务允许,尽量在一个微服务内闭环,用本地事务解决。微服务拆分过细是万恶之源。
  2. Seata AT 是首选:对于大多数非金融核心、并发量中等的业务,Seata AT 模式是开发效率最高的,几乎零侵入。
  3. Saga 是长事务利器:涉及跨机构、跨语言、流程极长的业务(如复杂的审批流、跨境转账),Saga 是唯一解。
  4. TCC/手动补偿是底牌:当性能要求极致,或者需要强控每一个步骤时,手写 TCC 或 Saga 补偿逻辑。但要做好心理准备,代码量会翻倍。
  5. 本地消息表是“核武器”:当你不信任任何分布式事务框架,或者系统极度核心(如账务中心),本地消息表+对账系统 才是最稳的。

Takeaway: 分布式事务没有银弹。一致性(Consistency)可用性(Availability)分区容错性(Partition tolerance),你只能取其二。在互联网架构中,我们通常选择AP + 最终一致性。而 Seata 和 Saga,就是实现最终一致性的那座桥。

希望这篇文章能让你在面对分布式事务时,不再是“两眼一抹黑”,而是能从容地掏出架构图,告诉团队:“这事儿,我有三种方案。”

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/1155460.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

别再迷信 Playwright 了,真正决定成败的不是浏览器

我以前也以为,只要页面是 JS 渲染的,上 Playwright 或 Selenium,问题基本就解决了。 后来在一个真实项目里,我用同一个目标站点做了三组对比实验,结论非常清楚: 浏览器自动化解决的是页面执行问题&#xff…

3BHL000406P0100 工业控制模块(ABB)核心特性与应用

3BHL000406P0100是ABB推出的工业级专用控制模块,归属同系列核心备件体系,与3BHL000406P0104等型号技术同源,主打高适配性与运行可靠性,专为逆变器、驱动设备配套控制场景设计。核心功能涵盖驱动信号处理、逻辑运算及精准指令输出&…

【性能测试】1_JMeter_JMeter环境搭建和配置

文章目录一、安装JDK二、安装JMeter三、JMeter基本配置3.1 界面汉化3.2 修改主题一、安装JDK 1、下载安装JDK 官网下载:https://www.oracle.com/ 提示:下载时注意电脑系统是32位或64位 2、配置系统环境变量 我的电脑 —> 属性 —> 高级系统设…

实时多维分析系统架构设计:从理论到实践

实时多维分析系统架构设计:从理论到实践关键词:实时多维分析、系统架构设计、数据处理、分析算法、实践应用摘要:本文围绕实时多维分析系统架构设计展开,从理论基础入手,详细阐述了相关核心概念、算法原理和数学模型。…

【性能测试】6_性能测试基础 _TPS算法

文章目录一、PV和UV二、常用平均并发数计算公式2.1 普通计算方法2.2 二八原则计算方法 (核心指导原则)2.3 按照业务数据进行计算2.3.1 计算模拟用户正常业务操作(稳定性测试) 的并发量2.3.2 计算模拟用户峰值业务操作(…

万字长文!从零开始构建你的第一个 ReAct Agent

其实对于 AI Agent 的介绍已经非常非常多了,简单来说,AI Agent 是一种具备“感知-思考-行动”能力的智能体,它能接收任务,自动推理并调用外部工具完成复杂流程。 而在众多 Agent 架构中,ReAct 框架(Reasoni…

Spark动态分区裁剪:大幅提升查询性能的黑科技

Spark动态分区裁剪:大幅提升查询性能的黑科技 关键词:Spark、动态分区裁剪、查询性能、数据处理、大数据 摘要:本文将深入探讨Spark动态分区裁剪这一能大幅提升查询性能的技术。我们会先介绍其背景知识,然后用通俗易懂的方式解释核…

智能销售管理软件助力企业获客与销售业绩提升

智能销售管理软件的定义与作用智能销售管理软件是集成多种功能的系统,专为提高企业获客效率和销售业绩设计。以VertGrow AI销冠为例,这款软件利用AI引流获客系统,实现自动化的客户获取和关系管理。有了这种智能获客工具,企业可以更…

HBase RegionServer高可用:基于Raft的故障自动恢复实现

HBase RegionServer高可用:基于Raft的故障自动恢复实现 一、引言 (Introduction) 钩子 (The Hook) “昨天晚上11点,我们的实时推荐系统突然崩溃了!” 运维同学的消息让整个团队瞬间清醒。排查后发现,是HBase集群中的一个RegionSer…

2026本科必备10个降AI率工具测评

2026本科必备10个降AI率工具测评 2026年本科生必备降AI率工具测评:为何需要这份榜单? 在人工智能技术不断进步的今天,论文查重系统对AIGC内容的识别能力显著提升。对于2026年的本科生而言,单纯依靠人工改写已难以满足学术规范要求…

航天器用地球地平线传感器行业全景分析:技术演进、竞争格局与市场规模预测(2026–2032)

航天器用地球地平线传感器(以下简称“地球敏感器”)是航天器姿态控制系统(AOCS)的核心载荷,通过探测地球与太空的辐射边界(主流为红外波段),实现航天器相对地球姿态的精准定位。其核…

【AI应用开发工程师】-Gemini写前端的一个坑

Gemini写前端的一个坑:当AI设计师固执己见时… 你的AI助手是否也曾像个固执己见的设计师,坚持用“过气”的Tailwind V3,而你明明知道V4才是真香?别急,这篇文章就是为你准备的“设计师沟通指南”! &#x1f…

刚入行Java如何快速提升自己的实力,让自己更有竞争力?

程序员提升自身核心竞争力最好的方式当然是研究Java开源框架的源码!据不完全统计,现在市面上不管是初级,中级,还是高级岗,面试的时候都有可能会问到源码中的问题,它已经成为程序员常规必备的一个技术点。如…

低成本MEMS寻北仪,如何破解中小矿山掘进定向困局

在智慧矿山建设的浪潮席卷行业之时,大型矿山凭借充足的资金储备,早已配齐各类高精尖定向设备,掘进效率与安全系数双双拉满。然而,占行业绝大多数的中小矿山,却始终被一道难题牢牢困住——想要实现精准掘进,…

做好项目管理的4条潜规则,比埋头干活更管用

项目管理的核心是搞定事、管好人,但只盯着甘特图、里程碑的“硬操作”远远不够。那些藏在流程背后的职场潜规则,往往决定着项目的成败。看懂并顺应这些规则,能让你的项目推进事半功倍。沟通到位,比方案完美更重要 很多项目经理会陷…

大数据领域ClickHouse的资源调度策略

ClickHouse资源调度策略深度解析:从理论到实践的全栈优化 元数据框架 标题:ClickHouse资源调度策略深度解析:从理论到实践的全栈优化 关键词:ClickHouse, 资源调度, MPP架构, 并行处理, 成本模型, 资源组, 负载均衡 摘要:本文以MPP(大规模并行处理)架构为理论基础,系…

数琨创享成功入选江苏省首批入库培育数据企业,踏入数智发展新征程

近日,江苏省数据局正式发布江苏省第一批入库培育数据企业名单。经多轮严格筛选,苏州数琨创享信息技术有限公司凭借在数据领域的综合实力与创新成效,同时成功入选数据服务、数据应用、数据技术培育类型证书。这不仅是对公司数据业务能力、技术…

【AI应用开发工程师】-别让你的模型患上“金鱼记忆症”

AI多轮对话:别让你的模型患上“金鱼记忆症” 你以为多轮对话就是让AI记住聊天历史?太天真了!这就像要求一个人记住整本《战争与和平》的每一个细节,然后立即分析第583页的隐喻意义一样困难。 📖 文章目录(点…

大厂面试必看!这三步快速吃透业务,拿满面试分!

正在备战大厂面试的学弟学妹们注意啦!“如何快速了解一项业务” 绝对是高频必考题,面试官问这个问题,核心就是想考察你的业务理解力和环境适应力,能不能快速上手工作。作为过来人,学长整理了一套亲测好用的回答逻辑&am…

基于python+django+mysql的小区物业管理系统+计算机专业

该系统是基于pythondjango开发的小区物业管理系统。适用场景:大学生、课程作业、毕业设计。学习过程中,如遇问题可以在github给作者留言。主要功能有:业主管理、报修管理、停车管理、资产管理、小区管理、用户管理、日志管理、系统信息。加油…