发布时间:2026/9/3 8:12:35
分布式事务方案选型-元一软件 在微服务架构下跨服务的数据一致性保证是个绕不开的难题。如果在一次业务操作中写多个DB或既写本地DB、又调外部RPC接口就会面临一致性问题怎么保证跨库、跨系统的操作要么都成功、要么都失败呢先说结论最常用的分布式事务方案是TCC、Saga、可靠消息。最推荐的开源框架是Apache Seata支持多种分布式事务模式包括TCC、Saga、AT等。下面展开介绍。BASE理论在一个数据库事务内多个sql写操作可以保证强一致。而到了分布式场景只能做到最终一致。这就引出了BASE理论基本可用、软状态、最终一致性即允许系统存在中间不一致状态只要最终达到一致即可。分布式事务方案大多在BASE理论指导下放弃强一致追求最终一致。主流方案和最佳场景下面每种方案都用一个独立的、最适合的业务场景来介绍。1. TCC最佳场景下单时扣库存、扣余额比如下单流程涉及订单服务、库存服务、支付服务。需要同时创建订单、冻结库存、冻结余额并发量较高且对一致性要求严格。原理与实现机制TCC需要一个协调者一般称为事务管理器负责编排整个流程。但资源锁定做到了业务层、而非DB层由应用代码显式实现Try、Confirm、Cancel三个接口。TM事务管理器负责调用各个服务的Try方法根据结果决定全部Confirm或全部Cancel。需记录事务日志处理重试和异常RM资源管理器各业务服务自身需要提供Try/Confirm/Cancel三个接口并保证幂等可能会调多次流程细节TM依次调用每个服务的Try接口预留业务资源如冻结库存、冻结余额不用上数据库行锁若所有Try成功TM依次调用Confirm接口将预留资源实际消耗或状态推进若任何Try失败或超时TM调用所有已成功服务的Cancel接口释放预留资源框架需要处理空回滚Try未执行但Cancel被调用、悬挂Cancel比Try先到、幂等接口被重复调用等问题一般通过记录全局事务状态并用全局事务ID查询状态来实现举例库存服务Try/Confirm/Cancel// Try阶段冻结库存、扣减可用库存 Transactional public boolean tryFreezeInventory(String productId, int qtyDelta, String txId){// 冻结库存、扣减可用库存update inventorysetfrozen_qtyfrozen_qty #{qtyDelta}, available_qty available_qty - #{qtyDelta}// where product_id#{productId} and available_qty - #{qtyDelta} 0if(inventoryMapper.freeze(productId, qtyDelta)0){// 冻结库存成功记录库存变更明细状态为TRY freezeRecordMapper.insert(txId, productId, qtyDelta);returntrue;}else{returnfalse;}}// Confirm阶段扣减冻结库存 Transactional public void confirm(String productId, int qtyDelta, String txId){// 扣减冻结库存update inventorysetfrozen_qtyfrozen_qty -#{qtyDelta} where product_id #{productId}inventoryMapper.deduct(productId, qtyDelta);// 更新库存变更明细为已完成 freezeRecordMapper.updateStatus(txId,CONFIRMED);}// Cancel阶段释放冻结库存、可用库存 Transactional public void cancel(String productId, int qtyDelta, String txId){FreezeRecord rfreezeRecordMapper.selectByTxId(txId);if(rnull){return;// 空回滚}// 释放冻结库存、可用库存update inventorysetfrozen_qtyfrozen_qty -#{qtyDelta}, available_qty available_qty #{qtyDelta}// where product_id#{productId}inventoryMapper.release(productId, qtyDelta);// 更新库存变更明细为已取消 freezeRecordMapper.updateStatus(txId,CANCELLED);}TCC将所有资源操作变成“预留”而非直接扣减避免了数据库行锁长时间持有非常适合秒杀、支付等并发高、资源竞争激烈的场景。2. SagaSaga一词的本意是长篇故事在分布式事务场景下是指一连串的事务。最佳场景出行预订机票酒店门票比如在线旅行平台用户一次性预订机票、酒店、门票三个服务相互独立、流程长并且每个预订都会调用外部第三方接口。因为无法控制外部服务的实现只能通过Saga模式来实现调外部接口、长流程的正向事务、补偿事务。原理与实现机制Saga模式的核心思想是将一个长事务拆分为多个有序的本地事务每个本地事务执行后直接提交不锁定资源。如果后续步骤失败则通过逆序调用之前的补偿事务来回滚。Saga一般采用编排式Orchestration存在一个中心协调者Orchestrator它负责调用每个步骤的本地事务并在失败时按逆序调用补偿。逻辑集中易于监控。协调者需要持久化Saga状态机以便在自身宕机后能够恢复并继续执行或补偿。关键实现点每个正向事务都必须有一个对应的补偿事务且补偿必须是幂等的因为可能存在重试缺少隔离性Saga不锁资源中间状态可被读取到因此业务设计上需要允许暂时的脏读例如预订酒店后变为“酒店预订成功”但因为机票出票失败后回滚为“酒店预订已取消”补偿失败时需要告警并人工介入因为回滚本身也可能失败举例出行预订的正向事务与补偿// 正向事务调用第三方接口预订酒店 public Booking bookHotel(HotelReq req){String bookingIdhotelApi.reserve(req);returnbookingRepo.save(new Booking(bookingId,RESERVED));}// 补偿事务取消预订 public void cancelHotel(Booking booking){hotelApi.cancel(booking.getBookingId());bookingRepo.updateStatus(booking.getId(),CANCELLED);}Saga不占用数据库锁各服务完全独立非常适合调用外部服务、流程长的业务。代价是需要实现补偿逻辑且中间状态可见业务必须能容忍短暂的不一致。3. 可靠消息最佳场景非核心链路的联动处理场景确保一定通知到、但可能多发消息联动方需做好幂等。比如用户注册送积分场景在用户注册后需要发送欢迎邮件、初始化积分账户。这些动作可以异步完成允许秒级延迟但必须保证最终完成。原理与实现机制要实现可靠消息需要通过本地消息表模式即本地事务消息队列实现最终一致。核心思想是将业务操作和消息持久化放在同一个DB事务中然后通过后台任务将消息发送到MQ下游消费时保证幂等。关键流程消息生产方执行业务操作的同时向本地DB的outbox表插入一条“待发送”消息两者在同一数据库事务中提交。然后发消息到MQ最后更新状态为“已发送”这2步可能失败兜底定时任务定时查询outbox表中超过一定时间仍处于“待发送”状态的记录将消息发送到MQ成功后更新状态为“已发送”消息消费方监听消息执行本地事务如初始化积分通过业务唯一键保证幂等举例注册用户时写入本地消息表、发消息-- 用户服务本地事务 BEGIN;INSERT INTO users(user_id, email,...)VALUES(...);INSERT INTO outbox(message_id, topic, payload, status)VALUES(msg_reg_123,USER_REGISTERED,{userId:...},PENDING);COMMIT;4. 两阶段提交2PC2 Phase Commit最佳场景内部低并发强一致转账比如公司内部财务系统A账户向B账户转账两个账户分别在不同数据库。并发量极低但绝不允许出现金额不一致。原理与实现机制2PC需要一个全局事务协调者Coordinator通常由事务管理器如JTA实现充当。参与者是各个资源管理器RM如数据库。协调者负责调度整个事务流程发送指令收集投票结果。参与者实际持有资源的节点执行预提交和最终提交/回滚。流程细节阶段一准备/投票协调者向所有参与者发送事务内容询问是否可以提交。参与者各自锁定资源如数据库行锁执行事务操作并写入undo/redo日志但不提交然后返回Yes或No阶段二提交/回滚若所有参与者返回Yes协调者发送提交指令若任一返回No或超时则发送回滚指令。参与者根据指令完成提交或回滚并释放锁。存在“协调者单点故障”问题若阶段二指令未能送达所有参与者部分参与者可能处于不确定状态需要人工介入或日志恢复举例公司内部跨系统转账通常基于XA协议实现如Java JTA。开发无需实现Try/Confirm但两个数据库资源在阶段一就被锁定其他事务无法操作A和B的余额直到协调者发出最终指令。内部管理系统的转账逻辑简单并发低这种强一致和阻塞是可以接受的。5. AT模式SeataAT模式是Seata引入的一种非侵入式的分布式事务解决方案Seata在内部做了对DB操作的代理层会生成业务层的undo log相当于在业务层实现了2PC。最佳场景无侵入改造老系统比如一个老旧的单体应用刚拆分为订单服务和库存服务数据库也一分为二。业务代码不想大改又需要保证“下单必须扣库存”的事务一致性。原理与实现机制一阶段在同一个本地事务中提交业务数据和undo log需先加全局锁。undo log是业务回滚日志在更新前查出前镜像然后做更新操作最后根据前镜像的主键id查出后镜像前镜像、后镜像的内容就是undo log的组成部分二阶段提交成功后异步删除对应的undo log。回滚通过一阶段的undo log进行反向补偿举例库存更新sql是update inventory set qty qty - 1 WHERE product_id 1001Seata自动记录前镜像qty100、后镜像qty99。回滚时生成sqlupdate inventory set qty 100 WHERE product_id 1001。业务代码完全不用加 Try/Confirm/Cancel只需引入Seata的代理数据源即可。AT模式适合对业务侵入性要求极低、SQL操作相对简单的改造场景。但它依赖关系型数据库默认读未提交需注意脏读问题复杂查询多或性能要求极高的场景要谨慎。方案选项总结方案一致性性能代码侵入性最适合的场景TCC强一致/最终一致高高资源争夺型高并发下单扣库存、余额Saga最终一致高中长流程、调用外部服务旅行出游预订可靠消息最终一致高中异步解耦的非核心流程注册送积分2PC强一致低低内部低并发强一致转账AT模式弱一致/读未提交中低无侵入改造老系统没有一套方案能覆盖所有分布式事务场景做方案选型时重点关注数据不一致的窗口能容忍多久并发量有多大是否能实现补偿逻辑再结合上述表格就能找到最合适的分布式事务方案通过Apache Seata来实现TCC、Saga、AT模式通过本地消息表来实现可靠消息。

