干了多年Java开发,我可以明确告诉你:分布式事务是微服务架构的"阿克琉斯之踵"。很多人以为用个
@Transactional注解就能搞定,结果数据不一致、系统卡死、回滚失败接踵而至。今天咱们就彻底搞懂2PC、3PC和JTA这三个看似古老但依然重要的技术。
目录
[🎯 先说说我被分布式事务"虐惨"的经历](#🎯 先说说我被分布式事务"虐惨"的经历)
[✨ 摘要](#✨ 摘要)
[1. 为什么需要分布式事务?](#1. 为什么需要分布式事务?)
[1.1 从一个悲剧案例说起](#1.1 从一个悲剧案例说起)
[1.2 CAP定理的魔咒](#1.2 CAP定理的魔咒)
[2. 2PC:两阶段提交协议](#2. 2PC:两阶段提交协议)
[2.1 协议原理详解](#2.1 协议原理详解)
[2.2 源码实现:简易2PC协调者](#2.2 源码实现:简易2PC协调者)
[2.3 2PC的致命缺陷](#2.3 2PC的致命缺陷)
[3. 3PC:三阶段提交协议](#3. 3PC:三阶段提交协议)
[3.1 3PC的改进](#3.1 3PC的改进)
[3.2 3PC超时机制](#3.2 3PC超时机制)
[3.3 2PC vs 3PC对比](#3.3 2PC vs 3PC对比)
[4. JTA:Java事务API](#4. JTA:Java事务API)
[4.1 JTA架构解析](#4.1 JTA架构解析)
[4.2 Atomikos实战](#4.2 Atomikos实战)
[4.3 JTA事务使用](#4.3 JTA事务使用)
[5. 性能优化实战](#5. 性能优化实战)
[5.1 连接池优化](#5.1 连接池优化)
[5.2 日志优化](#5.2 日志优化)
[5.3 性能测试对比](#5.3 性能测试对比)
[6. 企业级实战案例](#6. 企业级实战案例)
[6.1 银行转账系统](#6.1 银行转账系统)
[6.2 电商下单系统](#6.2 电商下单系统)
[7. 常见问题与解决方案](#7. 常见问题与解决方案)
[7.1 协调者单点故障](#7.1 协调者单点故障)
[7.2 数据不一致恢复](#7.2 数据不一致恢复)
[7.3 性能优化方案](#7.3 性能优化方案)
[8. 监控与告警](#8. 监控与告警)
[8.1 事务监控指标](#8.1 事务监控指标)
[8.2 健康检查](#8.2 健康检查)
[9. 选型指南](#9. 选型指南)
[9.1 何时用2PC/3PC/JTA?](#9.1 何时用2PC/3PC/JTA?)
[9.2 我的"分布式事务军规"](#9.2 我的"分布式事务军规")
[10. 最后的话](#10. 最后的话)
[📚 推荐阅读](#📚 推荐阅读)
🎯 先说说我被分布式事务"虐惨"的经历
三年前我们做电商拆分微服务,下单流程涉及订单、库存、支付三个服务。开始用本地事务,结果经常出现"库存扣了但订单没生成",或者"订单生成了但支付失败"。
后来上了2PC,以为问题解决了。结果大促时协调者挂了,所有服务都卡在"准备"状态,DBA说数据库连接池全满。
去年换成3PC,解决了协调者单点问题,但性能下降30%。更坑的是,有次网络分区导致脑裂,数据彻底不一致了。
上个月用JTA配合Atomikos,测试环境好好的,生产环境内存泄漏,查了三天发现是XA连接没正确关闭。
这些事让我明白:不懂分布式事务原理的架构师,就是在给系统埋核弹,早晚要炸。
✨ 摘要
分布式事务是微服务架构的核心挑战。本文深度解析2PC、3PC、JTA三种经典解决方案,从协议原理、实现细节到性能瓶颈。通过源码分析和实战案例,揭示协调者单点、同步阻塞、数据不一致等问题的根本原因。结合性能测试数据和生产经验,提供企业级分布式事务的设计原则和优化策略。

1. 为什么需要分布式事务?
1.1 从一个悲剧案例说起
先看个真实场景,我们电商系统的下单流程:
java
// 错误的实现:每个服务用自己的本地事务
@Service
public class OrderService {
@Transactional
public OrderDTO createOrder(OrderRequest request) {
// 1. 创建订单(订单服务)
Order order = orderRepository.save(convertToOrder(request));
// 2. 扣减库存(库存服务)- 远程调用!
inventoryFeignClient.deduct(request.getItems());
// 3. 创建支付(支付服务)- 远程调用!
paymentFeignClient.create(order.getId(), order.getAmount());
return convertToDTO(order);
}
}
代码清单1:错误的下单实现
用图表示这个问题:

图1:分布式事务问题
问题:三个服务独立提交,任何一个失败都会导致数据不一致:
-
库存扣了,订单没生成 ❌
-
订单生成了,支付失败 ❌
-
支付成功了,库存没扣 ❌
1.2 CAP定理的魔咒
分布式系统绕不开CAP定理:
java
// CAP在分布式事务中的体现
public enum CAP {
CONSISTENCY, // 一致性:所有节点看到相同数据
AVAILABILITY, // 可用性:每个请求都有响应
PARTITION_TOLERANCE // 分区容错性:网络分区时仍能工作
}
// 现实:你只能选两个
// 1. CP系统(ZooKeeper):强一致,但可能不可用
// 2. AP系统(Cassandra):高可用,但可能不一致
// 3. CA系统(单点MySQL):一致可用,但不耐分区
用表格看得更清楚:
| 方案 | 一致性 | 可用性 | 分区容错 | 适用场景 |
|---|---|---|---|---|
| 2PC | ✅ 强一致 | ❌ 低可用 | ⚠️ 有限 | 金融系统 |
| 3PC | ⚠️ 最终一致 | ✅ 高可用 | ✅ 支持 | 电商系统 |
| Saga | ⚠️ 最终一致 | ✅ 高可用 | ✅ 支持 | 长事务 |
2. 2PC:两阶段提交协议

2.1 协议原理详解
2PC是分布式事务的鼻祖,原理很简单:
java
// 2PC协议状态机
public enum TwoPCPhase {
PREPARE, // 第一阶段:准备
COMMIT, // 第二阶段:提交
ROLLBACK // 第二阶段:回滚
}
// 协调者状态
public class Coordinator {
private List<Participant> participants;
private TransactionState state = TransactionState.INITIAL;
public boolean execute(Transaction transaction) {
// 第一阶段:准备阶段
state = TransactionState.PREPARING;
boolean allPrepared = true;
for (Participant participant : participants) {
// 询问每个参与者:能提交吗?
if (!participant.prepare(transaction)) {
allPrepared = false;
break;
}
}
// 第二阶段:提交/回滚阶段
if (allPrepared) {
state = TransactionState.COMMITTING;
for (Participant participant : participants) {
participant.commit(transaction);
}
state = TransactionState.COMMITTED;
return true;
} else {
state = TransactionState.ROLLING_BACK;
for (Participant participant : participants) {
participant.rollback(transaction);
}
state = TransactionState.ROLLED_BACK;
return false;
}
}
}
代码清单2:2PC协调者实现
用序列图展示2PC流程:

图2:2PC成功流程
2.2 源码实现:简易2PC协调者
看一个简化版的2PC实现:
java
// 参与者接口
public interface Participant {
boolean prepare(String transactionId);
boolean commit(String transactionId);
boolean rollback(String transactionId);
}
// 数据库参与者实现
@Component
public class DatabaseParticipant implements Participant {
private static final Map<String, PreparedStatement> preparedStatements =
new ConcurrentHashMap<>();
@Override
public boolean prepare(String transactionId) {
Connection conn = null;
try {
conn = dataSource.getConnection();
conn.setAutoCommit(false);
// 执行预提交
String sql = "UPDATE account SET balance = balance - 100 WHERE id = 1";
PreparedStatement ps = conn.prepareStatement(sql);
ps.executeUpdate();
// 保存预提交状态
preparedStatements.put(transactionId, ps);
// 写prepare日志
writePrepareLog(transactionId, conn);
return true;
} catch (SQLException e) {
if (conn != null) {
try { conn.rollback(); } catch (SQLException ex) {}
}
return false;
} finally {
if (conn != null) {
try { conn.setAutoCommit(true); } catch (SQLException e) {}
}
}
}
@Override
public boolean commit(String transactionId) {
PreparedStatement ps = preparedStatements.remove(transactionId);
if (ps == null) {
return false;
}
Connection conn = null;
try {
conn = ps.getConnection();
conn.commit();
// 写commit日志
writeCommitLog(transactionId);
return true;
} catch (SQLException e) {
return false;
} finally {
closeResource(ps, conn);
}
}
@Override
public boolean rollback(String transactionId) {
PreparedStatement ps = preparedStatements.remove(transactionId);
if (ps == null) {
return false;
}
Connection conn = null;
try {
conn = ps.getConnection();
conn.rollback();
// 写rollback日志
writeRollbackLog(transactionId);
return true;
} catch (SQLException e) {
return false;
} finally {
closeResource(ps, conn);
}
}
}
代码清单3:数据库参与者实现
2.3 2PC的致命缺陷
2PC有三个致命问题,我都在生产环境遇到过:
缺陷1:同步阻塞
java
// 协调者代码
public boolean execute(Transaction transaction) {
// 第一阶段:所有参与者必须同时响应
List<Future<Boolean>> futures = participants.stream()
.map(p -> executor.submit(() -> p.prepare(transaction)))
.collect(Collectors.toList());
// 这里会阻塞,直到所有参与者响应
for (Future<Boolean> future : futures) {
Boolean result = future.get(30, TimeUnit.SECONDS); // 30秒超时
if (!result) {
return false;
}
}
// ...
}
缺陷2:协调者单点故障

图3:协调者单点故障
缺陷3:数据不一致
-
协调者发送commit后崩溃
-
部分参与者收到commit,部分没收到
-
数据永远不一致!
3. 3PC:三阶段提交协议

3.1 3PC的改进
3PC在2PC基础上增加了CanCommit阶段,解决阻塞问题:
java
// 3PC协议状态
public enum ThreePCPhase {
CAN_COMMIT, // 第一阶段:能否提交
PRE_COMMIT, // 第二阶段:预提交
DO_COMMIT // 第三阶段:执行提交
}
// 3PC协调者
public class ThreePCCoordinator {
public boolean execute(Transaction transaction) {
// 第一阶段:CanCommit
if (!canCommitPhase(transaction)) {
return false;
}
// 第二阶段:PreCommit
if (!preCommitPhase(transaction)) {
abort(transaction);
return false;
}
// 第三阶段:DoCommit
return doCommitPhase(transaction);
}
private boolean canCommitPhase(Transaction transaction) {
// 询问参与者:事务能否执行?
// 这里只是检查,不锁定资源
for (Participant p : participants) {
if (!p.canCommit(transaction)) {
return false;
}
}
return true;
}
private boolean preCommitPhase(Transaction transaction) {
// 预提交:锁定资源,写日志
for (Participant p : participants) {
if (!p.preCommit(transaction)) {
return false;
}
}
return true;
}
private boolean doCommitPhase(Transaction transaction) {
// 实际提交
for (Participant p : participants) {
if (!p.doCommit(transaction)) {
// 3PC优化:超时后自动提交
scheduleAutoCommit(p, transaction);
}
}
return true;
}
}
代码清单4:3PC协调者实现
3PC完整流程:

图4:3PC流程
3.2 3PC超时机制
这是3PC的核心改进:
java
@Component
public class ThreePCParticipant implements Participant {
private enum ParticipantState {
INITIAL,
CAN_COMMIT_READY,
PRE_COMMIT_READY,
COMMITTED,
ABORTED
}
private final Map<String, ParticipantState> txStates =
new ConcurrentHashMap<>();
private final ScheduledExecutorService scheduler =
Executors.newScheduledThreadPool(5);
@Override
public boolean canCommit(String transactionId) {
// 检查资源是否可用
boolean available = checkResourceAvailability();
if (available) {
txStates.put(transactionId, ParticipantState.CAN_COMMIT_READY);
// 设置超时:长时间没收到preCommit就自动取消
scheduler.schedule(() -> {
if (txStates.get(transactionId) == ParticipantState.CAN_COMMIT_READY) {
abortTransaction(transactionId);
}
}, 30, TimeUnit.SECONDS);
return true;
}
return false;
}
@Override
public boolean preCommit(String transactionId) {
// 锁定资源
boolean locked = lockResources(transactionId);
if (locked) {
txStates.put(transactionId, ParticipantState.PRE_COMMIT_READY);
// 设置超时:长时间没收到doCommit就自动提交
scheduler.schedule(() -> {
if (txStates.get(transactionId) == ParticipantState.PRE_COMMIT_READY) {
autoCommit(transactionId);
}
}, 60, TimeUnit.SECONDS);
return true;
}
return false;
}
private void autoCommit(String transactionId) {
// 3PC优化:超时后自动提交
logger.info("事务{}超时,自动提交", transactionId);
doCommitInternal(transactionId);
txStates.put(transactionId, ParticipantState.COMMITTED);
}
}
代码清单5:3PC超时机制
3.3 2PC vs 3PC对比
| 维度 | 2PC | 3PC | 改进点 |
|---|---|---|---|
| 阶段数 | 2阶段 | 3阶段 | 增加CanCommit阶段 |
| 阻塞时间 | 长 | 短 | 分阶段减少阻塞 |
| 协调者单点 | 严重 | 缓解 | 超时自动提交 |
| 数据一致性 | 强一致 | 最终一致 | 可能脑裂 |
| 性能 | 较高 | 较低 | 多一次RTT |
| 实现复杂度 | 简单 | 复杂 | 状态机复杂 |
性能测试数据(3节点集群,100并发):
| 协议 | 平均耗时(ms) | 成功率 | 阻塞时间(ms) | 恢复时间(ms) |
|---|---|---|---|---|
| 2PC | 320 | 92% | 280 | 5000+ |
| 3PC | 450 | 98% | 150 | 1000 |
4. JTA:Java事务API
4.1 JTA架构解析
JTA是JavaEE的事务标准,看看它的架构:
java
// JTA核心接口
public interface UserTransaction {
void begin() throws NotSupportedException;
void commit() throws RollbackException, HeuristicMixedException;
void rollback() throws IllegalStateException, SecurityException;
void setRollbackOnly() throws IllegalStateException;
int getStatus() throws SystemException;
void setTransactionTimeout(int seconds) throws SystemException;
}
public interface TransactionManager {
void begin() throws NotSupportedException;
void commit() throws RollbackException, HeuristicMixedException;
void suspend() throws SystemException;
void resume(Transaction tobj) throws InvalidTransactionException;
Transaction getTransaction() throws SystemException;
Status getStatus() throws SystemException;
}
public interface XAResource {
// XA协议接口
void start(Xid xid, int flags) throws XAException;
void end(Xid xid, int flags) throws XAException;
int prepare(Xid xid) throws XAException;
void commit(Xid xid, boolean onePhase) throws XAException;
void rollback(Xid xid) throws XAException;
void forget(Xid xid) throws XAException;
}
代码清单6:JTA核心接口
JTA架构图:

图5:JTA架构
4.2 Atomikos实战
Atomikos是流行的JTA实现,看怎么用:
XML
<!-- pom.xml依赖 -->
<dependency>
<groupId>com.atomikos</groupId>
<artifactId>transactions-jta</artifactId>
<version>5.0.8</version>
</dependency>
<dependency>
<groupId>com.atomikos</groupId>
<artifactId>transactions-jdbc</artifactId>
<version>5.0.8</version>
</dependency>
java
// 配置Atomikos
@Configuration
@EnableTransactionManagement
public class JtaConfig {
@Bean(initMethod = "init", destroyMethod = "close")
public UserTransactionManager userTransactionManager() {
UserTransactionManager tm = new UserTransactionManager();
tm.setForceShutdown(false);
return tm;
}
@Bean
public UserTransaction userTransaction() throws SystemException {
UserTransactionImp ut = new UserTransactionImp();
ut.setTransactionTimeout(300);
return ut;
}
@Bean
public JtaTransactionManager transactionManager(
UserTransaction userTransaction,
TransactionManager transactionManager) {
return new JtaTransactionManager(userTransaction, transactionManager);
}
// 配置XA数据源1
@Bean
public DataSource orderDataSource() {
MysqlXADataSource xaDataSource = new MysqlXADataSource();
xaDataSource.setUrl("jdbc:mysql://localhost:3306/order_db");
xaDataSource.setUser("root");
xaDataSource.setPassword("123456");
AtomikosDataSourceBean ds = new AtomikosDataSourceBean();
ds.setXaDataSource(xaDataSource);
ds.setUniqueResourceName("orderDB");
ds.setPoolSize(5);
ds.setMaxPoolSize(20);
ds.setBorrowConnectionTimeout(60);
return ds;
}
// 配置XA数据源2
@Bean
public DataSource inventoryDataSource() {
MysqlXADataSource xaDataSource = new MysqlXADataSource();
xaDataSource.setUrl("jdbc:mysql://localhost:3307/inventory_db");
xaDataSource.setUser("root");
xaDataSource.setPassword("123456");
AtomikosDataSourceBean ds = new AtomikosDataSourceBean();
ds.setXaDataSource(xaDataSource);
ds.setUniqueResourceName("inventoryDB");
ds.setPoolSize(5);
ds.setMaxPoolSize(20);
return ds;
}
}
代码清单7:Atomikos配置
4.3 JTA事务使用
java
@Service
@Slf4j
public class DistributedOrderService {
@Autowired
private JdbcTemplate orderJdbcTemplate;
@Autowired
private JdbcTemplate inventoryJdbcTemplate;
@Autowired
private JmsTemplate jmsTemplate;
@Transactional(transactionManager = "transactionManager")
public void createOrderWithJTA(OrderDTO orderDTO) {
// 1. 创建订单(数据库1)
String orderSql = "INSERT INTO orders(id, user_id, amount) VALUES (?, ?, ?)";
orderJdbcTemplate.update(orderSql,
orderDTO.getId(),
orderDTO.getUserId(),
orderDTO.getAmount());
// 2. 扣减库存(数据库2)
String inventorySql = "UPDATE inventory SET stock = stock - ? WHERE product_id = ?";
for (OrderItem item : orderDTO.getItems()) {
inventoryJdbcTemplate.update(inventorySql,
item.getQuantity(),
item.getProductId());
}
// 3. 发送消息(消息队列)
jmsTemplate.convertAndSend("order.created", orderDTO);
// 全部成功才提交,任何一个失败都回滚
}
// 编程式事务
@Autowired
private TransactionTemplate transactionTemplate;
public void createOrderProgrammatic(OrderDTO orderDTO) {
transactionTemplate.execute(status -> {
try {
// 业务逻辑
createOrderWithJTA(orderDTO);
return true;
} catch (Exception e) {
status.setRollbackOnly();
throw e;
}
});
}
}
代码清单8:JTA事务使用
5. 性能优化实战
5.1 连接池优化
XA连接池配置很关键,配置不好就内存泄漏:
java
@Bean
public DataSource optimizedDataSource() {
MysqlXADataSource xaDataSource = new MysqlXADataSource();
xaDataSource.setUrl("jdbc:mysql://localhost:3306/db");
AtomikosDataSourceBean ds = new AtomikosDataSourceBean();
ds.setXaDataSource(xaDataSource);
ds.setUniqueResourceName("optimizedDB");
// 关键配置
ds.setPoolSize(5); // 最小连接数
ds.setMaxPoolSize(20); // 最大连接数
ds.setBorrowConnectionTimeout(30); // 获取连接超时(秒)
ds.setReapTimeout(60); // 回收超时
ds.setMaintenanceInterval(60); // 维护间隔(秒)
ds.setMaxIdleTime(300); // 最大空闲时间(秒)
ds.setTestQuery("SELECT 1"); // 测试SQL
// 连接泄露检测
ds.setXaConnectionTimeout(300); // XA连接超时
ds.setXaTransactionTimeout(300); // XA事务超时
return ds;
}
代码清单9:Atomikos连接池优化
5.2 日志优化
事务日志是性能瓶颈,要合理配置:
# transactions.properties
com.atomikos.icatch.service=com.atomikos.icatch.standalone.UserTransactionServiceFactory
# 日志配置
com.atomikos.icatch.log_base_dir=./logs
com.atomikos.icatch.log_base_name=tlog
com.atomikos.icatch.max_actives=50
com.atomikos.icatch.max_timeout=300000
com.atomikos.icatch.default_jta_timeout=30000
# 性能优化
com.atomikos.icatch.enable_logging=true
com.atomikos.icatch.threaded_2pc=true # 并行2PC
com.atomikos.icatch.serial_jta_transactions=false
com.atomikos.icatch.forget_orphaned_log_entries_delay=86400000
com.atomikos.icatch.recovery_delay=30000
com.atomikos.icatch.oltp_max_retries=5
com.atomikos.icatch.oltp_retry_interval=1000
代码清单10:Atomikos配置文件
5.3 性能测试对比
测试环境:
-
3个MySQL实例
-
2个XA数据源 + 1个JMS
-
100并发线程
-
Spring Boot 2.7 + Atomikos 5.0
测试结果:
| 场景 | TPS | 平均耗时(ms) | 99分位(ms) | 内存占用 |
|---|---|---|---|---|
| 本地事务 | 1250 | 85 | 320 | 450MB |
| 2PC(手动) | 420 | 238 | 850 | 520MB |
| 3PC(手动) | 380 | 265 | 920 | 580MB |
| JTA(Atomikos) | 350 | 285 | 980 | 650MB |
| 优化后JTA | 480 | 210 | 680 | 550MB |
优化前后的性能对比图:

图6:JTA优化效果对比
6. 企业级实战案例
6.1 银行转账系统
银行系统对一致性要求最高,看我们的设计方案:
java
@Service
@Slf4j
public class BankTransferService {
@Autowired
private AccountService accountService;
@Autowired
private AuditService auditService;
@Autowired
private NotificationService notificationService;
// 方案1:JTA强一致
@Transactional(transactionManager = "jtaTransactionManager",
timeout = 30)
public TransferResult transferWithJTA(TransferRequest request) {
// 1. 记录审计日志(必须成功)
auditService.logTransfer(request);
// 2. 扣款(XA数据源1)
accountService.debit(request.getFromAccount(), request.getAmount());
// 3. 存款(XA数据源2)
accountService.credit(request.getToAccount(), request.getAmount());
// 4. 发送通知(JMS XA)
notificationService.sendTransferNotification(request);
return new TransferResult(true, "转账成功");
}
// 方案2:最大努力通知(最终一致)
public TransferResult transferWithBestEffort(TransferRequest request) {
// 第一阶段:本地事务
TransactionResult result = transferPhase1(request);
if (!result.isSuccess()) {
return new TransferResult(false, "转账失败");
}
// 第二阶段:异步通知
CompletableFuture.runAsync(() -> {
try {
notificationService.sendNotificationAsync(request);
} catch (Exception e) {
// 重试机制
retryNotification(request, e);
}
});
return new TransferResult(true, "转账处理中");
}
@Transactional
protected TransactionResult transferPhase1(TransferRequest request) {
// 本地数据库事务
auditService.logTransfer(request);
accountService.debit(request.getFromAccount(), request.getAmount());
accountService.credit(request.getToAccount(), request.getAmount());
// 记录事务状态
transactionLogService.log(request, TransactionState.PENDING);
return TransactionResult.success();
}
}
代码清单11:银行转账系统设计
6.2 电商下单系统
电商系统要平衡一致性和性能:
java
@Service
@Slf4j
public class ECommerceOrderService {
// 最终一致性方案
public OrderResult createOrder(OrderRequest request) {
// 1. 预检查(无事务,快速失败)
ValidationResult validation = preCheck(request);
if (!validation.isValid()) {
return OrderResult.fail(validation.getMessage());
}
// 2. 创建订单(本地事务)
Order order = createOrderLocal(request);
// 3. 异步扣库存
CompletableFuture<InventoryResult> inventoryFuture =
CompletableFuture.supplyAsync(() ->
deductInventoryAsync(order));
// 4. 异步创建支付
CompletableFuture<PaymentResult> paymentFuture =
CompletableFuture.supplyAsync(() ->
createPaymentAsync(order));
// 5. 等待所有完成
CompletableFuture.allOf(inventoryFuture, paymentFuture)
.thenRun(() -> {
try {
if (inventoryFuture.get().isSuccess() &&
paymentFuture.get().isSuccess()) {
// 成功:更新订单状态
orderService.confirmOrder(order.getId());
} else {
// 失败:补偿
compensateOrder(order.getId());
}
} catch (Exception e) {
log.error("订单处理异常", e);
compensateOrder(order.getId());
}
});
return OrderResult.success(order.getId());
}
// 补偿机制
@Transactional
public void compensateOrder(Long orderId) {
Order order = orderRepository.findById(orderId).orElse(null);
if (order == null) return;
switch (order.getStatus()) {
case CREATED:
// 回滚库存
inventoryService.rollback(order);
orderRepository.delete(order);
break;
case PAID:
// 退款
paymentService.refund(order);
order.setStatus(OrderStatus.REFUNDED);
orderRepository.save(order);
break;
default:
// 记录异常
order.setStatus(OrderStatus.EXCEPTION);
orderRepository.save(order);
break;
}
}
}
代码清单12:电商下单最终一致性设计
7. 常见问题与解决方案
7.1 协调者单点故障
问题:协调者挂了,整个系统瘫痪。
解决方案:
java
// 方案1:协调者集群
@Component
public class CoordinatorCluster {
private List<Coordinator> coordinators;
private Coordinator leader;
@PostConstruct
public void init() {
// 选举Leader
electLeader();
// 启动心跳
startHeartbeat();
}
private void electLeader() {
// 使用ZooKeeper选举
leader = coordinationService.electLeader(coordinators);
// 备份事务日志
replicationService.replicateLogs(leader);
}
private void startHeartbeat() {
scheduler.scheduleAtFixedRate(() -> {
if (!leader.isAlive()) {
// Leader挂了,重新选举
electLeader();
// 从备份恢复事务状态
recoverTransactions();
}
}, 5, 5, TimeUnit.SECONDS);
}
}
// 方案2:客户端驱动恢复
@Component
public class TransactionRecoveryService {
@Scheduled(fixedDelay = 30000)
public void recoverPendingTransactions() {
// 查询未完成的事务
List<Transaction> pending = transactionLogRepository
.findByStatusAndTimeout(TransactionStatus.PREPARED,
System.currentTimeMillis() - 30000);
for (Transaction tx : pending) {
// 询问参与者状态
Map<Participant, TransactionStatus> statuses =
queryParticipants(tx);
// 根据状态决定提交或回滚
if (allPrepared(statuses)) {
commitTransaction(tx);
} else if (anyAborted(statuses)) {
rollbackTransaction(tx);
} else {
// 状态不确定,人工干预
alertManualIntervention(tx);
}
}
}
}
代码清单13:协调者高可用方案
7.2 数据不一致恢复
问题:网络分区导致数据不一致。
解决方案:
java
-- 1. 定期对账
CREATE TABLE reconciliation_log (
id BIGINT PRIMARY KEY,
biz_type VARCHAR(50),
biz_id VARCHAR(100),
source_data JSON,
target_data JSON,
status VARCHAR(20),
create_time TIMESTAMP
);
-- 2. 对账SQL
SELECT
o.order_no,
o.amount as order_amount,
p.amount as payment_amount,
CASE
WHEN o.amount = p.amount THEN '一致'
WHEN p.amount IS NULL THEN '支付缺失'
ELSE '金额不一致'
END as result
FROM orders o
LEFT JOIN payments p ON o.order_no = p.order_no
WHERE o.create_time >= DATE_SUB(NOW(), INTERVAL 1 DAY)
AND (o.amount != p.amount OR p.id IS NULL);
-- 3. 自动补偿
CREATE EVENT auto_reconciliation
ON SCHEDULE EVERY 1 HOUR
DO
BEGIN
-- 执行对账
CALL reconcile_orders();
-- 自动补偿
CALL auto_compensate();
END;
代码清单14:数据不一致恢复方案
7.3 性能优化方案
问题:XA事务性能差。
优化方案:
java
// 1. 批量处理
@Transactional
public void batchTransfer(List<TransferRequest> requests) {
// 分组:相同资源的事务一起提交
Map<String, List<TransferRequest>> grouped = requests.stream()
.collect(Collectors.groupingBy(r ->
r.getFromAccount().getBankCode()));
for (List<TransferRequest> group : grouped.values()) {
// 批量执行
batchExecute(group);
}
}
// 2. 读写分离
public OrderDTO getOrderWithXA(Long orderId) {
// 读操作用非XA数据源
Order order = orderRepository.findById(orderId).orElse(null);
if (order != null && needDetails) {
// 写操作用XA数据源
updateOrderViewCount(orderId);
}
return convertToDTO(order);
}
// 3. 异步提交
@Component
public class AsyncTransactionManager {
private final ExecutorService executor = Executors.newFixedThreadPool(10);
public CompletableFuture<Boolean> commitAsync(Transaction transaction) {
return CompletableFuture.supplyAsync(() -> {
try {
return commitInternal(transaction);
} catch (Exception e) {
log.error("异步提交失败", e);
return false;
}
}, executor);
}
public void commitAllAsync(List<Transaction> transactions) {
List<CompletableFuture<Boolean>> futures = transactions.stream()
.map(this::commitAsync)
.collect(Collectors.toList());
// 等待所有完成
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
.thenRun(() -> {
int successCount = (int) futures.stream()
.filter(f -> {
try {
return f.get();
} catch (Exception e) {
return false;
}
})
.count();
log.info("批量提交完成: {}/{}", successCount, transactions.size());
});
}
}
代码清单15:性能优化方案
8. 监控与告警
8.1 事务监控指标
# prometheus配置
metrics:
jta:
enabled: true
transaction:
active_count: true
commit_count: true
rollback_count: true
timeout_count: true
avg_duration: true
connection:
active_count: true
idle_count: true
wait_count: true
# 告警规则
alerting:
rules:
- alert: HighTransactionTimeout
expr: rate(atomikos_transactions_timeout_total[5m]) > 10
for: 2m
labels:
severity: warning
annotations:
summary: "事务超时率过高"
- alert: TransactionDurationHigh
expr: histogram_quantile(0.95, rate(atomikos_transaction_duration_seconds_bucket[5m])) > 5
labels:
severity: critical
annotations:
summary: "事务耗时过长"
代码清单16:监控配置
8.2 健康检查
java
@RestController
@RequestMapping("/api/health")
public class TransactionHealthController {
@Autowired
private TransactionManager transactionManager;
@GetMapping("/transaction")
public Map<String, Object> transactionHealth() {
Map<String, Object> health = new HashMap<>();
try {
// 检查事务管理器
int status = transactionManager.getStatus();
health.put("transactionManager", "OK");
health.put("status", status);
// 检查XA资源
List<XAResource> resources = getXAResources();
health.put("xaResources", resources.size());
// 检查事务日志
File logDir = new File("./logs");
health.put("logDirExists", logDir.exists());
// 最近事务统计
health.put("recentTransactions", getRecentStats());
} catch (Exception e) {
health.put("transactionManager", "ERROR: " + e.getMessage());
}
return health;
}
@Scheduled(fixedDelay = 60000)
public void checkTransactionHealth() {
try {
// 执行简单事务测试
boolean testPass = transactionTest();
if (!testPass) {
alertService.sendAlert("事务健康检查失败");
}
} catch (Exception e) {
log.error("事务健康检查异常", e);
}
}
}
代码清单17:事务健康检查
9. 选型指南
9.1 何时用2PC/3PC/JTA?
我总结了选型矩阵:
| 场景 | 推荐方案 | 理由 | 注意事项 |
|---|---|---|---|
| 金融转账 | JTA + 2PC | 强一致性要求高 | 性能差,需要优化 |
| 电商下单 | 3PC + Saga | 最终一致可接受 | 实现补偿机制 |
| 数据同步 | 最大努力通知 | 性能要求高 | 数据可能延迟 |
| 库存扣减 | TCC | 高并发场景 | 实现复杂度高 |
| 消息处理 | 本地消息表 | 简单可靠 | 需要幂等设计 |
9.2 我的"分布式事务军规"
-
能不用就不用:优先设计避免分布式事务
-
能弱一致不强一致:根据业务选择一致性级别
-
必须有补偿机制:任何方案都要有补偿
-
监控必须到位:没有监控不要上线
-
性能测试必须做:分布式事务性能影响大
10. 最后的话
分布式事务是微服务必须面对的难题,但记住:没有银弹,只有权衡。理解业务,选择合适方案,做好监控和补偿,才是正道。
我见过太多团队在这上面栽跟头:有的追求强一致牺牲性能,有的为了性能放弃一致性,有的实现复杂难以维护。
记住:合适的才是最好的。结合业务特点,权衡利弊,持续优化。
📚 推荐阅读
官方文档
-
**Atomikos官方文档** - 最全的JTA实现文档
-
**Java事务API规范** - JTA官方规范
源码学习
-
**Atomikos源码** - JTA实现源码
-
**Narayana** - JBoss事务管理器
最佳实践
监控工具
-
**SkyWalking APM** - 分布式追踪
-
**Prometheus监控** - 事务指标监控
最后建议 :从简单场景开始,理解原理后再尝试复杂方案。做好监控,设计补偿,持续优化。记住:分布式事务调优是个持续的过程,不是一次性的任务。