分布式事務問題的7種常見解決方案
前言
分布式事務問題,無論在面試,還是工作中經常會遇到。
分布式系統下,數據一致性不再是數據庫事務那么簡單的。
分布式事務作為其中最復雜的挑戰之一,曾讓無數團隊深夜加班、焦頭爛額。
今天這篇文章就跟大家一起聊聊分布式事務問題的7種常見解決方案,希望對你會有所幫助。

1.為什么分布式事務如此棘手?
在單體應用時代,數據庫的ACID事務保證了數據一致性。
但在微服務架構下,一個業務操作需要跨多個服務、多個數據庫,傳統事務模型不再適用。
想象一下電商下單場景:
- 訂單服務創建訂單(訂單數據庫)
- 庫存服務扣減庫存(庫存數據庫)
- 支付服務處理支付(支付數據庫)
- 積分服務增加積分(積分數據庫)
這四個操作要么全部成功,要么全部失敗。
這就是分布式事務要解決的核心問題。
那么,如何解決問題呢?
2. 常見的解決方案
2.1 2PC(兩階段提交)
該方案是強一致性方案。
2PC是最經典的分布式事務協議,通過協調者(Coordinator) 統一調度參與者(Participant) 的執行。
分為兩個階段:

第一階段:準備階段
協調者詢問所有參與者:“能否提交事務?”
參與者執行本地事務但不提交,鎖定資源并回復YES/NO。
// 參與者偽代碼
public boolean prepare() {
try {
startTransaction();
executeSql("UPDATE account SET frozen = 100 WHERE id = 1"); // 預留資源
return true; // 返回YES
} catch (Exception e) {
rollback();
return false; // 返回NO
}
}
第二階段:提交/回滾階段
- 若所有參與者返回YES,協調者發送commit命令,參與者提交事務
- 若有任一參與者返回NO,協調者發送rollback命令,參與者回滾事務
致命缺陷:
- 同步阻塞:所有參與者在prepare后鎖定資源,直到收到commit/rollback(高并發下吞吐量驟降)
- 單點故障:協調者宕機導致參與者永久阻塞
- 數據不一致:網絡分區時部分參與者可能提交成功
2.2 3PC(三階段提交)
該方案也是強一致性方案。
3PC可以解決2PC阻塞問題。
3PC在2PC基礎上增加預提交階段,并引入超時機制:

- CanCommit階段:協調者詢問參與者狀態(不鎖定資源)
- PreCommit階段:參與者鎖定資源并執行SQL(不提交)
- DoCommit階段:正式提交
改進點:
- 參與者超時未收到命令自動提交(降低阻塞風險)
- 預提交階段發現異常可提前終止
但依然存在問題:
- 網絡分區時仍可能數據不一致
- 實現復雜度顯著增加
2.3 TCC(Try-Confirm-Cancel)
該方案是最終一致性方案。
它是業務層面的2PC。
TCC將業務邏輯拆分為三個階段:
- Try:預留資源(如凍結庫存)
- Confirm:確認操作(正式扣減庫存)
- Cancel:釋放資源(解凍庫存)
// 積分服務TCC實現
public class PointsService {
@Transactional
public boolean tryDeductPoints(Long userId, int points) {
// 檢查用戶積分是否充足
UserPoints user = userPointsDao.selectForUpdate(userId);
if (user.getAvailable() < points) {
throw new InsufficientPointsException();
}
// 凍結積分
userPointsDao.freeze(userId, points);
}
public boolean confirmDeductPoints(Long userId, int points) {
// 實際扣減凍結積分
userPointsDao.confirmDeduct(userId, points);
}
public boolean cancelDeductPoints(Long userId, int points) {
// 釋放凍結積分
userPointsDao.unfreeze(userId, points);
}
}
執行流程:
- 主業務調用所有服務的try方法
- 全部try成功則調用confirm;任一try失敗則調用cancel
優勢:
- 無全局鎖:只在try階段鎖定局部資源
- 高可用:協調者可集群部署
挑戰:
- 需手動實現回滾邏輯(業務侵入性強)
- 所有服務需提供三種接口
金融核心系統首選:某銀行跨境支付系統采用TCC方案,日均處理200萬筆交易,跨5個服務的事務成功率99.99%
2.4 可靠消息最終一致性
該方案也是最終一致性方案。
可以使用RocketMQ的事務消息。
RocketMQ的事務消息完美解決本地操作與消息發送的一致性問題:

關鍵步驟:
- 發送half消息(對消費者不可見)
- 執行本地事務
- 根據本地事務結果commit/rollback
- MQ定時回查未決事務
示例代碼:
// 訂單服務使用事務消息
public class OrderService {
@Autowired
private RocketMQTemplate rocketMQTemplate;
public void createOrder(Order order) {
// 1. 發送half消息
Message msg = MessageBuilder.withPayload(order).build();
TransactionSendResult result = rocketMQTemplate.sendMessageInTransaction(
"order_topic", msg, null);
// 2. 執行本地事務(在TransactionListener中實現)
}
}
// 事務監聽器
@RocketMQTransactionListener
class OrderTransactionListener implements RocketMQLocalTransactionListener {
@Override
public RocketMQLocalTransactionState executeLocalTransaction(Message msg, Object arg) {
try {
Order order = (Order) msg.getPayload();
orderDao.save(order); // 本地事務
return RocketMQLocalTransactionState.COMMIT;
} catch (Exception e) {
return RocketMQLocalTransactionState.ROLLBACK;
}
}
@Override
public RocketMQLocalTransactionState checkLocalTransaction(Message msg) {
// 回查邏輯
return checkOrderStatus(msg);
}
}
2.5 最大努力通知
該方案是弱一致性方案。
適用于對實時性要求低的場景(如短信通知):
- 業務主流程完成后發送通知
- 失敗后按策略重試(如間隔1min、5min、10min)
- 達到閾值后人工干預
// 最大努力通知服務
public class BestEffortNotifier {
private static final int[] RETRY_INTERVALS = {1, 5, 10, 30, 60}; // 分鐘
public void notify(String event) {
int retryCount = 0;
while (retryCount < RETRY_INTERVALS.length) {
try {
if (sendNotification(event)) {
return; // 通知成功
}
} catch (Exception e) {
// 記錄日志
}
Thread.sleep(RETRY_INTERVALS[retryCount] * 60 * 1000);
retryCount++;
}
alertManualIntervention(event); // 人工介入
}
}
實戰經驗:支付回調采用此方案,重試8次跨12小時,99.5%的通知在30分鐘內成功
2.6 Seata AT模式
該方案是自動化的TCC。
Seata的AT(Auto Transaction)模式在不侵入業務代碼的前提下實現分布式事務:
核心機制:
- 全局鎖:TC(事務協調器)管理內存級全局鎖(替代數據庫行鎖)
- SQL代理:解析業務SQL自動生成回滾日志
- 二階段異步提交:極大提升吞吐量
/* 原始SQL */
UPDATE product SET stock = stock - 10 WHERE id = 1001;
/* Seata自動記錄回滾日志 */
INSERT INTO undo_log (branch_id, xid,
before_image, after_image)
VALUES (?, ?,
'{"stock":100}', -- 更新前值
'{"stock":90}'); -- 更新后值
性能對比:
| 方案 | 鎖持有時間 | 鎖沖突檢測耗時 | 適用場景 |
|---|---|---|---|
| 傳統2PC | 500~2000ms | 5~20ms | 低并發強一致性 |
| Seata AT | 1~10ms | 0.01ms | 高并發最終一致性 |
局限:
- 不支持嵌套事務
- 熱點數據更新沖突率高
2.7 eBay事件隊列
該方案是基于本地事務的最終一致性方案。
eBay提出的經典方案:
- 將分布式操作拆分為本地事務+異步事件
- 使用事件表確保事件不丟失
- 通過補償機制解決失敗場景
-- 訂單服務數據庫
BEGIN TRANSACTION;
-- 1. 創建訂單
INSERT INTO orders (...) VALUES (...);
-- 2. 記錄事件(與訂單在同一個事務)
INSERT INTO event_queue (event_type, payload, status)
VALUES ('ORDER_CREATED', '{"orderId":1001}', 'PENDING');
COMMIT;
-- 定時任務掃描事件表并發布
該方案在早期eBay系統中每天處理1億+事件,保證核心交易鏈路最終一致
3.方案的選型指南
根據業務場景選擇合適方案:
| 方案 | 一致性級別 | 性能 | 復雜度 | 適用場景 |
|---|---|---|---|---|
| 2PC/3PC | 強一致性 | 低 | 中 | 銀行核心系統 |
| TCC | 最終一致 | 高 | 高 | 電商交易、積分體系 |
| RocketMQ事務消息 | 最終一致 | 高 | 中 | 訂單創建、物流通知 |
| 最大努力通知 | 弱一致 | 高 | 低 | 短信提醒、運營通知 |
| Seata AT | 最終一致 | 高 | 低 | 微服務架構的常規業務 |
| eBay事件隊列 | 最終一致 | 高 | 中 | 內部狀態同步 |
黃金法則:
- 強一致性需求:選擇2PC/ZooKeeper(犧牲性能)
- 高并發場景:選擇可靠消息/Seata AT(最終一致)
- 弱一致性場景:最大努力通知(成本最低)
總結
經過十年演進,分布式事務解決方案已從強一致性向高性能最終一致性發展。
技術沒有絕對的好壞,只有適合與否。
我曾見過團隊為了追求理論上的強一致性,把系統搞得復雜不堪;也見過過度追求性能導致資金損失的血淚教訓。
分布式事務的本質,是在業務需求與技術可行性之間找到平衡點。
致開發者:不必追求完美的分布式事務解決方案,適合業務場景的才是最好的。
在設計時多問自己:
- 業務能容忍多長時間不一致?
- 事務失敗后如何補償?
- 是否有完善的監控和人工介入機制?
愿你在分布式系統的海洋中,乘風破浪,游刃有余。
最后說一句(求關注,別白嫖我)
如果這篇文章對您有所幫助,或者有所啟發的話,幫忙關注一下我的同名公眾號:蘇三說技術,您的支持是我堅持寫作最大的動力。
求一鍵三連:點贊、轉發、在看。
關注公眾號:【蘇三說技術】,在公眾號中回復:進大廠,可以免費獲取我最近整理的10萬字的面試寶典,好多小伙伴靠這個寶典拿到了多家大廠的offer。
本文收錄于我的技術網站:http://www.susan.net.cn

浙公網安備 33010602011771號