相关新闻

2026/9/3 8:12:35

Herro晨间定制流程:用自动化聚合信息,打造个人数字管家

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/3 8:07:35

Java EE 进阶:2.1 HTML

目录 1.HTML 1.1 HTML基础 1.1.1 什么是HTML 1.1.2 认识 HTML 标签 1.1.3 HTML 文件基本结构 1.1.4 标签层次结构 1.2 HTML 快速入门 1.2.1 开发工具 1.2.2 快速开发 1.3 HTML 常见标签 1.3.1 标题标签 h1-h6 1.3.2 段落标签:p 1.3.3 换行标签&#xf…

2026/9/3 8:27:37

Linux:信号量,生产者消费者模型和日志实现

目录 1.什么是信号量 2.基于环形队列的生产者消费者模型 3.日志的实现 1.什么是信号量 信号量的本质是一把计数器,描述资源的数量。申请信号量的本质是用来预定资源的,比如买电影票。因为信号量也是共享资源,同时只能一个线程访问&#…

2026/9/3 8:27:37

WPF附加属性进阶:从布局标记到行为注入的扩展机制详解

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/3 8:27:37

游戏MOD本地化部署指南:从环境准备到功能验证全流程

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/3 8:27:37

dialog封装

通用封装弹窗 / 自定义组件模板&#xff0c;可套用 // 子组件 <template><el-dialog v-model"innerFlag" title"弹窗">// 业务内容 <template #footer><span class""><el-button click"state.peopleDialogShow …

