漫畫分布式事務技術選型
🎯 學習目標:掌握架構師核心技能——分布式事務技術選型與一致性解決方案,構建高可靠的分布式系統
🎭 第一章:分布式事務模式對比
🤔 2PC vs 3PC vs TCC vs Saga
想象分布式事務就像不同的團隊協作方式…
🎭 分布式事務協作模式:2PC (二階段提交):
┌─────────────────────────────────────┐
│ 協調者 (Coordinator) │
│ │ │
│ ┌─────────┼─────────┐ │
│ ▼ ▼ ▼ │
│ 參與者A 參與者B 參與者C │
│ (準備) (準備) (準備) │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ (提交) (提交) (提交) │
│ │
│ 特點: │
│ ? 強一致性保證 │
│ ? 阻塞性問題 │
│ ? 單點故障風險 │
│ ? 適用:小規模關鍵業務 │
└─────────────────────────────────────┘TCC (Try-Confirm-Cancel):
┌─────────────────────────────────────┐
│ 業務活動管理器 │
│ │ │
│ ┌─────────┼─────────┐ │
│ ▼ ▼ ▼ │
│ 服務A-Try 服務B-Try 服務C-Try │
│ (資源預留) (資源預留) (資源預留) │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ Confirm Confirm Confirm │
│ (確認) (確認) (確認) │
│ │
│ 特點: │
│ ? 業務侵入性強 │
│ ? 最終一致性 │
│ ? 性能較好 │
│ ? 適用:業務可拆分場景 │
└─────────────────────────────────────┘Saga (長事務):
┌─────────────────────────────────────┐
│ Saga編排器 │
│ │ │
│ T1 → T2 → T3 → T4 → T5 │
│ │ │ │ │ │ │
│ C1 ← C2 ← C3 ← C4 ← C5 │
│ (事務) (補償) │
│ │
│ 特點: │
│ ? 長事務友好 │
│ ? 業務補償邏輯 │
│ ? 最終一致性 │
│ ? 適用:復雜業務流程 │
└─────────────────────────────────────┘
📊 分布式事務技術詳細對比
📊 分布式事務技術對比:┌─────────────┬─────────────┬─────────────┬─────────────┐
│ 特性 │ 2PC │ TCC │ Saga │
├─────────────┼─────────────┼─────────────┼─────────────┤
│ 🔒 一致性 │ 強一致 │ 最終一致 │ 最終一致 │
│ 🚀 性能 │ 低 │ 中 │ 高 │
│ 🔧 復雜度 │ 簡單 │ 高 │ 中 │
│ 📊 吞吐量 │ 低 │ 中 │ 高 │
│ 🛡? 可靠性 │ 中等 │ 高 │ 高 │
│ 💻 業務侵入 │ 小 │ 大 │ 中 │
│ 🌐 適用場景 │ 關鍵業務 │ 核心交易 │ 復雜流程 │
│ 🎯 推薦度 │ 低 │ 高 │ 高 │
└─────────────┴─────────────┴─────────────┴─────────────┘技術選型建議:
🎯 銀行轉賬系統 → TCC (資金安全)
🎯 電商下單流程 → Saga (流程復雜)
🎯 庫存扣減場景 → TCC (原子操作)
🎯 數據同步任務 → Saga (長時間運行)
🎯 支付系統 → TCC (強一致性要求)
🎯 分布式事務選型決策樹
🎯 分布式事務選型流程:開始選型│┌────▼────┐│一致性要求│└────┬────┘│┌──────────────┼──────────────┐▼ ▼ ▼強一致性 最終一致性 高性能要求│ │ │┌────▼────┐ ┌────▼────┐ ┌────▼────┐│關鍵業務 │ │復雜流程 │ │高并發 │└────┬────┘ └────┬────┘ └────┬────┘│ │ │▼ ▼ ▼XA/2PC事務 Saga模式 異步消息(小規模場景) (長事務場景) (最終一致)重要業務場景選型:
┌─────────────────┬─────────────┬─────────────┐
│ 業務場景 │ 推薦方案 │ 備選方案 │
├─────────────────┼─────────────┼─────────────┤
│ 銀行轉賬 │ TCC │ XA │
│ 電商下單 │ Saga │ 本地消息表 │
│ 庫存扣減 │ TCC │ 分布式鎖 │
│ 訂單支付 │ TCC │ 2PC │
│ 數據同步 │ Saga │ 消息隊列 │
│ 批量處理 │ Saga │ 狀態機 │
└─────────────────┴─────────────┴─────────────┘
🛠? 第二章:Seata分布式事務框架
🏗? Seata架構與模式
// 🛠? Seata分布式事務實戰/*** Seata配置中心*/
@Configuration
@EnableAutoDataSourceProxy
public class SeataConfig {/*** AT模式數據源代理*/@Bean@Primarypublic DataSourceProxy dataSourceProxy(DataSource dataSource) {return new DataSourceProxy(dataSource);}/*** TCC模式業務資源管理器*/@Beanpublic TccTransactionManager tccTransactionManager() {return new TccTransactionManager();}/*** Saga狀態機配置*/@Beanpublic StateMachineEngine stateMachineEngine() {StateMachineEngineBuilder builder = StateMachineEngineBuilder.newBuilder();builder.setDataSource(dataSource()).setTransactionManager(platformTransactionManager()).setApplicationContext(applicationContext);return builder.build();}
}/*** AT模式 - 自動補償*/
@Service
@Slf4j
public class OrderServiceAT {@Autowiredprivate OrderRepository orderRepository;@Autowiredprivate AccountServiceClient accountServiceClient;@Autowiredprivate ProductServiceClient productServiceClient;/*** 創建訂單 - AT模式全局事務*/@GlobalTransactional(name = "create-order", rollbackFor = Exception.class)public Order createOrder(CreateOrderRequest request) {log.info("開始創建訂單: userId={}", request.getUserId());try {// 1. 創建訂單記錄Order order = new Order();order.setUserId(request.getUserId());order.setProductId(request.getProductId());order.setQuantity(request.getQuantity());order.setAmount(request.getAmount());order.setStatus(OrderStatus.PENDING);order.setCreateTime(LocalDateTime.now());order = orderRepository.save(order);log.info("訂單創建成功: orderId={}", order.getId());// 2. 扣減賬戶余額accountServiceClient.deductBalance(request.getUserId(), request.getAmount());log.info("賬戶扣款成功: userId={}, amount={}", request.getUserId(), request.getAmount());// 3. 扣減商品庫存productServiceClient.reduceStock(request.getProductId(), request.getQuantity());log.info("庫存扣減成功: productId={}, quantity={}", request.getProductId(), request.getQuantity());// 4. 更新訂單狀態order.setStatus(OrderStatus.SUCCESS);orderRepository.save(order);log.info("訂單處理完成: orderId={}", order.getId());return order;} catch (Exception e) {log.error("訂單創建失敗: userId={}", request.getUserId(), e);throw new OrderException("訂單創建失敗: " + e.getMessage(), e);}}
}/*** TCC模式 - 手動補償*/
@LocalTCC
@Service
@Slf4j
public class AccountServiceTCC {@Autowiredprivate AccountRepository accountRepository;@Autowiredprivate AccountFreezeRepository freezeRepository;/*** Try階段 - 凍結資金*/@TwoPhaseBusinessAction(name = "deductBalance",commitMethod = "confirmDeduct",rollbackMethod = "cancelDeduct")public boolean deductBalance(BusinessActionContext context,@BusinessActionContextParameter("userId") Long userId,@BusinessActionContextParameter("amount") BigDecimal amount) {String xid = context.getXid();log.info("TCC Try階段 - 凍結資金: xid={}, userId={}, amount={}", xid, userId, amount);try {// 1. 檢查賬戶余額Account account = accountRepository.findByUserId(userId);if (account == null) {throw new AccountException("賬戶不存在");}if (account.getBalance().compareTo(amount) < 0) {throw new InsufficientBalanceException("余額不足");}// 2. 凍結資金AccountFreeze freeze = new AccountFreeze();freeze.setXid(xid);freeze.setUserId(userId);freeze.setAmount(amount);freeze.setStatus(FreezeStatus.TRYING);freeze.setCreateTime(LocalDateTime.now());freezeRepository.save(freeze);// 3. 扣減可用余額account.setBalance(account.getBalance().subtract(amount));account.setFrozenAmount(account.getFrozenAmount().add(amount));accountRepository.save(account);log.info("資金凍結成功: xid={}, userId={}, amount={}", xid, userId, amount);return true;} catch (Exception e) {log.error("資金凍結失敗: xid={}, userId={}, amount={}", xid, userId, amount, e);return false;}}/*** Confirm階段 - 確認扣款*/public boolean confirmDeduct(BusinessActionContext context) {String xid = context.getXid();Long userId = (Long) context.getActionContext("userId");BigDecimal amount = (BigDecimal) context.getActionContext("amount");log.info("TCC Confirm階段 - 確認扣款: xid={}, userId={}, amount={}", xid, userId, amount);try {// 1. 查找凍結記錄AccountFreeze freeze = freezeRepository.findByXid(xid);if (freeze == null) {log.warn("凍結記錄不存在: xid={}", xid);return true; // 冪等性處理}if (freeze.getStatus() == FreezeStatus.CONFIRMED) {log.warn("重復確認: xid={}", xid);return true; // 冪等性處理}// 2. 確認扣款freeze.setStatus(FreezeStatus.CONFIRMED);freeze.setUpdateTime(LocalDateTime.now());freezeRepository.save(freeze);// 3. 減少凍結金額Account account = accountRepository.findByUserId(userId);account.setFrozenAmount(account.getFrozenAmount().subtract(amount));accountRepository.save(account);log.info("扣款確認完成: xid={}, userId={}, amount={}", xid, userId, amount);return true;} catch (Exception e) {log.error("扣款確認失敗: xid={}, userId={}, amount={}", xid, userId, amount, e);return false;}}/*** Cancel階段 - 取消扣款*/public boolean cancelDeduct(BusinessActionContext context) {String xid = context.getXid();Long userId = (Long) context.getActionContext("userId");BigDecimal amount = (BigDecimal) context.getActionContext("amount");log.info("TCC Cancel階段 - 取消扣款: xid={}, userId={}, amount={}", xid, userId, amount);try {// 1. 查找凍結記錄AccountFreeze freeze = freezeRepository.findByXid(xid);if (freeze == null) {log.warn("凍結記錄不存在: xid={}", xid);return true; // 冪等性處理}if (freeze.getStatus() == FreezeStatus.CANCELLED) {log.warn("重復取消: xid={}", xid);return true; // 冪等性處理}// 2. 取消凍結freeze.setStatus(FreezeStatus.CANCELLED);freeze.setUpdateTime(LocalDateTime.now());freezeRepository.save(freeze);// 3. 恢復資金Account account = accountRepository.findByUserId(userId);account.setBalance(account.getBalance().add(amount));account.setFrozenAmount(account.getFrozenAmount().subtract(amount));accountRepository.save(account);log.info("扣款取消完成: xid={}, userId={}, amount={}", xid, userId, amount);return true;} catch (Exception e) {log.error("扣款取消失敗: xid={}, userId={}, amount={}", xid, userId, amount, e);return false;}}
}/*** Saga模式 - 狀態機編排*/
@Component
@Slf4j
public class OrderSagaService {@Autowiredprivate StateMachineEngine stateMachineEngine;/*** 執行訂單處理Saga*/public void processOrderSaga(CreateOrderRequest request) {log.info("開始執行訂單Saga: userId={}", request.getUserId());try {// 創建狀態機實例StateMachineInstance instance = stateMachineEngine.startAsync("orderProcessSaga",UUID.randomUUID().toString(),createSagaContext(request));log.info("訂單Saga啟動成功: instanceId={}", instance.getId());} catch (Exception e) {log.error("訂單Saga啟動失敗: userId={}", request.getUserId(), e);throw new SagaException("Saga執行失敗: " + e.getMessage(), e);}}private Map<String, Object> createSagaContext(CreateOrderRequest request) {Map<String, Object> context = new HashMap<>();context.put("userId", request.getUserId());context.put("productId", request.getProductId());context.put("quantity", request.getQuantity());context.put("amount", request.getAmount());context.put("orderId", null); // 將在狀態流轉中設置return context;}/*** Saga狀態定義*/@SagaOrchestrationStartpublic void startOrderProcess(SagaTransactionContext context) {log.info("Saga開始 - 訂單處理: userId={}", context.getVariable("userId"));}@SagaOrchestrationTask("createOrder")public void createOrder(SagaTransactionContext context) {log.info("Saga步驟1 - 創建訂單");// 創建訂單邏輯Order order = orderService.createOrderLocal((Long) context.getVariable("userId"),(Long) context.getVariable("productId"),(Integer) context.getVariable("quantity"),(BigDecimal) context.getVariable("amount"));context.setVariable("orderId", order.getId());log.info("訂單創建成功: orderId={}", order.getId());}@SagaOrchestrationTask("deductBalance")public void deductBalance(SagaTransactionContext context) {log.info("Saga步驟2 - 扣減余額");accountService.deductBalanceLocal((Long) context.getVariable("userId"),(BigDecimal) context.getVariable("amount"));log.info("余額扣減成功");}@SagaOrchestrationTask("reduceStock")public void reduceStock(SagaTransactionContext context) {log.info("Saga步驟3 - 扣減庫存");productService.reduceStockLocal((Long) context.getVariable("productId"),(Integer) context.getVariable("quantity"));log.info("庫存扣減成功");}@SagaOrchestrationTask("updateOrderStatus")public void updateOrderStatus(SagaTransactionContext context) {log.info("Saga步驟4 - 更新訂單狀態");orderService.updateOrderStatusLocal((Long) context.getVariable("orderId"),OrderStatus.SUCCESS);log.info("訂單狀態更新成功");}// 補償方法@SagaOrchestrationCompensation("createOrder")public void compensateCreateOrder(SagaTransactionContext context) {log.info("Saga補償 - 取消訂單");Long orderId = (Long) context.getVariable("orderId");if (orderId != null) {orderService.cancelOrderLocal(orderId);log.info("訂單取消完成: orderId={}", orderId);}}@SagaOrchestrationCompensation("deductBalance")public void compensateDeductBalance(SagaTransactionContext context) {log.info("Saga補償 - 恢復余額");accountService.refundBalanceLocal((Long) context.getVariable("userId"),(BigDecimal) context.getVariable("amount"));log.info("余額恢復完成");}@SagaOrchestrationCompensation("reduceStock")public void compensateReduceStock(SagaTransactionContext context) {log.info("Saga補償 - 恢復庫存");productService.restoreStockLocal((Long) context.getVariable("productId"),(Integer) context.getVariable("quantity"));log.info("庫存恢復完成");}
}
🎯 面試重點總結
💡 分布式事務架構師面試題
1. 事務模式選擇
Q: 什么場景選擇TCC?什么場景選擇Saga?
A:
- TCC適用場景:資金交易、庫存扣減等需要強一致性的核心業務
- Saga適用場景:訂單流程、批處理等長事務業務流程
- 選擇依據:一致性要求、業務復雜度、性能要求
2. 分布式事務設計
Q: 如何設計一個高可用的分布式事務系統?
A:
- 冪等性設計:防重試、防重復提交
- 補償機制:業務補償、技術補償
- 監控告警:事務狀態監控、異常告警
- 容錯處理:超時重試、人工介入
3. 性能優化
Q: 分布式事務性能優化策略?
A:
- 異步處理:非關鍵步驟異步執行
- 批量操作:減少網絡開銷
- 緩存優化:減少數據庫訪問
- 分片策略:避免熱點數據
🎖? 分布式事務架構師核心能力
- 方案選型能力:根據業務特點選擇合適的事務模式
- 系統設計能力:設計高可用、高性能的事務系統
- 問題診斷能力:快速定位和解決分布式事務問題
- 優化調優能力:持續優化事務系統性能
🔄 分布式事務是分布式系統的核心挑戰!掌握技術選型與實現原理,構建高可靠的分布式事務系統!
📌 行動指南
- 點贊 → 讓更多Java開發者掌握分布式技術
- 評論 → 留言"分布式技術"領取[分布式系統設計模板]
- 關注 → 追蹤更新《更多Java技術精彩內容》(已寫完待發布)
- 贊賞 → 解鎖完整源碼+專屬技術咨詢
🚀 下期預告
《更多Java技術精彩內容》關注可搶先看
📚 相關推薦:
- 漫畫Java基礎
- 漫畫Spring全家桶
- 漫畫JVM調優
讓我們一起在Java的世界里探索更多精彩內容! 🚀