From fafba719fa3abf3cf400c6aca8fc1c29109fd6d9 Mon Sep 17 00:00:00 2001 From: yasinshaw Date: Sat, 28 Feb 2026 20:50:13 +0800 Subject: [PATCH] feat: add distributed transaction Q&A --- questions/distributed-transaction.md | 298 +++++++++++++++++++++++++++ 1 file changed, 298 insertions(+) create mode 100644 questions/distributed-transaction.md diff --git a/questions/distributed-transaction.md b/questions/distributed-transaction.md new file mode 100644 index 0000000..5f2868d --- /dev/null +++ b/questions/distributed-transaction.md @@ -0,0 +1,298 @@ +# 分布式事务 + +## 问题 + +**背景**:在微服务架构中,我们经常遇到跨服务的数据一致性问题。 + +**问题**: +1. 请描述分布式事务的常见解决方案(至少 3 种) +2. 它们的优缺点和适用场景是什么? +3. 你在实际项目中是如何选择的?有没有遇到过什么坑? + +--- + +## 标准答案 + +### 1. 常见解决方案 + +#### **方案一:2PC (Two-Phase Commit,两阶段提交)** + +**原理**: +- 准备阶段:协调者询问所有参与者是否可以提交 +- 提交阶段:如果所有参与者都回复"可以",则发送提交指令;否则发送回滚指令 + +**优点**: +- 强一致性 +- 原理简单,易于理解 + +**缺点**: +- **同步阻塞**:所有参与者在事务提交前都处于阻塞状态 +- **单点故障**:协调者故障会导致参与者一直阻塞 +- **数据不一致**:在第二阶段,部分节点收到提交指令,部分未收到 + +**适用场景**: +- 传统关系型数据库(XA 协议) +- 对一致性要求极高,可接受性能损耗的场景 + +**实际应用**: +- MySQL XA 事务 +- Java JTA (Java Transaction API) + +--- + +#### **方案二:3PC (Three-Phase Commit,三阶段提交)** + +**原理**: +在 2PC 基础上增加 CanCommit 阶段: +1. CanCommit:协调者询问参与者是否可以执行 +2. PreCommit:参与者预执行并回复 +3. DoCommit:正式提交 + +**优点**: +- 相比 2PC 减少了阻塞时间 +- 引入超时机制,参与者可以自动决策 + +**缺点**: +- 仍然存在数据不一致风险 +- 协议更复杂,实现成本高 +- 性能提升有限 + +**适用场景**: +- 很少在实际生产中使用,更多是理论意义 + +--- + +#### **方案三:TCC (Try-Confirm-Cancel,补偿事务)** + +**原理**: +- **Try 阶段**:尝试执行业务,完成资源的检查和预留 +- **Confirm 阶段**:确认执行业务,使用 Try 阶段预留的资源 +- **Cancel 阶段**:取消执行业务,释放 Try 阶段预留的资源 + +**代码示例**: +```java +// Try 阶段 +public boolean try() { + // 检查账户余额 + // 冻结相应金额(预留资源) + // return true/false +} + +// Confirm 阶段 +public boolean confirm() { + // 扣除冻结金额 + // 真正完成转账 +} + +// Cancel 阶段 +public boolean cancel() { + // 释放冻结金额 + // 恢复原始状态 +} +``` + +**优点**: +- **最终一致性** +- 性能较好,相比 2PC 没有长时间锁资源 +- 业务可控性强 + +**缺点**: +- **代码侵入性强**:每个业务都需要写三个接口 +- **开发成本高**:需要考虑各种异常情况 +- **容易遗漏**:Cancel 接口如果实现不完整会导致资源泄露 + +**适用场景**: +- 对性能有一定要求 +- 业务逻辑清晰,可以拆分成 Try/Confirm/Cancel +- 高并发场景 + +**实际应用**: +- 阿里巴巴 Seata 的 TCC 模式 +- 支付系统、订单系统 + +--- + +#### **方案四:本地消息表(异步确保)** + +**原理**: +1. 上游服务在同一本地事务中: + - 完成业务操作 + - 存储一条消息到本地消息表(状态为"待发送") +2. 定时任务扫描消息表,发送消息到 MQ +3. 下游服务消费 MQ,执行业务逻辑 +4. 下游服务成功后通知上游更新消息状态 + +**优点**: +- 实现简单 +- 可靠性高(消息持久化) +- 支持重试 + +**缺点**: +- 需要维护本地消息表 +- 定时任务有延迟 +- 需要处理消息重复消费(幂等性) + +**适用场景**: +- 可以接受最终一致性 +- 对实时性要求不高 +- 高并发场景 + +**实际应用**: +- 支付宝到账通知 +- 订单创建后的物流通知 + +--- + +#### **方案五:MQ 事务消息(RocketMQ 方案)** + +**原理**: +1. 发送半消息(Half Message)到 MQ(消息对消费者不可见) +2. 执行本地事务 +3. 提交/回滚消息: + - 本地事务成功 → 提交消息(消息对消费者可见) + - 本地事务失败 → 删除消息 +4. MQ 提供反查机制:如果长时间未收到确认,主动查询业务方事务状态 + +**优点**: +- 解耦性强 +- 性能好 +- 支持大规模分布式事务 + +**缺点**: +- 依赖特定 MQ(如 RocketMQ) +- 需要实现反查接口 +- 消息可能有延迟 + +**适用场景**: +- 高并发、大规模分布式系统 +- 可以接受最终一致性 +- 需要解耦上下游服务 + +**实际应用**: +- RocketMQ 事务消息 +- 双11 大促场景 + +--- + +#### **方案六:Saga 模式** + +**原理**: +将长事务拆分为多个本地短事务,每个短事务都有对应的补偿操作: +- 正向操作:T1, T2, T3, ..., Tn +- 补偿操作:Cn, ..., C3, C2, C1(反向补偿) + +**示例**: +``` +预订行程 Saga: +1. 预订航班 (T1) +2. 预订酒店 (T2) +3. 预订租车 (T3) + +如果 T2 失败: +1. 取消航班 (C1) +2. 返回失败给用户 +``` + +**优点**: +- 适合长事务、业务流程复杂的场景 +- 最终一致性 +- 可以跨多个服务 + +**缺点**: +- 需要为每个操作设计补偿逻辑 +- 补偿操作可能失败,需要处理 +- 无法保证隔离性(脏读问题) + +**适用场景**: +- 业务流程长、涉及多个服务 +- 旅行预订、电商下单 +- 微服务编排 + +**实际应用**: +- Apache ServiceComb Saga +- Netflix Conductor + +--- + +### 2. 方案对比总结 + +| 方案 | 一致性 | 性能 | 复杂度 | 适用场景 | +|------|--------|------|--------|----------| +| 2PC | 强一致性 | 低(同步阻塞) | 低 | 传统数据库 | +| 3PC | 强一致性 | 中 | 中 | 很少使用 | +| TCC | 最终一致性 | 高 | 高(业务侵入) | 高并发、强业务控制 | +| 本地消息表 | 最终一致性 | 中 | 低 | 高可靠性、可接受延迟 | +| MQ 事务消息 | 最终一致性 | 高 | 中 | 大规模、高并发、解耦 | +| Saga | 最终一致性 | 高 | 高(补偿逻辑) | 长事务、业务编排 | + +--- + +### 3. 实际项目选择建议 + +**选择决策树**: +``` +是否需要强一致性? +├─ 是 → 2PC(XA 事务) +└─ 否 → 最终一致性 + │ + ├─ 业务可以拆分为 Try/Confirm/Cancel? + │ ├─ 是 → TCC(高并发、强控制) + │ └─ 否 → 继续判断 + │ + ├─ 使用 RocketMQ? + │ ├─ 是 → MQ 事务消息 + │ └─ 否 → 继续判断 + │ + ├─ 业务流程长、涉及多服务? + │ ├─ 是 → Saga + │ └─ 否 → 本地消息表 +``` + +**常见坑和注意事项**: + +1. **幂等性问题**(所有方案都需要考虑) + - 重复请求导致的重复扣款、重复发货 + - 解决:使用唯一业务 ID、Redis 分布式锁 + +2. **空补偿问题**(TCC) + ```java + // Cancel 被调用时,Try 可能还没执行 + public void cancel() { + // 需要检查是否有冻结记录 + if (没有冻结记录) { + return; // 空补偿,直接返回 + } + // 执行取消逻辑 + } + ``` + +3. **悬挂问题**(TCC) + - Confirm 比 Cancel 先到 + - 解决:记录事务状态,拒绝后续操作 + +4. **消息丢失**(MQ 方案) + - 网络抖动导致消息丢失 + - 解决:ACK 机制 + 重试 + 死信队列 + +5. **资源锁定时间**(2PC) + - 长时间锁资源导致性能下降 + - 解决:控制事务规模,拆分大事务 + +--- + +### 4. 阿里 P7 加分项 + +**实际项目经验**: +- 设计并实现过千万级用户的分布式事务系统 +- 处理过分布式事务的性能瓶颈(如连接池优化、并发度控制) +- 有 TCC/Saga 的踩坑经验和解决方案 + +**深度理解**: +- 理解 CAP 理论在实际场景中的权衡 +- 能根据业务特点选择合适的一致性级别 +- 有监控和告警体系,能快速定位分布式事务问题 + +**架构能力**: +- 能设计支持多种分布式事务模式的统一框架 +- 考虑降级和熔断策略 +- 有混沌工程实践(注入故障测试系统恢复能力)