📖 开场:跨行转账
想象你用支付宝给朋友的银行卡转账 💰:
单体应用(本地事务):
同一个数据库:
你的账户 -100元
朋友的账户 +100元
↓
事务:要么都成功,要么都失败 ✅
分布式系统(分布式事务):
支付宝数据库:
你的账户 -100元 ✅
↓
【网络故障】💔
↓
银行数据库:
朋友的账户 +100元 ❌ 失败
结果:
你扣了100元,朋友没收到 😱
钱凭空消失了!
这就是分布式事务问题:如何保证跨系统、跨数据库的操作一致性!
🤔 什么是分布式事务?
本地事务 vs 分布式事务
本地事务(ACID):
┌────────────────┐
│ 一个数据库 │
│ │
│ BEGIN; │
│ UPDATE A... │ ← 事务内操作
│ UPDATE B... │ ← 事务内操作
│ COMMIT; │
│ │
└────────────────┘
特点:数据库保证ACID ✅
分布式事务:
┌────────────────┐ ┌────────────────┐
│ 服务A │ │ 服务B │
│ 数据库A │ │ 数据库B │
│ │ │ │
│ UPDATE A... │───?───→│ UPDATE B... │
│ │ │ │
└────────────────┘ └────────────────┘
问题:如何保证两个操作的一致性?🤔
- A成功,B失败 → 不一致 ❌
- A失败,B成功 → 不一致 ❌
- 都成功或都失败 ✅
CAP理论
CAP = 分布式系统的三个特性
| 特性 | 说明 |
|---|---|
| C (Consistency) | 一致性:所有节点看到的数据相同 |
| A (Availability) | 可用性:每个请求都能得到响应 |
| P (Partition tolerance) | 分区容错性:网络分区时系统仍能运行 |
定理:分布式系统最多只能同时满足两个 ⚠️
CP:一致性 + 分区容错(牺牲可用性)
例如:Zookeeper
AP:可用性 + 分区容错(牺牲一致性)
例如:Eureka
CA:一致性 + 可用性(牺牲分区容错)
例如:单机数据库(不是分布式)
BASE理论
BASE = CAP的延伸(最终一致性)
| 特性 | 说明 |
|---|---|
| BA (Basically Available) | 基本可用:允许部分不可用 |
| S (Soft state) | 软状态:中间状态 |
| E (Eventually consistent) | 最终一致性:最终达到一致 |
比喻:
转账:
- CP方式:转账立即成功或失败(强一致性)
- BASE方式:转账可能延迟,但最终会成功(最终一致性)
银行转账就是BASE:
10:00 你转账100元 → 立即扣款
10:05 朋友收到款 → 延迟到账
最终一致 ✅
🎯 分布式事务的6种解决方案
方案1:2PC(两阶段提交)🔄
原理
两个阶段:
- 准备阶段(Prepare):协调者询问参与者是否可以提交
- 提交阶段(Commit):所有参与者都同意,则提交;否则回滚
角色:
- 协调者(Coordinator):事务管理器
- 参与者(Participant):各个服务
流程:
阶段1:准备阶段
┌────────────┐
│ 协调者 │ ── "准备提交" ──→ ┌──────────┐
└────────────┘ │ 参与者A │
↓ └──────────┘
等待所有参与者响应 ↓
↓ 执行事务但不提交
↓ ↓
↓ "准备好了" ✅
↓ ↓
↓ ┌──────────┐
↓ │ 参与者B │
↓ └──────────┘
↓ ↓
↓ 执行事务但不提交
↓ ↓
↓ "准备好了" ✅
↓
所有参与者都准备好 ✅
阶段2:提交阶段
┌────────────┐
│ 协调者 │ ── "提交" ──→ ┌──────────┐
└────────────┘ │ 参与者A │
└──────────┘
↓
COMMIT ✅
┌──────────┐
│ 参与者B │
└──────────┘
↓
COMMIT ✅
代码示例(JTA)
import javax.transaction.TransactionManager;
import javax.transaction.UserTransaction;
@Service
@Slf4j
public class OrderService {
@Autowired
private UserTransaction userTransaction; // JTA事务管理器
@Autowired
private OrderDao orderDao; // 订单数据库
@Autowired
private InventoryDao inventoryDao; // 库存数据库
/**
* 创建订单(2PC)
*/
public void createOrder(Order order) {
try {
// ⭐ 开启分布式事务
userTransaction.begin();
// ⭐ 参与者1:创建订单
orderDao.insert(order);
log.info("订单创建成功: orderId={}", order.getId());
// ⭐ 参与者2:扣减库存
inventoryDao.deduct(order.getProductId(), order.getQuantity());
log.info("库存扣减成功: productId={}", order.getProductId());
// ⭐ 提交事务(2PC)
// 阶段1:准备阶段(询问所有参与者)
// 阶段2:提交阶段(所有参与者提交)
userTransaction.commit();
log.info("分布式事务提交成功");
} catch (Exception e) {
log.error("分布式事务失败,回滚", e);
try {
// ⭐ 回滚事务(所有参与者回滚)
userTransaction.rollback();
} catch (Exception ex) {
log.error("回滚失败", ex);
}
}
}
}
优缺点
优点 ✅:
- 强一致性(ACID)
- 实现简单(JTA提供支持)
缺点 ❌:
- 同步阻塞(等待所有参与者响应)
- 单点故障(协调者挂了,整个系统不可用)
- 性能差(等待时间长)
- 数据库锁时间长(准备阶段到提交阶段)
适用场景:
- 对一致性要求极高
- 并发量不高
- 短事务
方案2:3PC(三阶段提交)🔄🔄
原理
三个阶段(改进2PC):
- CanCommit:询问是否可以提交(不执行事务)
- PreCommit:预提交(执行事务但不提交)
- DoCommit:正式提交
阶段1:CanCommit
协调者 → 参与者:"你能提交吗?"
参与者 → 协调者:"可以" or "不可以"
阶段2:PreCommit
协调者 → 参与者:"预提交"
参与者:执行事务,锁定资源,但不提交
阶段3:DoCommit
协调者 → 参与者:"正式提交"
参与者:COMMIT
改进点:
- 增加超时机制(避免无限等待)
- 阶段1不锁定资源(减少锁时间)
问题:
- 依然有同步阻塞
- 依然有单点故障
- 实现更复杂
结论:实际应用很少,了解即可 ⚠️
方案3:TCC(Try-Confirm-Cancel)⭐⭐⭐
原理
三个阶段:
- Try:尝试执行,预留资源
- Confirm:确认执行,使用预留的资源
- Cancel:取消执行,释放预留的资源
比喻:订酒店
Try:预定房间,冻结房间(不能给别人)
↓
所有服务的Try都成功
↓
Confirm:正式入住,扣款
↓
成功 ✅
或者:
Try:预定房间
↓
某个服务的Try失败
↓
Cancel:取消预定,释放房间
↓
回滚 ✅
代码示例
/**
* 订单服务
*/
@Service
@Slf4j
public class OrderService {
@Autowired
private AccountService accountService; // 账户服务
@Autowired
private InventoryService inventoryService; // 库存服务
/**
* 创建订单(TCC)
*/
public boolean createOrder(Order order) {
try {
// ⭐ Try阶段:预留资源
// 1. 冻结账户金额
boolean accountTry = accountService.tryFreeze(
order.getUserId(), order.getAmount());
if (!accountTry) {
return false;
}
// 2. 冻结库存
boolean inventoryTry = inventoryService.tryFreeze(
order.getProductId(), order.getQuantity());
if (!inventoryTry) {
// 库存冻结失败,取消账户冻结
accountService.cancel(order.getUserId(), order.getAmount());
return false;
}
// 3. 创建订单(状态:待确认)
order.setStatus("PENDING");
orderDao.insert(order);
log.info("Try阶段成功");
// ⭐ Confirm阶段:确认提交
// 1. 扣款(使用冻结的金额)
accountService.confirm(order.getUserId(), order.getAmount());
// 2. 扣减库存(使用冻结的库存)
inventoryService.confirm(order.getProductId(), order.getQuantity());
// 3. 更新订单状态
order.setStatus("CONFIRMED");
orderDao.update(order);
log.info("Confirm阶段成功");
return true;
} catch (Exception e) {
log.error("订单创建失败,执行Cancel", e);
// ⭐ Cancel阶段:取消,释放资源
try {
// 1. 解冻账户
accountService.cancel(order.getUserId(), order.getAmount());
// 2. 解冻库存
inventoryService.cancel(order.getProductId(), order.getQuantity());
// 3. 删除订单
orderDao.delete(order.getId());
log.info("Cancel阶段成功");
} catch (Exception ex) {
log.error("Cancel失败", ex);
}
return false;
}
}
}
账户服务:
@Service
@Slf4j
public class AccountService {
@Autowired
private AccountDao accountDao;
/**
* ⭐ Try:冻结金额
*/
public boolean tryFreeze(Long userId, BigDecimal amount) {
Account account = accountDao.findByUserId(userId);
// 检查余额
if (account.getAvailable().compareTo(amount) < 0) {
log.warn("余额不足: available={}, need={}", account.getAvailable(), amount);
return false;
}
// 冻结金额:可用余额 → 冻结余额
account.setAvailable(account.getAvailable().subtract(amount));
account.setFrozen(account.getFrozen().add(amount));
accountDao.update(account);
log.info("冻结金额成功: userId={}, amount={}", userId, amount);
return true;
}
/**
* ⭐ Confirm:扣款(使用冻结的金额)
*/
public void confirm(Long userId, BigDecimal amount) {
Account account = accountDao.findByUserId(userId);
// 冻结余额 → 扣除
account.setFrozen(account.getFrozen().subtract(amount));
account.setTotal(account.getTotal().subtract(amount));
accountDao.update(account);
log.info("扣款成功: userId={}, amount={}", userId, amount);
}
/**
* ⭐ Cancel:解冻金额
*/
public void cancel(Long userId, BigDecimal amount) {
Account account = accountDao.findByUserId(userId);
// 冻结余额 → 可用余额
account.setFrozen(account.getFrozen().subtract(amount));
account.setAvailable(account.getAvailable().add(amount));
accountDao.update(account);
log.info("解冻金额成功: userId={}, amount={}", userId, amount);
}
}
库存服务:
@Service
@Slf4j
public class InventoryService {
@Autowired
private InventoryDao inventoryDao;
/**
* ⭐ Try:冻结库存
*/
public boolean tryFreeze(Long productId, int quantity) {
Inventory inventory = inventoryDao.findByProductId(productId);
// 检查库存
if (inventory.getAvailable() < quantity) {
log.warn("库存不足: available={}, need={}", inventory.getAvailable(), quantity);
return false;
}
// 冻结库存:可用库存 → 冻结库存
inventory.setAvailable(inventory.getAvailable() - quantity);
inventory.setFrozen(inventory.getFrozen() + quantity);
inventoryDao.update(inventory);
log.info("冻结库存成功: productId={}, quantity={}", productId, quantity);
return true;
}
/**
* ⭐ Confirm:扣减库存(使用冻结的库存)
*/
public void confirm(Long productId, int quantity) {
Inventory inventory = inventoryDao.findByProductId(productId);
// 冻结库存 → 扣除
inventory.setFrozen(inventory.getFrozen() - quantity);
inventory.setTotal(inventory.getTotal() - quantity);
inventoryDao.update(inventory);
log.info("扣减库存成功: productId={}, quantity={}", productId, quantity);
}
/**
* ⭐ Cancel:解冻库存
*/
public void cancel(Long productId, int quantity) {
Inventory inventory = inventoryDao.findByProductId(productId);
// 冻结库存 → 可用库存
inventory.setFrozen(inventory.getFrozen() - quantity);
inventory.setAvailable(inventory.getAvailable() + quantity);
inventoryDao.update(inventory);
log.info("解冻库存成功: productId={}, quantity={}", productId, quantity);
}
}
优缺点
优点 ✅:
- 不依赖数据库的分布式事务(2PC需要)
- 性能较好(无长时间锁)
- 强一致性
缺点 ❌:
- 实现复杂(每个服务都要实现Try/Confirm/Cancel三个方法)
- 对业务侵入性强(需要改造业务代码)
- 幂等性要求高(Confirm/Cancel可能重复调用)
适用场景:
- 对一致性要求高
- 业务允许改造
- 互联网金融等场景 ⭐
方案4:本地消息表 📝
原理
核心思想:利用消息和本地事务保证最终一致性
服务A:
1. 开启本地事务
2. 执行业务操作(如扣款)
3. 插入消息表(状态:待发送)
4. 提交本地事务
↓
定时任务扫描消息表
↓
发送消息到MQ
↓
服务B消费消息
↓
执行业务操作(如加款)
↓
成功后通知服务A
↓
服务A更新消息状态(已发送)
代码示例
订单服务(发送方):
@Service
@Slf4j
public class OrderService {
@Autowired
private OrderDao orderDao;
@Autowired
private MessageDao messageDao; // 本地消息表
@Autowired
private RabbitTemplate rabbitTemplate;
/**
* 创建订单
*/
@Transactional
public void createOrder(Order order) {
// ⭐ 1. 执行业务操作
orderDao.insert(order);
log.info("订单创建成功: orderId={}", order.getId());
// ⭐ 2. 插入本地消息表(同一个本地事务)
LocalMessage message = new LocalMessage();
message.setId(UUID.randomUUID().toString());
message.setContent(JSON.toJSONString(order));
message.setStatus("PENDING"); // 待发送
message.setCreateTime(new Date());
messageDao.insert(message);
log.info("消息保存成功: messageId={}", message.getId());
// ⭐ 3. 提交本地事务(订单和消息同时提交)✅
}
/**
* ⭐ 定时任务:扫描待发送的消息
*/
@Scheduled(fixedRate = 5000) // 每5秒执行一次
public void sendPendingMessages() {
// 查询待发送的消息
List<LocalMessage> messages = messageDao.findByStatus("PENDING");
for (LocalMessage message : messages) {
try {
// 发送到MQ
rabbitTemplate.convertAndSend("order.exchange", "order.created", message.getContent());
// 更新消息状态
message.setStatus("SENT");
message.setSendTime(new Date());
messageDao.update(message);
log.info("消息发送成功: messageId={}", message.getId());
} catch (Exception e) {
log.error("消息发送失败: messageId={}", message.getId(), e);
// 失败了,下次继续重试
}
}
}
}
库存服务(接收方):
@Service
@Slf4j
public class InventoryService {
@Autowired
private InventoryDao inventoryDao;
/**
* ⭐ 消费订单消息
*/
@RabbitListener(queues = "inventory.queue")
public void handleOrderCreated(String messageContent) {
Order order = JSON.parseObject(messageContent, Order.class);
log.info("收到订单消息: orderId={}", order.getId());
try {
// 扣减库存
inventoryDao.deduct(order.getProductId(), order.getQuantity());
log.info("库存扣减成功: productId={}", order.getProductId());
} catch (Exception e) {
log.error("库存扣减失败", e);
// ⭐ 失败后,消息会重新投递(MQ的重试机制)
throw new RuntimeException(e);
}
}
}
优缺点
优点 ✅:
- 实现相对简单
- 不依赖第三方框架
- 性能较好(异步)
缺点 ❌:
- 需要额外的消息表
- 需要定时任务扫描
- 最终一致性(有延迟)
适用场景:
- 对一致性要求不是特别高
- 允许短时间不一致
- 大部分互联网业务 ⭐
方案5:MQ事务消息(RocketMQ)📮
原理
利用MQ的半消息机制:
1. 发送半消息(Half Message)到MQ
↓
2. MQ返回成功
↓
3. 执行本地事务
↓
4. 本地事务成功 → 提交消息(Commit)
本地事务失败 → 回滚消息(Rollback)
↓
5. 消费者消费消息
代码示例
订单服务(发送方):
@Service
@Slf4j
public class OrderService implements RocketMQLocalTransactionListener {
@Autowired
private RocketMQTemplate rocketMQTemplate;
@Autowired
private OrderDao orderDao;
/**
* 创建订单
*/
public void createOrder(Order order) {
// ⭐ 1. 发送事务消息
Message<Order> message = MessageBuilder.withPayload(order).build();
rocketMQTemplate.sendMessageInTransaction(
"order-producer-group",
"OrderTopic:TagA",
message,
order // 传递给本地事务的参数
);
}
/**
* ⭐ 2. 执行本地事务(RocketMQ回调)
*/
@Override
public RocketMQLocalTransactionState executeLocalTransaction(Message msg, Object arg) {
Order order = (Order) arg;
try {
// 执行本地事务
orderDao.insert(order);
log.info("本地事务执行成功: orderId={}", order.getId());
// ⭐ 返回COMMIT,消息会被消费者消费
return RocketMQLocalTransactionState.COMMIT;
} catch (Exception e) {
log.error("本地事务执行失败", e);
// ⭐ 返回ROLLBACK,消息会被删除
return RocketMQLocalTransactionState.ROLLBACK;
}
}
/**
* ⭐ 3. 事务回查(MQ定期回查事务状态)
*/
@Override
public RocketMQLocalTransactionState checkLocalTransaction(Message msg) {
// 从数据库查询订单是否存在
Order order = JSON.parseObject(new String((byte[]) msg.getPayload()), Order.class);
Order dbOrder = orderDao.findById(order.getId());
if (dbOrder != null) {
// 订单存在,提交消息
return RocketMQLocalTransactionState.COMMIT;
} else {
// 订单不存在,回滚消息
return RocketMQLocalTransactionState.ROLLBACK;
}
}
}
库存服务(接收方):
@Service
@Slf4j
@RocketMQMessageListener(
topic = "OrderTopic",
consumerGroup = "inventory-consumer-group"
)
public class InventoryService implements RocketMQListener<Order> {
@Autowired
private InventoryDao inventoryDao;
/**
* ⭐ 消费消息
*/
@Override
public void onMessage(Order order) {
log.info("收到订单消息: orderId={}", order.getId());
try {
// 扣减库存
inventoryDao.deduct(order.getProductId(), order.getQuantity());
log.info("库存扣减成功");
} catch (Exception e) {
log.error("库存扣减失败", e);
// ⭐ 抛异常,MQ会重试
throw new RuntimeException(e);
}
}
}
优缺点
优点 ✅:
- 实现简单(MQ封装了复杂逻辑)
- 性能好(异步)
- 可靠性高(MQ保证)
缺点 ❌:
- 依赖RocketMQ(Kafka不支持)
- 最终一致性(有延迟)
适用场景:
- 使用RocketMQ的项目
- 对一致性要求不是特别高
- 互联网业务 ⭐⭐⭐
方案6:Saga模式 📜
原理
Saga = 长事务拆分成多个短事务 + 补偿机制
正向操作:T1 → T2 → T3 → ... → Tn
补偿操作:C1 ← C2 ← C3 ← ... ← Cn
场景1:全部成功
T1 成功 → T2 成功 → T3 成功 ✅
场景2:T3失败
T1 成功 → T2 成功 → T3 失败 ❌
↓
C2补偿 ← C1补偿 ← 回滚完成 ✅
代码示例
@Service
@Slf4j
public class OrderSagaService {
@Autowired
private OrderService orderService;
@Autowired
private AccountService accountService;
@Autowired
private InventoryService inventoryService;
/**
* Saga模式:创建订单
*/
public boolean createOrderSaga(Order order) {
boolean step1 = false;
boolean step2 = false;
boolean step3 = false;
try {
// ⭐ 步骤1:创建订单
step1 = orderService.createOrder(order);
if (!step1) {
throw new Exception("创建订单失败");
}
log.info("✅ 步骤1:创建订单成功");
// ⭐ 步骤2:扣款
step2 = accountService.deduct(order.getUserId(), order.getAmount());
if (!step2) {
throw new Exception("扣款失败");
}
log.info("✅ 步骤2:扣款成功");
// ⭐ 步骤3:扣减库存
step3 = inventoryService.deduct(order.getProductId(), order.getQuantity());
if (!step3) {
throw new Exception("扣减库存失败");
}
log.info("✅ 步骤3:扣减库存成功");
return true;
} catch (Exception e) {
log.error("Saga事务失败,开始补偿", e);
// ⭐ 补偿:按相反顺序回滚
if (step3) {
// 补偿步骤3:恢复库存
inventoryService.compensateAdd(order.getProductId(), order.getQuantity());
log.info("补偿:恢复库存");
}
if (step2) {
// 补偿步骤2:退款
accountService.compensateAdd(order.getUserId(), order.getAmount());
log.info("补偿:退款");
}
if (step1) {
// 补偿步骤1:删除订单
orderService.compensateCancel(order.getId());
log.info("补偿:删除订单");
}
return false;
}
}
}
优缺点
优点 ✅:
- 适合长事务(T1→T2→...→Tn)
- 不需要锁资源
- 性能好
缺点 ❌:
- 实现复杂(需要为每个操作编写补偿逻辑)
- 不保证隔离性(中间状态可见)
- 补偿逻辑复杂
适用场景:
- 长流程业务(如订单流程)
- 对性能要求高
- 可以容忍中间状态
📊 六种方案对比
| 方案 | 一致性 | 性能 | 复杂度 | 适用场景 |
|---|---|---|---|---|
| 2PC | ⭐⭐⭐ 强一致 | ⭐ 差 | ⭐⭐ 中 | 短事务、低并发 |
| 3PC | ⭐⭐⭐ 强一致 | ⭐ 差 | ⭐⭐⭐ 高 | 很少用 |
| TCC | ⭐⭐⭐ 强一致 | ⭐⭐ 好 | ⭐⭐⭐ 高 | 金融、支付 |
| 本地消息表 | ⭐⭐ 最终一致 | ⭐⭐⭐ 好 | ⭐⭐ 中 | 互联网业务 |
| MQ事务消息 | ⭐⭐ 最终一致 | ⭐⭐⭐ 好 | ⭐ 低 | 推荐 ⭐⭐⭐ |
| Saga | ⭐⭐ 最终一致 | ⭐⭐⭐ 好 | ⭐⭐⭐ 高 | 长流程业务 |
🎓 面试题速答
Q1: 分布式事务有哪些解决方案?
A: 六种方案:
- 2PC:两阶段提交,强一致性但性能差
- 3PC:三阶段提交,改进2PC但很少用
- TCC:Try-Confirm-Cancel,强一致性,适合金融
- 本地消息表:最终一致性,实现简单
- MQ事务消息:RocketMQ支持,推荐 ⭐⭐⭐
- Saga:长事务拆分+补偿,适合长流程
最常用:MQ事务消息(RocketMQ)
Q2: 什么是2PC?有什么问题?
A: 2PC = 两阶段提交
两个阶段:
- 准备阶段:协调者询问所有参与者是否可以提交
- 提交阶段:所有参与者都同意,则提交;否则回滚
问题:
- 同步阻塞(等待所有参与者响应)
- 单点故障(协调者挂了)
- 性能差(锁资源时间长)
Q3: TCC是什么?如何实现?
A: TCC = Try-Confirm-Cancel
三个阶段:
- Try:尝试执行,预留资源(如冻结余额)
- Confirm:确认执行,使用预留的资源(如扣款)
- Cancel:取消执行,释放预留的资源(如解冻)
示例:
// Try:冻结余额
account.setAvailable(account.getAvailable().subtract(amount));
account.setFrozen(account.getFrozen().add(amount));
// Confirm:扣款
account.setFrozen(account.getFrozen().subtract(amount));
account.setTotal(account.getTotal().subtract(amount));
// Cancel:解冻
account.setFrozen(account.getFrozen().subtract(amount));
account.setAvailable(account.getAvailable().add(amount));
Q4: MQ事务消息如何保证一致性?
A: 原理:半消息机制
1. 发送半消息到MQ(消费者看不到)
2. 执行本地事务
3. 本地事务成功 → 提交消息(消费者可以消费)
本地事务失败 → 回滚消息(删除)
4. MQ定期回查事务状态
关键点:
- 半消息:消费者看不到
- 事务回查:MQ主动查询本地事务状态
- 保证本地事务和消息发送的一致性 ✅
Q5: 如何选择分布式事务方案?
A: 根据场景选择:
| 场景 | 推荐方案 | 理由 |
|---|---|---|
| 金融支付 | TCC | 强一致性 ⭐⭐⭐ |
| 电商订单 | MQ事务消息 | 最终一致性,性能好 ⭐⭐⭐ |
| 长流程业务 | Saga | 适合长事务 ⭐⭐ |
| 低并发业务 | 2PC | 简单,强一致 ⭐ |
| 简单业务 | 本地消息表 | 实现简单 ⭐⭐ |
推荐:MQ事务消息(RocketMQ)⭐⭐⭐
Q6: 强一致性和最终一致性的区别?
A: 强一致性(CP):
- 所有操作立即生效
- 读到的都是最新数据
- 例如:2PC、TCC
最终一致性(BASE):
- 操作可能有延迟
- 短时间内可能不一致
- 最终会达到一致
- 例如:MQ事务消息、本地消息表
选择:
- 金融交易:强一致性 ⭐
- 互联网业务:最终一致性 ⭐⭐⭐
🎬 总结
分布式事务解决方案
┌────────────────────────────────────────┐
│ 2PC:两阶段提交 │
│ 强一致性,性能差 ⭐ │
└────────────────────────────────────────┘
┌────────────────────────────────────────┐
│ TCC:Try-Confirm-Cancel │
│ 强一致性,适合金融 ⭐⭐ │
└────────────────────────────────────────┘
┌────────────────────────────────────────┐
│ MQ事务消息(推荐)⭐⭐⭐ │
│ 最终一致性,性能好 │
└────────────────────────────────────────┘
┌────────────────────────────────────────┐
│ 本地消息表 │
│ 最终一致性,实现简单 ⭐⭐ │
└────────────────────────────────────────┘
┌────────────────────────────────────────┐
│ Saga:长事务拆分+补偿 │
│ 最终一致性,适合长流程 ⭐⭐ │
└────────────────────────────────────────┘
大部分场景用MQ事务消息!✅
🎉 恭喜你!
你已经完全掌握了分布式事务的6种解决方案!🎊
核心要点:
- 2PC:强一致,性能差
- TCC:强一致,适合金融
- MQ事务消息:最终一致,推荐 ⭐⭐⭐
- 本地消息表:最终一致,简单
- Saga:长事务,补偿机制
下次面试,这样回答:
"分布式事务有6种解决方案,其中最常用的是MQ事务消息。
MQ事务消息利用RocketMQ的半消息机制,先发送半消息到MQ,执行本地事务,成功则提交消息,失败则回滚消息。MQ还会定期回查事务状态,保证本地事务和消息发送的一致性。
对于金融等对一致性要求极高的场景,会使用TCC方案,分为Try(预留资源)、Confirm(确认提交)、Cancel(取消释放)三个阶段,实现强一致性。
我们项目的订单系统使用RocketMQ事务消息,订单创建成功后发送消息,库存服务消费消息扣减库存,实现最终一致性,性能和可靠性都很好。"
面试官:👍 "很好!你对分布式事务理解很全面!"
本文完 🎬
上一篇: 197-Zookeeper实现分布式锁的原理.md
下一篇: 199-Seata分布式事务框架.md
作者注:写完这篇,我都想去银行当转账员了!💰
如果这篇文章对你有帮助,请给我一个Star⭐!