微服务分布式事务实战:从数据一致性到故障恢复全方案

某电商平台在 "会员积分兑换商品" 业务中出现严重数据不一致问题:用户积分成功扣减(积分服务),但商品库存未同步减少(库存服务),导致用户 "花了积分却没拿到商品",客服投诉量激增。事后排查发现,由于库存服务数据库临时宕机,积分扣减后库存更新失败,但积分服务已提交本地事务无法回滚,最终造成跨服务数据不一致。这正是微服务架构下分布式事务的典型痛点:本地事务独立提交 / 回滚、跨服务数据一致性难保障、故障后恢复机制缺失。本文将以 Seata、Saga 等框架为核心,结合实际业务场景,详解 "AT、TCC、Saga、TXC" 四种分布式事务模式的实现方案、适用场景与故障处理策略,帮助团队在性能与一致性之间找到最佳平衡。

一、分布式事务的核心痛点与一致性需求

1. 微服务架构下的事务挑战

在单体架构中,所有业务操作都在同一数据库内,可通过 ACID 事务(原子性、一致性、隔离性、持久性)保障数据一致性;但微服务拆分后,业务操作需跨多个服务(如 "下单" 涉及订单、库存、支付服务)和多个数据库,传统本地事务机制完全失效,主要痛点体现在:

  • 事务边界跨服务:一个业务流程(如 "积分兑换商品")涉及积分服务(扣减积分)、库存服务(减少库存)、订单服务(创建兑换订单),每个服务有独立数据库,本地事务仅能控制自身数据,无法联动其他服务;
  • 网络不可靠性:服务间调用依赖网络传输,可能出现 "调用超时""网络中断" 等问题(如积分服务调用库存服务时网络卡顿),导致部分服务执行成功、部分失败,数据不一致;
  • 服务故障不可预测:服务或数据库可能突发宕机(如库存服务数据库崩溃),若此时已有部分服务完成本地事务提交(如积分已扣减),故障恢复后无法回滚已提交的操作,导致数据永久不一致;
  • 性能与一致性冲突:若为追求强一致性而采用 "全链路锁定" 机制(如分布式锁),会导致服务并发能力急剧下降(如 QPS 从 1000 降至 100),无法满足高并发场景(如秒杀、大促)需求;
  • 故障恢复复杂:当事务执行失败时(如部分服务操作失败),需手动排查每个服务的执行状态(如积分是否扣减、库存是否更新),再通过接口或脚本手动恢复数据,耗时且易出错(如漏恢复、重复恢复)。

支付退款业务的事务困境:

用户发起退款申请后,退款流程需调用支付服务(发起退款)、订单服务(更新订单状态为 "已退款")、财务服务(记录退款流水)。某次支付服务退款成功,但订单服务因 JVM 内存溢出未更新状态,导致订单显示 "未退款" 而用户已收到退款,财务流水也未记录,后续对账时发现大量账实不符。

2. 分布式事务的一致性需求分级

不同业务场景对数据一致性的要求不同,盲目追求强一致性会牺牲性能,需根据业务重要性分级选择:

  • 强一致性(Linearizability):事务执行后,所有服务的数据立即保持一致,且所有后续访问都能看到事务执行的结果(如银行转账:A 账户扣款成功,B 账户必须同时到账);适用场景:金融转账、支付对账等核心资金场景;
  • 最终一致性(Eventual Consistency):事务执行后,短期内可能存在数据不一致,但通过补偿机制(如重试、回滚),最终所有服务的数据会达到一致状态(如电商订单:下单后 10 秒内,库存服务与订单服务数据可能不同步,但 1 分钟后会一致);适用场景:商品库存扣减、积分兑换、物流状态更新等非实时资金场景;
  • 弱一致性(Weak Consistency):事务执行后,数据一致性没有时间保证,甚至可能永远不一致(需人工干预);适用场景:非核心数据统计(如商品浏览量计数,偶尔少算 1-2 次不影响业务);

3. 主流分布式事务模式对比