2026/9/3 8:27:37

5款免费macOS系统优化工具:让你的Mac重获新生

5款免费macOS系统优化工具&#xff1a;让你的Mac重获新生 你是否发现自己的Mac变得越来越慢&#xff1f;存储空间总是不够用&#xff1f;别担心&#xff0c;这几乎是每个Mac用户都会遇到的问题。随着使用时间的增长&#xff0c;系统会积累大量缓存文件、日志和临时数据&#x…

2026/9/3 8:22:36

高通CamX相机框架源码解析:从架构设计到开发调试实战

简介&#xff1a;本资源为高通Camera Camx架构官方级源码仓库完整镜像&#xff0c;面向Android系统工程师、相机算法开发者及嵌入式图像处理研究人员&#xff0c;用于深度理解骁龙平台相机HAL层设计、图像流水线调度与硬件协同机制。压缩包含763个文件&#xff0c;主体为358个头…

2026/9/1 16:02:17

vSound小提琴数字处理器实操指南:从接线到演出的完整配置

电小提琴或者原声小提琴插电演出&#xff0c;第一个绕不开的坎就是声音难听。原声琴的共鸣和空气感一旦进了拾音器&#xff0c;出来的往往是一坨干瘪、发尖、带着奇怪塑料味的信号。我当初第一次把琴接上乐队调音台&#xff0c;直接被主唱吐槽"你这声音像在锯钢丝"。…

