MySQL 分布式事务实现详解:从简单到复杂的方案对比
- MySQL 分布式事务 Java 实现详解:从简单到复杂方案
- 结合第三方组件实现分布式事务
MySQL 分布式事务 Java 实现详解:从简单到复杂方案
分布式事务指跨多个 MySQL 节点(或资源管理器)的事务,需保证“全部成功或全部回滚”。以下按 实现复杂度从简单到困难 排序,用 Java 语言 实现 5 种主流方案,涵盖核心原理、代码示例及关键注意事项。
一、方案 1:MySQL 原生 XA 事务(两阶段提交,强一致性)
复杂度:★☆☆☆☆(最简单,依赖 MySQL XA 协议)
原理:基于 XA 两阶段提交(2PC),Java 通过 javax.sql.XADataSource 接口协调多个 MySQL 实例,事务管理器(TM)由应用层或中间件担任。
Java 实现步骤
1. 依赖准备
- MySQL 驱动:
mysql-connector-java:8.0+ - 事务管理器:Java 自带
JTA(需容器支持,如 JBoss;或用轻量级 TM 如 Atomikos)
2. 核心代码(模拟跨两个 MySQL 节点)
import com.mysql.cj.jdbc.MysqlXADataSource;
import javax.sql.XAConnection;
import javax.sql.XADataSource;
import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class XATransactionDemo {
// 模拟两个 MySQL 节点(Node1: 3306,Node2: 3307)
static class NodeConfig {
String url;
String username;
String password;
XADataSource xaDataSource;
public NodeConfig(String url, String user, String pwd) {
this.url = url;
this.username = user;
this.password = pwd;
MysqlXADataSource ds = new MysqlXADataSource();
ds.setUrl(url);
ds.setUser(user);
ds.setPassword(pwd);
this.xaDataSource = ds;
}
}
public static void main(String[] args) throws Exception {
// 1. 配置两个 MySQL 节点
NodeConfig node1 = new NodeConfig(
"jdbc:mysql://localhost:3306/db_node1?useSSL=false",
"root", "123456"
);
NodeConfig node2 = new NodeConfig(
"jdbc:mysql://localhost:3307/db_node2?useSSL=false",
"root", "123456"
);
// 2. 生成全局事务 ID(XID)
byte[] globalId = "global_tx_001".getBytes();
int formatId = 1;
Xid xid = new CustomXid(globalId, formatId); // 自定义 Xid 实现
Connection conn1 = null, conn2 = null;
XAConnection xaConn1 = null, xaConn2 = null;
try {
// 3. 获取 XA 连接并执行 SQL
xaConn1 = node1.xaDataSource.getXAConnection();
conn1 = xaConn1.getConnection();
XAResource xaRes1 = xaConn1.getXAResource();
xaRes1.start(xid, XAResource.TMNOFLAGS); // 开启 XA 事务
executeSql(conn1, "INSERT INTO order (id, user_id, amount) VALUES (1, 1001, 99.9)");
xaRes1.end(xid, XAResource.TMSUCCESS); // 结束 XA 事务
xaConn2 = node2.xaDataSource.getXAConnection();
conn2 = xaConn2.getConnection();
XAResource xaRes2 = xaConn2.getXAResource();
xaRes2.start(xid, XAResource.TMNOFLAGS);
executeSql(conn2, "UPDATE stock SET count = count - 1 WHERE product_id = 2001");
xaRes2.end(xid, XAResource.TMSUCCESS);
// 4. 两阶段提交:准备阶段
int prepare1 = xaRes1.prepare(xid);
int prepare2 = xaRes2.prepare(xid);
if (prepare1 == XAResource.XA_OK && prepare2 == XAResource.XA_OK) {
// 5. 提交阶段
xaRes1.commit(xid, false);
xaRes2.commit(xid, false);
System.out.println("XA 事务提交成功");
} else {
// 回滚阶段
xaRes1.rollback(xid);
xaRes2.rollback(xid);
System.out.println("XA 事务回滚");
}
} catch (Exception e) {
// 异常回滚
if (xaConn1 != null) xaConn1.getXAResource().rollback(xid);
if (xaConn2 != null) xaConn2.getXAResource().rollback(xid);
e.printStackTrace();
} finally {
// 关闭连接
close(conn1, xaConn1);
close(conn2, xaConn2);
}
}
// 执行 SQL
private static void executeSql(Connection conn, String sql) throws SQLException {
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.executeUpdate();
}
}
// 关闭资源
private static void close(AutoCloseable... resources) {
for (AutoCloseable res : resources) {
if (res != null) try { res.close(); } catch (Exception e) {}
}
}
// 自定义 Xid 实现(简化版)
static class CustomXid implements Xid {
private byte[] globalTransactionId;
private int formatId;
public CustomXid(byte[] globalId, int formatId) {
this.globalTransactionId = globalId;
this.formatId = formatId;
}
@Override public int getFormatId() { return formatId; }
@Override public byte[] getGlobalTransactionId() { return globalTransactionId; }
@Override public byte[] getBranchQualifier() { return "branch_001".getBytes(); }
}
}
关键点
- Xid 唯一性:全局事务 ID 需唯一(可用 UUID 生成)。
- 异常处理:任一节点准备失败,需回滚所有节点。
- 依赖:需 MySQL 驱动支持 XA(
mysql-connector-java:8.0+)。
二、方案 2:应用层伪分布式事务(本地事务+补偿,最终一致性)
复杂度:★★☆☆☆(需手动记录日志+补偿逻辑)
原理:拆分跨库操作为本地事务,用“事务日志表”记录步骤状态,失败时按日志逆序补偿。
Java 实现步骤
1. 数据库表设计
-- 事务日志表(单库存储)
CREATE TABLE tx_log (
tx_id VARCHAR(64) PRIMARY KEY, -- 全局事务 ID
step INT, -- 步骤序号
action VARCHAR(32), -- 操作类型(insert/update)
biz_id VARCHAR(64), -- 业务 ID(如订单 ID)
status TINYINT, -- 0:待执行 1:成功 2:失败
create_time DATETIME DEFAULT CURRENT_TIMESTAMP
);
2. Java 代码(Spring Boot + MyBatis)
@Service
public class PseudoDistributedTxService {
@Autowired
private OrderMapper orderMapper; // 订单库 Mapper
@Autowired
private StockMapper stockMapper; // 库存库 Mapper
@Autowired
private TxLogMapper txLogMapper; // 事务日志 Mapper
// 执行分布式事务
@Transactional(rollbackFor = Exception.class) // 本地事务(仅日志库)
public void executeOrderWithStock(String txId) {
try {
// 步骤 1:订单库插入订单(本地事务)
orderMapper.insert(new Order(1L, 1001L, 99.9));
txLogMapper.insertLog(txId, 1, "insert_order", "1", 1); // 记录日志
// 步骤 2:库存库扣减库存(本地事务)
stockMapper.updateCount(2001L, -1);
txLogMapper.insertLog(txId, 2, "update_stock", "2001", 1); // 记录日志
} catch (Exception e) {
// 失败时触发补偿
compensate(txId);
throw new RuntimeException("事务失败,已补偿");
}
}
// 补偿逻辑(逆序执行)
private void compensate(String txId) {
List<TxLog> logs = txLogMapper.selectByTxId(txId, 1); // 查询成功的步骤
Collections.reverse(logs); // 逆序补偿
for (TxLog log : logs) {
switch (log.getAction()) {
case "insert_order":
orderMapper.deleteById(Long.parseLong(log.getBizId())); // 删除订单
break;
case "update_stock":
stockMapper.updateCount(Long.parseLong(log.getBizId()), 1); // 恢复库存
break;
}
txLogMapper.updateStatus(log.getId(), 2); // 标记补偿完成
}
}
}
关键点
- 日志表唯一性:
tx_id + step唯一,避免重复记录。 - 补偿幂等性:补偿操作需支持重复执行(如用
status字段判断)。
三、方案 3:基于消息队列的最终一致性(异步解耦)
复杂度:★★★☆☆(需引入消息队列,如 RabbitMQ)
原理:通过“本地消息表”确保“业务操作+消息发送”原子性,消息队列异步触发远程操作。
Java 实现步骤
1. 依赖准备
- Spring Boot Starter AMQP(RabbitMQ)
- MySQL 驱动
2. 核心代码(订单服务+库存服务)
订单服务(生产者):
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private MsgLogMapper msgLogMapper; // 本地消息表 Mapper
@Autowired
private RabbitTemplate rabbitTemplate;
@Transactional(rollbackFor = Exception.class)
public void createOrder(Order order) {
// 1. 本地事务:插入订单
orderMapper.insert(order);
// 2. 记录消息到本地消息表
MsgLog msgLog = new MsgLog(UUID.randomUUID().toString(), "order_created",
JSON.toJSONString(order), 0);
msgLogMapper.insert(msgLog);
}
// 后台线程发送消息(定时任务)
@Scheduled(fixedRate = 5000)
public void sendPendingMsgs() {
List<MsgLog> logs = msgLogMapper.selectByStatus(0); // 查询待发送消息
for (MsgLog log : logs) {
try {
rabbitTemplate.convertAndSend("order_exchange", "order.created", log.getContent());
msgLogMapper.updateStatus(log.getId(), 1); // 标记为已发送
} catch (Exception e) {
msgLogMapper.updateStatus(log.getId(), 2); // 标记为发送失败
}
}
}
}
库存服务(消费者):
@Component
public class StockConsumer {
@Autowired
private StockMapper stockMapper;
@RabbitListener(queues = "stock_queue")
public void handleOrderCreated(String orderJson) {
Order order = JSON.parseObject(orderJson, Order.class);
// 扣减库存(本地事务)
stockMapper.updateCount(order.getProductId(), -1);
}
}
关键点
- 消息可靠性:用本地消息表+定时任务确保消息不丢失。
- 消费者幂等性:通过订单 ID 去重(如 Redis 记录已处理订单)。
四、方案 4:TCC 模式(Try-Confirm-Cancel,强一致性)
复杂度:★★★★☆(需业务层实现三阶段逻辑)
原理:Try 预留资源,Confirm 确认执行业务,Cancel 取消预留,Java 需手动实现三阶段。
Java 实现步骤
1. TCC 接口定义
public interface TccService {
// Try 阶段:预留资源
boolean tryTransfer(Long fromId, Long toId, BigDecimal amount);
// Confirm 阶段:确认执行
boolean confirmTransfer(String txId);
// Cancel 阶段:取消预留
boolean cancelTransfer(String txId);
}
2. Java 实现(转账场景)
@Service
public class TransferTccService implements TccService {
@Autowired
private AccountMapper accountMapper;
@Autowired
private TccLogMapper tccLogMapper; // TCC 日志表(记录 txId 状态)
@Override
@Transactional
public boolean tryTransfer(Long fromId, Long toId, BigDecimal amount) {
// 1. 检查余额并冻结
Account from = accountMapper.selectById(fromId);
if (from.getBalance().compareTo(amount) < 0) return false;
accountMapper.updateFreeze(fromId, amount); // 冻结金额
// 2. 记录 TCC 日志(状态:TRYING)
tccLogMapper.insert(new TccLog(UUID.randomUUID().toString(), "transfer", "TRYING"));
return true;
}
@Override
@Transactional
public boolean confirmTransfer(String txId) {
TccLog log = tccLogMapper.selectByTxId(txId);
if (log == null || !"TRYING".equals(log.getStatus())) return false;
// 1. 扣减冻结金额
Account from = accountMapper.selectById(1L); // 假设 fromId=1
accountMapper.updateBalance(from.getId(), from.getBalance().subtract(from.getFreeze()));
accountMapper.updateFreeze(from.getId(), BigDecimal.ZERO);
// 2. 增加对方余额
Account to = accountMapper.selectById(2L); // 假设 toId=2
accountMapper.updateBalance(to.getId(), to.getBalance().add(amount));
// 3. 更新日志状态:CONFIRMED
tccLogMapper.updateStatus(txId, "CONFIRMED");
return true;
}
@Override
@Transactional
public boolean cancelTransfer(String txId) {
TccLog log = tccLogMapper.selectByTxId(txId);
if (log == null || !"TRYING".equals(log.getStatus())) return false;
// 1. 释放冻结金额
Account from = accountMapper.selectById(1L);
accountMapper.updateFreeze(from.getId(), BigDecimal.ZERO);
// 2. 更新日志状态:CANCELED
tccLogMapper.updateStatus(txId, "CANCELED");
return true;
}
}
关键点
- 幂等性:用
txId判断 Confirm/Cancel 是否已执行。 - 空回滚:Try 未执行但 Cancel 被调用时,需忽略。
五、方案 5:SAGA 模式(长事务拆分,最终一致性)
复杂度:★★★★★(最复杂,需拆分长事务为子事务)
原理:将长事务拆分为子事务,每个子事务有补偿方法,失败时逆序补偿。
Java 实现步骤
1. 子事务定义
// 子事务接口
public interface SagaStep {
void execute() throws Exception; // 执行子事务
void compensate() throws Exception; // 补偿子事务
}
// 预订酒店子事务
public class BookHotelStep implements SagaStep {
@Override public void execute() { /* 调用酒店 API 预订 */ }
@Override public void compensate() { /* 取消酒店预订 */ }
}
// 购买机票子事务
public class BuyTicketStep implements SagaStep {
@Override public void execute() { /* 调用机票 API 购买 */ }
@Override public void compensate() { /* 退机票 */ }
}
2. SAGA 执行器
public class SagaExecutor {
private List<SagaStep> steps = new ArrayList<>();
private Map<Integer, Boolean> executedSteps = new HashMap<>(); // 记录执行状态
public void addStep(SagaStep step) {
steps.add(step);
}
public void run() throws Exception {
List<Integer> successIndexes = new ArrayList<>();
try {
for (int i = 0; i < steps.size(); i++) {
steps.get(i).execute(); // 执行子事务
successIndexes.add(i); // 记录成功步骤
}
} catch (Exception e) {
// 失败时逆序补偿
for (int i = successIndexes.size() - 1; i >= 0; i--) {
steps.get(successIndexes.get(i)).compensate();
}
throw e;
}
}
}
3. 使用示例
public class TravelBookingSaga {
public static void main(String[] args) throws Exception {
SagaExecutor executor = new SagaExecutor();
executor.addStep(new BookHotelStep());
executor.addStep(new BuyTicketStep());
executor.run(); // 执行 SAGA 事务
}
}
关键点
- 补偿顺序:严格逆序补偿(后进先出)。
- 状态持久化:用数据库记录子事务状态,支持崩溃恢复。
总结
| 方案 | 复杂度 | Java 核心技术 | 适用场景 |
|---|---|---|---|
| MySQL XA 事务 | ★☆☆☆☆ | javax.sql.XADataSource、两阶段提交 | 小规模、强一致、同构 MySQL |
| 应用层伪分布式 | ★★☆☆☆ | 事务日志表、补偿逻辑 | 非核心业务、简单补偿 |
| 消息队列最终一致 | ★★★☆☆ | RabbitMQ/Kafka、本地消息表 | 微服务解耦、异步场景 |
| TCC 模式 | ★★★★☆ | Try/Confirm/Cancel 三阶段、幂等性 | 核心业务、短事务、强一致 |
| SAGA 模式 | ★★★★★ | 子事务拆分、状态机、逆序补偿 | 长事务、业务流程化、最终一致 |
选择建议:优先用 XA(简单强一致)或消息队列(解耦),核心业务用 TCC,长事务用 SAGA。Java 实现需注意事务日志、幂等性和异常处理,确保分布式事务可靠性。
结合第三方组件实现分布式事务
MySQL 结合第三方分布式事务组件实现分布式事务详解
在实际生产环境中,纯手动实现分布式事务(如原生 XA、TCC、SAGA)存在开发成本高、易出错、维护复杂等问题。第三方分布式事务组件通过封装底层协调逻辑(如事务管理器、资源管理器、锁机制),大幅简化分布式事务开发。本文以 主流组件 Seata 为例,详细说明如何结合 MySQL 实现分布式事务,并对比其他组件的特点。
一、第三方分布式事务组件概述
第三方组件核心作用是统一管理分布式事务的协调逻辑,提供标准化接口(如注解、API),支持多种事务模式(AT、TCC、SAGA、XA),适配不同业务场景。
主流组件对比
| 组件 | 核心模式 | 特点 | 适用场景 |
|---|---|---|---|
| Seata | AT/TCC/SAGA/XA | 阿里开源,社区活跃,支持多模式,无侵入 AT 模式 | 大多数分布式场景(电商、金融) |
| Hmily | TCC/SAGA | 轻量级,支持熔断、重试,国产开源 | 中小规模项目,追求轻量灵活 |
| ByteTCC | TCC/XA | 性能优异,支持 Dubbo/Spring Cloud | 高并发核心业务(如支付、转账) |
| TCC-Transaction | TCC | 较早开源,文档丰富,需手动实现补偿逻辑 | 熟悉 TCC 模式的团队 |
二、以 Seata 为例:结合 MySQL 实现分布式事务
Seata(Simple Extensible Autonomous Transaction Architecture)是阿里开源的分布式事务解决方案,支持 AT(Automatic Transaction,无侵入)、TCC、SAGA、XA 四种模式。以下以最常用的 AT 模式(无侵入,适合快速接入)为例,详细说明实现步骤。
核心概念
- TC(Transaction Coordinator):事务协调者,独立部署的服务,维护全局事务和分支事务状态,驱动提交/回滚。
- TM(Transaction Manager):事务管理器,嵌入应用中,定义全局事务范围(
@GlobalTransactional注解)。 - RM(Resource Manager):资源管理器,嵌入应用中,管理分支事务(MySQL 数据库连接),向 TC 汇报状态,执行 undo/redo 日志。
实现步骤
1. 环境准备
- MySQL:至少 2 个实例(模拟跨库),或单库多表(简化演示)。
- Seata Server:下载并启动 TC(https://seata.io/zh-cn/blog/download.html)。
- 注册中心:Seata 需注册中心(如 Nacos、Eureka),这里用 Nacos 示例。
2. 部署 Seata Server
(1)修改 Seata 配置
下载 Seata Server 后,修改 conf/application.yml(以 Nacos 为注册中心):
server:
port: 7091
spring:
application:
name: seata-server
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848 # Nacos 地址
namespace: "" # 命名空间(默认 public)
seata:
config:
type: nacos # 配置中心用 Nacos
nacos:
server-addr: 127.0.0.1:8848
group: SEATA_GROUP
namespace: ""
registry:
type: nacos # 注册中心用 Nacos
nacos:
application: seata-server
server-addr: 127.0.0.1:8848
group: SEATA_GROUP
namespace: ""
store:
mode: file # 事务日志存储(生产用 db 模式)
(2)启动 Seata Server
# Linux/Mac
sh bin/seata-server.sh -p 8091 -h 127.0.0.1
# Windows
bin/seata-server.bat -p 8091 -h 127.0.0.1
3. MySQL 配置(AT 模式必需)
AT 模式依赖 undo_log 表记录回滚日志,需在参与事务的每个 MySQL 库中创建:
CREATE TABLE `undo_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`branch_id` bigint(20) NOT NULL,
`xid` varchar(100) NOT NULL,
`context` varchar(128) NOT NULL,
`rollback_info` longblob NOT NULL,
`log_status` int(11) NOT NULL,
`log_created` datetime NOT NULL,
`log_modified` datetime NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
4. 项目集成 Seata(Spring Boot 示例)
以“订单-库存”分布式事务为例(订单库 db_order,库存库 db_stock),使用 Seata AT 模式。
(1)添加依赖
<!-- Seata 核心依赖 -->
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>1.6.1</version> <!-- 最新稳定版 -->
</dependency>
<!-- Nacos 注册中心(若用其他注册中心需替换) -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- MySQL 驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- MyBatis Plus(简化 ORM) -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
(2)配置文件(application.yml)
spring:
application:
name: order-service # 服务名(注册到 Nacos)
datasource:
# 订单库数据源(需 Seata 代理)
order:
url: jdbc:mysql://localhost:3306/db_order?useSSL=false&serverTimezone=UTC
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
# 库存库数据源(需 Seata 代理)
stock:
url: jdbc:mysql://localhost:3307/db_stock?useSSL=false&serverTimezone=UTC
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848 # Nacos 地址
# Seata 配置
seata:
application-id: ${spring.application.name} # 应用 ID(默认服务名)
tx-service-group: my_test_tx_group # 事务组名称(需与 Seata Server 配置一致)
service:
vgroup-mapping:
my_test_tx_group: default # 事务组映射到 Seata Server 集群名(默认 default)
registry:
type: nacos
nacos:
application: seata-server # Seata Server 注册到 Nacos 的服务名
server-addr: 127.0.0.1:8848
config:
type: nacos
nacos:
server-addr: 127.0.0.1:8848
group: SEATA_GROUP
(3)数据源代理(关键!Seata 需代理数据源)
Seata 通过代理数据源拦截 SQL,生成 undo_log 并记录全局锁。需为每个数据源配置代理:
@Configuration
public class DataSourceProxyConfig {
// 订单库数据源代理
@Bean
@ConfigurationProperties(prefix = "spring.datasource.order")
public DruidDataSource orderDataSource() {
return new DruidDataSource();
}
@Bean
public DataSourceProxy orderDataSourceProxy(DruidDataSource orderDataSource) {
return new DataSourceProxy(orderDataSource); // Seata 代理
}
// 库存库数据源代理(同上)
@Bean
@ConfigurationProperties(prefix = "spring.datasource.stock")
public DruidDataSource stockDataSource() {
return new DruidDataSource();
}
@Bean
public DataSourceProxy stockDataSourceProxy(DruidDataSource stockDataSource) {
return new DataSourceProxy(stockDataSource);
}
}
(4)业务逻辑实现(订单+库存)
订单服务(TM 角色,定义全局事务):
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderMapper orderMapper; // 订单库 Mapper
@Autowired
private StockFeignClient stockFeignClient; // 库存服务 Feign 客户端
// 全局事务注解:标记分布式事务入口
@GlobalTransactional(rollbackFor = Exception.class)
@Override
public void createOrder(OrderDTO orderDTO) {
// 1. 本地事务:插入订单(订单库)
Order order = new Order();
order.setId(orderDTO.getOrderId());
order.setUserId(orderDTO.getUserId());
order.setAmount(orderDTO.getAmount());
orderMapper.insert(order);
// 2. 远程调用:扣减库存(库存库,分支事务)
stockFeignClient.deductStock(orderDTO.getProductId(), 1);
}
}
库存服务(RM 角色,分支事务):
@Service
public class StockServiceImpl implements StockService {
@Autowired
private StockMapper stockMapper; // 库存库 Mapper
@Override
public void deductStock(Long productId, Integer count) {
// 本地事务:扣减库存(库存库)
stockMapper.deductStock(productId, count);
}
}
5. 测试分布式事务
- 启动 Nacos、Seata Server、订单服务、库存服务。
- 调用订单服务的
createOrder接口:- 若库存扣减成功:全局事务提交,Seata TC 通知 RM 删除 undo_log。
- 若库存扣减失败(如抛异常):全局事务回滚,Seata TC 通知 RM 执行 undo_log 回滚订单插入。
三、Seata 其他模式简介
1. TCC 模式(手动补偿)
需业务层实现 Try-Confirm-Cancel 接口,Seata 协调三阶段提交:
// TCC 接口示例(库存服务)
@LocalTCC
public interface StockTccService {
// Try:预留资源(冻结库存)
@TwoPhaseBusinessAction(name = "deductStockTcc", commitMethod = "confirm", rollbackMethod = "cancel")
boolean tryDeduct(@BusinessActionContextParameter(paramName = "productId") Long productId,
@BusinessActionContextParameter(paramName = "count") Integer count);
// Confirm:确认执行业务(扣减冻结库存)
boolean confirm(BusinessActionContext context);
// Cancel:取消预留(释放冻结库存)
boolean cancel(BusinessActionContext context);
}
2. SAGA 模式(长事务拆分)
将长事务拆分为子事务,Seata 记录状态并驱动补偿:
// SAGA 事务配置(通过 JSON 定义子事务和补偿)
{
"forward": [
{"name": "createOrder", "service": "orderService", "method": "create"},
{"name": "deductStock", "service": "stockService", "method": "deduct"}
],
"reverse": [
{"name": "cancelOrder", "service": "orderService", "method": "cancel"},
{"name": "restoreStock", "service": "stockService", "method": "restore"}
]
}
四、其他第三方组件简要示例
1. Hmily(TCC/SAGA 模式)
- 特点:轻量级,支持熔断、重试,国产开源。
- 集成步骤:
- 引入依赖
org.dromara:hmily-spring-boot-starter。 - 配置 Hmily Server 地址。
- 业务方法添加
@Hmily注解,实现 TCC 三阶段接口。
- 引入依赖
2. ByteTCC(TCC/XA 模式)
- 特点:性能优异,支持 Dubbo/Spring Cloud,字节跳动开源。
- 集成步骤:
- 引入依赖
org.bytesoft:bytetcc-core。 - 配置 ByteTCC 事务管理器。
- 业务层实现
Compensable接口(TCC)或XATransaction接口(XA)。
- 引入依赖
五、总结
第三方分布式事务组件通过封装协调逻辑,大幅降低了 MySQL 分布式事务的开发门槛。Seata 是首选方案,其 AT 模式无侵入、易接入,适合大多数场景;TCC 模式适合核心业务手动控制;SAGA 模式适合长事务。其他组件如 Hmily、ByteTCC 可根据团队熟悉度和性能需求选择。
关键优势:
- 无需手动编写补偿逻辑(AT 模式)。
- 提供事务监控、重试、熔断等增强功能。
- 适配多种注册中心和服务框架(Spring Cloud、Dubbo)。
通过组件化方案,可高效实现 MySQL 分布式事务,保障数据一致性。
3万+

被折叠的 条评论
为什么被折叠?