不同分布式事务模式在一致性、性能、适用场景上差异显著,需根据业务需求选择:

|-------------------------------------|---------------------------------------------------------|-------------------|-------------|-----------------------------------|-------------------------|
| 事务模式 | 核心原理 | 一致性级别 | 性能表现 | 适用场景 | 典型框架 / 工具 |
| AT 模式(Automatic Transaction) | 基于两阶段提交(2PC),自动生成 undo/redo 日志,支持自动回滚与提交 | 最终一致性(接近强一致) | 中(有锁开销) | 无侵入式开发、业务逻辑简单(如单表增删改)、Java 微服务 | Seata |
| TCC 模式(Try-Confirm-Cancel) | 拆分为 Try(资源检查与预留)、Confirm(确认执行)、Cancel(取消释放)三阶段,手动实现业务逻辑 | 强一致性 / 最终一致性(可控制) | 高(无锁,纯业务逻辑) | 业务逻辑复杂(如多表操作)、高并发场景(如秒杀) | Seata、Hmily |
| Saga 模式 | 基于状态机,将长事务拆分为多个短本地事务,每个事务执行后记录状态,失败时调用补偿事务回滚 | 最终一致性 | 高(无锁,异步执行) | 长事务场景(如订单履约:下单→支付→发货→确认收货)、跨语言微服务 | Seata、Axon、Apache Camel |
| TXC 模式(Transaction Cross-chain) | 基于本地消息表,事务执行时先写消息表,再调用下游服务,失败时通过消息表重试 | 最终一致性 | 高(异步,无锁) | 非实时依赖场景(如退款通知、日志同步)、高可用需求场景 | RocketMQ 事务消息、本地消息表 |
| S2F 模式(Seata for Fescar) | 基于 AT 模式优化,支持多数据源、微服务跨库事务,简化配置 | 最终一致性 | 中高 | 多数据库、复杂服务依赖的 Java 微服务 | Seata 1.6+ |

二、Seata AT 模式实战:无侵入式分布式事务

Seata 是 Alibaba 开源的分布式事务框架,AT 模式是其核心模式,支持无侵入式开发(无需修改业务代码),通过 "两阶段提交 + undo/redo 日志" 实现最终一致性,适合大多数 Java 微服务场景。

1. AT 模式核心原理

Seata AT 模式将分布式事务拆分为 "全局事务" 和 "分支事务":

  • 全局事务:整个分布式事务的入口(如 "下单" 事务),由事务协调器(TC)管理;
  • 分支事务:每个服务的本地事务(如订单服务的 "创建订单"、库存服务的 "扣减库存"),由资源管理器(RM)管理;
  • 核心流程(两阶段)
    1. 第一阶段(Prepare)
      • 全局事务发起者(如订单服务)向 TC 申请全局事务 ID(XID);
      • 订单服务执行本地事务(创建订单),RM 自动生成 undo 日志(记录事务前数据状态,用于回滚)和 redo 日志(记录事务后数据状态,用于提交),本地事务暂不提交;
      • 订单服务调用库存服务,传递 XID,库存服务执行本地事务(扣减库存),同样生成 undo/redo 日志,暂不提交;
      • 所有分支事务执行完成后,向 TC 汇报执行状态(成功 / 失败);
    1. 第二阶段(Commit/Rollback)
      • 若所有分支事务均成功,TC 通知所有 RM 提交本地事务,RM 删除 undo 日志;
      • 若任一分支事务失败,TC 通知所有 RM 回滚本地事务,RM 通过 undo 日志恢复数据到事务前状态;

2. 环境准备与部署

2.1 组件版本选择
  • Seata Server:1.8.0(稳定版);
  • 微服务框架:Spring Cloud Alibaba 2022.0.0.0
  • 数据库:MySQL 8.0(支持 InnoDB 引擎,需开启事务日志);
  • 注册中心:Nacos 2.2.3(Seata Server 与微服务需注册到同一 Nacos);