2026/9/2 9:00:32

传感器接口IC如何攻克生物化学传感的微弱信号难题?

1. 从电极到比特流&#xff1a;为什么生物化学传感必须依赖专用接口IC 做生物化学传感的人都有过类似的经历&#xff1a;明明传感器本身性能很好&#xff0c;信号输出却一塌糊涂——噪声大、漂移明显、重复性差&#xff0c;怎么调都达不到预期。很多时候问题并不在传感器&#…

2026/9/2 8:41:06

STM32F411CEU6多通道ADC采集:扫描模式+DMA实现详解

1. 多通道 ADC 的用武之地把“Multichannel ADC”和“STM32F411CEU6”这两个关键字放在一起&#xff0c;其实就是嵌入式开发里最常遇到的一类需求&#xff1a;用一块不算贵的 MCU&#xff0c;同时采集多路模拟信号。STM32F411CEU6 是 48 引脚的 Cortex-M4F 主控&#xff0c;主频…

2026/9/3 0:02:06

零基础装 OpenClaw 小龙虾 AI:Windows 一键部署教程与避坑要点

Windows 部署 OpenClaw 完整教程&#xff5c;本地 AI 智能体 5 分钟落地&#xff0c;环境配置一次搞定 版本说明&#xff1a;Windows 3.1.0 / Mac 2.7.9 写在前面 近两年开源 AI 领域有一款被称作「数字员工」的工具持续走热&#xff0c;它就是 OpenClaw&#xff0c;圈内人更习…

2026/9/3 0:02:06

Hermes Agent 本地部署新方案:Windows 整合包减少依赖报错

Windows 本地部署 Hermes 太麻烦&#xff1f;这版一键包 5 分钟快速跑通 很多人想体验 Hermes Agent&#xff0c;但真正开始部署时&#xff0c;往往会卡在环境配置这一步。 需要安装各类依赖、调试运行环境、处理路径问题&#xff0c;还容易遇到命令行报错、系统拦截、文件缺…

2026/9/3 0:02:06

实测 OpenClaw 一键包,5 分钟完成本地自动化环境搭建

OpenClaw 本地 AI 自动化工具部署指南&#xff5c;使用一键包规避环境配置难题 痛点&#xff1a;部署 AI 自动化工具常常要处理 Python、Node.js 各类依赖&#xff0c;版本冲突、环境配置耗费大量时间&#xff0c;OpenClaw 提供一键安装包&#xff0c;降低部署门槛。 适配系统&…

2026/9/2 1:15:22

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行&#xff0c;Type-C接口算是典型的“看着简单&#xff0c;做起来全坑”的东西。光引脚就24个&#xff0c;高低速信号、电源、控制线全部塞在一个小小的连接器里&#xff0c;如果PCB布局不做规划&#xff0c;打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/2 1:15:22

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/2 1:15:20

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…