2.2 部署 Seata Server(事务协调器 TC)
  1. 下载 Seata Server :从 Seata 官网(https://seata.io/zh-cn/blog/download.html)下载 1.8.0 版本压缩包,解压后进入conf目录;
  1. 配置 Seata Server
    • 修改application.yml,配置 Nacos 注册中心、数据库存储(用于存储全局事务状态):
复制代码

server:

port: 7091 # Seata Server端口

spring:

application:

name: seata-server

logging:

config: classpath:logback-spring.xml

file:

path: ${user.home}/logs/seata

seata:

config:

type: nacos # 配置中心类型

nacos:

server-addr: 192.168.1.100:8848 # Nacos地址

group: SEATA_GROUP # 配置分组

namespace: # Nacos命名空间(可选)

username: nacos

password: nacos

registry:

type: nacos # 注册中心类型

nacos:

application: seata-server

server-addr: 192.168.1.100:8848

group: SEATA_GROUP

namespace:

username: nacos

password: nacos

store:

mode: db # 存储模式(db/file/redis,推荐db)

db:

datasource: druid

db-type: mysql

driver-class-name: com.mysql.cj.jdbc.Driver

url: jdbc:mysql://192.168.1.101:3306/seata?useUnicode=true&rewriteBatchedStatements=true

user: root

password: 123456

min-conn: 5

max-conn: 100

global-table: global_table # 全局事务表

branch-table: branch_table # 分支事务表

lock-table: lock_table # 全局锁表

distributed-lock-table: distributed_lock

query-limit: 100

  1. 初始化 Seata 数据库:在 MySQL 中创建seata数据库,执行conf/db/schema-mysql.sql脚本,创建global_table、branch_table、lock_table等核心表;
  1. 启动 Seata Server :执行bin/seata-server.sh(Linux)或bin/seata-server.bat(Windows),启动后在 Nacos 控制台可看到seata-server服务已注册;
2.3 微服务集成 Seata AT 模式

以 "订单服务" 和 "库存服务" 为例,集成 Seata 实现 "下单扣库存" 的分布式事务:

2.3.1 数据库准备(资源管理器 RM)

在订单服务数据库(order_db)和库存服务数据库(inventory_db)中,分别创建 Seata 所需的undo_log表(用于存储 undo 日志),执行以下 SQL:

复制代码

CREATE TABLE `undo_log` (

```id` bigint NOT NULL AUTO_INCREMENT,``

```branch_id` bigint NOT NULL COMMENT '分支事务ID',``

```xid` varchar(100) NOT NULL COMMENT '全局事务ID',``

```context` varchar(128) NOT NULL COMMENT '上下文信息',``

```rollback_info` longblob NOT NULL COMMENT '回滚信息',``

```log_status` int NOT NULL COMMENT '日志状态:0-未提交,1-已提交,2-已回滚',``

```log_created` datetime NOT NULL COMMENT '创建时间',``

```log_modified` datetime NOT NULL COMMENT '修改时间',``

PRIMARY KEY (`id`),

UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Seata undo日志表';

2.3.2 微服务依赖配置(Spring Cloud Alibaba)

在订单服务和库存服务的pom.xml中添加 Seata 依赖:

复制代码

Seata Spring Cloud Starter -->

.cloud arter-alibaba-seata</artifactId>

>2022.0.0.0</version>

clusions>

排除旧版本Seata,引入指定版本 -->

</groupId>

<artifactId>seata-spring-boot-starter

</dependency>

<dependency>

>io.seata>

seata-spring-boot-starter .0</dependency>

2.3.3 微服务配置文件

在application.yml中配置 Seata 相关参数(订单服务与库存服务配置类似):

复制代码

spring:

application:

name: order-service # 服务名(需与Seata配置中的service.vgroup-mapping对应)

cloud:

alibaba:

seata:

tx-service-group: order-service-group # 事务组名

datasource:

driver-class-name: com.mysql.cj.jdbc.Driver

url: jdbc:mysql://192.168.1.101:3306/order_db?useUnicode=true&characterEncoding=utf8

username: root

password: 123456

# Seata配置

seata:

enabled: true

application-id: ${spring.application.name}

tx-service-group: ${spring.cloud.alibaba.seata.tx-service-group}

service:

vgroup-mapping:

order-service-group: default # 事务组与Seata Server集群映射(default为Seata Server默认集群名)

grouplist:

default: 192.168.1.102:8091 # Seata Server地址(注意:8091为Seata Server的RPC端口,非7091)

registry:

type: nacos

nacos:

server-addr: 192.168.1.100:8848

group: SEATA_GROUP

namespace:

username: nacos

password: nacos

2.3.4 业务代码实现(无侵入式)

Seata AT 模式无需修改业务代码,仅需在全局事务发起者(订单服务)的方法上添加@GlobalTransactional注解,标记为全局事务入口:

  1. 订单服务(全局事务发起者)
复制代码

@Service

public class OrderServiceImpl implements OrderService {

@Autowired

private OrderMapper orderMapper; // 订单DAO

@Autowired

private InventoryFeignClient inventoryFeignClient; // 库存服务Feign客户端(调用库存扣减接口)

/**

* 创建订单(全局事务入口)

* @GlobalTransactional:标记为全局事务,rollbackFor:指定异常时回滚

*/

@Override

@GlobalTransactional(rollbackFor = Exception.class)

public String createOrder(OrderDTO orderDTO) {

// 1. 本地事务:创建订单

Order order = new Order();

order.setOrderNo(UUID.randomUUID().toString());

order.setUserId(orderDTO.getUserId());

order.setProductId(orderDTO.getProductId());

order.setQuantity(orderDTO.getQuantity());

order.setStatus(0); // 0-待支付

orderMapper.insert(order);

System.out.println("订单创建成功,订单号:" + order.getOrderNo());

// 2. 远程调用:库存服务扣减库存

InventoryDTO inventoryDTO = new InventoryDTO();

inventoryDTO.setProductId(orderDTO.getProductId());

inventoryDTO.setReduceQuantity(orderDTO.getQuantity());

String inventoryResult = inventoryFeignClient.reduceInventory(inventoryDTO);

if (!"success".equals(inventoryResult)) {

// 若库存扣减失败,抛出异常,触发全局回滚

throw new RuntimeException("库存扣减失败,订单创建回滚");

}

return "订单创建成功,订单号:" + order.getOrderNo();

}

}

  1. 库存服务(分支事务)

库存服务无需添加 Seata 注解,仅需实现库存扣减的本地事务逻辑:

复制代码

@Service

public class InventoryServiceImpl implements InventoryService {

@Autowired

private InventoryMapper inventoryMapper; // 库存DAO

/**

* 扣减库存(本地事务,Seata自动管理分支事务)

*/

@Override

public String reduceInventory(InventoryDTO inventoryDTO) {

// 1. 检查库存是否充足

Inventory inventory = inventoryMapper.selectByProductId(inventoryDTO.getProductId());

if (inventory == null || inventory.getStock() < inventoryDTO.getReduceQuantity()) {

return "fail:库存不足";

}

// 2. 扣减库存(本地事务)

int rows = inventoryMapper.reduceStock(

inventoryDTO.getProductId(),

inventoryDTO.getReduceQuantity()

);

if (rows > 0) {

System.out.println("库存扣减成功,商品ID:" + inventoryDTO.getProductId() + ",扣减数量:" + inventoryDTO.getReduceQuantity());

return "success";

} else {

return "fail:库存扣减失败";

}

}

}

3. 功能验证与故障测试

3.1 正常场景验证(所有分支事务成功)
  1. 调用订单服务的createOrder接口,传入参数(userId=123,productId=456,quantity=2);
  1. 观察日志:订单服务创建订单成功,库存服务扣减库存成功;
  1. 检查数据库:
    • order_db.order表新增订单记录;
    • inventory_db.inventory表中商品 ID=456 的库存减少 2;
    • 两个数据库的undo_log表无残留记录(已提交,自动删除);
    • Seata 数据库的global_table中全局事务状态为 "已提交"(status=1);
3.2 异常场景验证(分支事务失败,触发回滚)
  1. 模拟库存服务异常:在库存服务的reduceInventory方法中手动抛出异常(如throw new RuntimeException("库存服务临时故障"));
  1. 调用订单服务的createOrder接口;
  1. 观察日志:订单服务创建订单成功,但调用库存服务时抛出异常,触发@GlobalTransactional回滚;
  1. 检查数据库:
    • order_db.order表无新增订单记录(订单服务本地事务已回滚);
    • inventory_db.inventory表库存无变化(库存服务本地事务未提交,或已回滚);
    • 两个数据库的undo_log表记录已删除(回滚完成后清理);
    • Seata 数据库的global_table中全局事务状态为 "已回滚"(status=2);
3.3 故障恢复验证(Seata Server 宕机后恢复)
  1. 启动订单服务和库存服务,调用createOrder接口,在库存服务执行过程中强制关闭 Seata Server;
  1. 观察日志:订单服务和库存服务的分支事务处于 "未提交" 状态;
  1. 重启 Seata Server,Seata 会自动读取global_table和branch_table中的未完成事务,重新发起第二阶段(Commit/Rollback);
  1. 检查结果:若宕机前所有分支事务已成功,重启后 Seata 会通知所有 RM 提交事务;若存在失败分支,会通知回滚,确保数据一致性;

三、TCC 模式实战:高并发场景下的强一致性保障

Seata AT 模式虽无侵入,但依赖数据库 undo 日志和全局锁,在高并发场景(如秒杀)下会因锁竞争导致性能下降。TCC 模式通过 "Try-Confirm-Cancel" 三阶段手动拆分业务逻辑,无锁竞争,性能更高,适合高并发、业务逻辑复杂的场景(如秒杀库存扣减、多表关联操作)。

1. TCC 模式核心原理

TCC 模式将分布式事务拆分为三个独立的业务操作,由业务代码手动实现,不依赖数据库事务:

  • Try 阶段:资源检查与预留(如检查库存是否充足,并冻结部分库存,防止其他事务占用);
  • Confirm 阶段:确认执行(如将冻结的库存正式扣减,此阶段操作必须成功,无回滚机制);
  • Cancel 阶段:取消释放(若 Try 阶段预留资源后其他事务失败,释放预留的资源,如解冻冻结的库存);
  • 核心特点
    1. 无锁竞争:Try 阶段仅预留资源,不修改最终数据,避免全局锁;
    1. 强一致性可控:若所有 Try 成功,Confirm 执行后数据立即一致;若任一 Try 失败,Cancel 执行后资源释放,数据恢复初始状态;
    1. 业务侵入性强:需手动实现 Try/Confirm/Cancel 三个方法,业务代码复杂度高;

2. Seata TCC 模式实战(秒杀库存扣减)

以 "秒杀商品库存扣减" 为例,用 Seata TCC 模式实现高并发下的库存一致性管理:

2.1 数据库设计(库存表拆分)

为支持 TCC 的 "资源预留",将库存表拆分为 "基础库存表" 和 "冻结库存表":

  1. 基础库存表(inventory_base):存储商品总库存;
复制代码

CREATE TABLE `inventory_base` (

```id` bigint NOT NULL AUTO_INCREMENT,``

```product_id` bigint NOT NULL COMMENT '商品ID',``

```total_stock` int NOT NULL COMMENT '总库存',``

```available_stock` int NOT NULL COMMENT '可用库存(total_stock - frozen_stock)',``

```create_time` datetime NOT NULL,``

```update_time` datetime NOT NULL,``

PRIMARY KEY (`id`),

UNIQUE KEY `uk_product_id` (`product_id`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='基础库存表';

  1. 冻结库存表(inventory_frozen):存储 Try 阶段冻结的库存,关联全局事务 ID;
复制代码

CREATE TABLE `inventory_frozen` (

```id` bigint NOT NULL AUTO_INCREMENT,``

```product_id` bigint NOT NULL COMMENT '商品ID',``

```frozen_quantity` int NOT NULL COMMENT '冻结数量',``

```xid` varchar(100) NOT NULL COMMENT '全局事务ID',``

```branch_id` bigint NOT NULL COMMENT '分支事务ID',``

```status` tinyint NOT NULL COMMENT '状态:0-冻结中,1-已确认(扣减),2-已取消(解冻)',``

```create_time` datetime NOT NULL,``

```update_time` datetime NOT NULL,``

PRIMARY KEY (`id`),

UNIQUE KEY `uk_xid_branch_id` (`xid`,`branch_id`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='冻结库存表';

2.2 业务代码实现(TCC 三阶段)

Seata TCC 模式需通过@LocalTCC注解标记 TCC 接口,并手动实现 Try/Confirm/Cancel 方法:

2.2.1 TCC 接口定义(InventoryTccService)
复制代码

/**

* 库存TCC接口

* @LocalTCC:标记为TCC接口,Seata自动识别Try/Confirm/Cancel方法

*/

@LocalTCC

public interface InventoryTccService {

/**

* Try阶段:冻结库存(资源检查与预留)

* @TwoPhaseBusinessAction:指定Confirm和Cancel方法名,businessKey:业务标识(商品ID)

*/

@TwoPhaseBusinessAction(

name = "reduceInventoryTcc",

commitMethod = "confirm",

rollbackMethod = "cancel",

businessKey = "productId"

)

String tryFreezeInventory(

@BusinessKeyParam("productId") Long productId,

Integer quantity,

@Param("xid") String xid,

@Param("branchId") Long branchId

);

/**

* Confirm阶段:确认扣减库存(Try成功后执行)

*/

String confirm(

@Param("xid") String xid,

@Param("branchId") Long branchId,

@Param("productId") Long productId,

@Param("quantity") Integer quantity

);

/**

* Cancel阶段:取消冻结(Try失败或其他事务失败后执行)

*/

String cancel(

@Param("xid") String xid,

@Param("branchId") Long branchId,

@Param("productId") Long productId,

@Param("quantity") Integer quantity

);

}

2.2.2 TCC 接口实现(InventoryTccServiceImpl)
复制代码

@Service

public class InventoryTccServiceImpl implements InventoryTccService {

@Autowired

private InventoryBaseMapper baseMapper; // 基础库存DAO

@Autowired

private InventoryFrozenMapper frozenMapper; // 冻结库存DAO

/**

* Try阶段:检查可用库存,冻结对应数量

*/

@Override

public String tryFreezeInventory(Long productId, Integer quantity, String xid, Long branchId) {

// 1. 检查可用库存是否充足(使用悲观锁防止并发问题)

InventoryBase base = baseMapper.selectByProductIdForUpdate(productId);

if (base == null || base.getAvailableStock()

return "fail:可用库存不足";

}

// 2. 冻结库存:减少可用库存,新增冻结记录

// 2.1 更新基础库存表的可用库存

int updateRows = baseMapper.reduceAvailableStock(productId, quantity);

if (updateRows return "fail:可用库存更新失败";

}

// 2.2 新增冻结库存记录

InventoryFrozen frozen = new InventoryFrozen();

frozen.setProductId(productId);

frozen.setFrozenQuantity(quantity);

frozen.setXid(xid);

frozen.setBranchId(branchId);

frozen.setStatus(0); // 0-冻结中

frozen.setCreateTime(new Date());

frozen.setUpdateTime(new Date());

frozenMapper.insert(frozen);

System.out.println("Try阶段:冻结库存成功,商品ID:" + productId + ",冻结数量:" + quantity + ",XID:" + xid);

return "success";

}

/**

* Confirm阶段:将冻结库存转为正式扣减(删除冻结记录,无需修改总库存,Try阶段已减少可用库存)

*/

@Override

public String confirm(String xid, Long branchId, Long productId, Integer quantity) {

// 1. 查询冻结记录

InventoryFrozen frozen = frozenMapper.selectByXidAndBranchId(xid, branchId);

if (frozen == null || frozen.getStatus() != 0) {

return "fail:冻结记录不存在或已处理";

}

// 2. 更新冻结记录状态为"已确认"(或直接删除,视业务需求)

frozen.setStatus(1);

frozen.setUpdateTime(new Date());

int updateRows = frozenMapper.updateById(frozen);

if (updateRows > 0) {

System.out.println("Confirm阶段:库存扣减成功,商品ID:" + productId + ",XID:" + xid);

return "success";

} else {

return "fail:冻结记录更新失败";

}

}

/**

* Cancel阶段:解冻冻结的库存(恢复可用库存,更新冻结记录状态)

*/

@Override

public String cancel(String xid, Long branchId, Long productId, Integer quantity) {

// 1. 查询冻结记录

InventoryFrozen frozen = frozenMapper.selectByXidAndBranchId(xid, branchId);

if (frozen == null || frozen.getStatus() != 0) {

return "fail:冻结记录不存在或已处理";

}

// 2. 解冻库存:恢复基础库存表的可用库存

int updateRows = baseMapper.increaseAvailableStock(productId, quantity);

if (updateRows

return "fail:可用库存恢复失败";

}

// 3. 更新冻结记录状态为"已取消"

frozen.setStatus(2);

frozen.setUpdateTime(new Date());

frozenMapper.updateById(frozen);

System.out.println("Cancel阶段:库存解冻成功,商品ID:" + productId + ",解冻数量:" + quantity + ",XID:" + xid);

return "success";

}

}

2.2.3 全局事务发起者(秒杀服务)

秒杀服务作为全局事务发起者,调用库存服务的 TCC 接口:

复制代码

@Service

public class SeckillServiceImpl implements SeckillService {

@Autowired

private SeckillMapper seckillMapper; // 秒杀活动DAO

@Autowired

private InventoryTccService inventoryTccService; // 库存TCC接口(本地调用,若跨服务需用Feign+Seata TCC)

@Autowired

private GlobalTransactionContext globalTransactionContext; // Seata全局事务上下文

/**

* 秒杀下单(全局事务入口)

*/

@Override

@GlobalTransactional(rollbackFor = Exception.class)

public String seckillOrder(Long seckillId, Long userId, Integer quantity) {

// 1. 获取全局事务信息(XID、分支ID)

GlobalTransaction tx = globalTransactionContext.getCurrent();

String xid = tx.getXid();

Long branchId = tx.getBranchId();

// 2. 检查秒杀活动是否有效

Seckill seckill = seckillMapper.selectById(seckillId);

if (seckill == null || seckill.getStatus() != 1 || seckill.getEndTime().before(new Date())) {

throw new RuntimeException("秒杀活动已结束或无效");

}

// 3. 调用库存TCC的Try阶段:冻结库存

String tryResult = inventoryTccService.tryFreezeInventory(

seckill.getProductId(),

quantity,

xid,

branchId

);

if (!"success".equals(tryResult)) {

throw new RuntimeException("库存冻结失败,秒杀下单回滚:" + tryResult);

}

// 4. 本地事务:创建秒杀订单(省略订单创建逻辑,若失败会触发Cancel阶段)

// ...

return "秒杀下单成功,XID:" + xid;

}

}

3. TCC 模式性能优化与注意事项

3.1 性能优化点
  • 减少锁竞争:Try 阶段使用行锁(如select ... for update)而非表锁,降低并发阻塞;
  • 异步化处理:Confirm/Cancel 阶段可异步执行(如通过消息队列),减少同步等待时间;
  • 资源预分配:秒杀场景下,提前将库存加载到 Redis,Try 阶段先检查 Redis 库存,再同步到数据库,提升并发能力;
3.2 注意事项
  • 幂等性设计:Confirm/Cancel 阶段可能因网络重试执行多次,需确保方法幂等(如通过xid+branchId判断是否已处理);
  • 空回滚防护:若 Try 阶段未执行(如网络超时),Cancel 阶段可能空执行,需在 Cancel 方法中判断冻结记录是否存在;
  • 悬挂问题:若 Try 阶段执行超时,但实际已成功,后续 Cancel 阶段执行后,Confirm 阶段又执行,导致数据不一致;需通过冻结记录状态严格控制执行顺序(如 Confirm 仅处理 "冻结中" 状态,Cancel 后状态改为 "已取消",拒绝 Confirm);

四、Saga 模式实战:长事务与跨语言微服务场景

Saga 模式适合长事务场景(如订单履约流程:下单→支付→发货→确认收货,每个步骤间隔可能达数小时)和跨语言微服务(如 Java 订单服务调用 Python 物流服务),通过 "拆分短事务 + 补偿事务" 实现最终一致性,无锁且支持异步执行。

1. Saga 模式核心原理

Saga 模式将分布式长事务拆分为多个独立的短本地事务(Step),每个 Step 执行后记录事务状态,若某个 Step 失败,调用对应的补偿事务(Compensation)回滚前面的 Step:

  • 正向流程:Step1(下单)→ Step2(支付)→ Step3(发货)→ Step4(确认收货);
  • 补偿流程:若 Step3(发货)失败,触发补偿流程:Compensation3(取消发货)→ Compensation2(退款)→ Compensation1(取消订单);
  • 实现方式
    1. 编排式(Choreography):无中心协调器,每个服务通过事件通知触发下一个服务(如订单服务创建订单后发送 "订单创建事件",支付服务监听事件后执行支付);
    1. 协同式(Orchestration):有中心协调器(Saga Orchestrator),统一管理所有 Step 的执行与补偿(如订单履约协调器调用订单、支付、物流服务);

2. Seata Saga 模式实战(订单履约流程)

以 "订单履约流程" 为例,用 Seata Saga 模式实现协同式分布式事务,协调器统一管理 "创建订单→支付→发货→确认收货" 四个 Step:

2.1 环境准备
  • Seata Server:1.8.0(支持 Saga 模式);
  • 服务列表:订单服务(Java)、支付服务(Java)、物流服务(Python)、Saga 协调器(基于 Seata Saga Orchestrator);
  • 通信方式:HTTP(跨语言服务调用)、Dubbo(Java 服务间调用);
2.2 Saga 流程定义(JSON 格式)

通过 JSON 定义

相关推荐
子春一2 小时前
Flutter 2025 测试策略全景:从单元测试到混沌工程,构建坚不可摧的高质量应用
flutter·架构
timmy-uav3 小时前
BetaFlight代码解析(20)—屏幕显示(OSD)
架构·系统架构·无人机·飞控·betaflight
神算大模型APi--天枢6463 小时前
国产硬件架构算力平台:破解大模型本地化部署难题,标准化端口加速企业 AI 落地
大数据·前端·人工智能·架构·硬件架构
武子康3 小时前
Java-192 深入拆解 EVCache 内部原理:Memcached 架构、Slab 分配与 LRU 过期机制全解析
数据库·redis·缓存·架构·memcached·guava·evcache
ClouGence3 小时前
从 0 到 1 构建 TDSQL MySQL 实时同步链路
数据库·分布式·sql·mysql
哈哈哈笑什么3 小时前
完整Redis分布式锁技术方案(基于Redisson)
redis·分布式·spring cloud
切糕师学AI4 小时前
ARM 架构中的数据同步屏障(DSB)是什么?
arm开发·架构·数据同步·屏障
周杰伦_Jay4 小时前
【字节开源Golang框架Eino】技术详解:架构原理+实战落地+避坑指南(附代码)
架构·golang·开源
rolt4 小时前
[漫画]《软件方法》微服务的遮羞布
微服务·ddd·领域驱动设计