分布式事務(wù)解決方案綜述與 Seata Saga 實(shí)踐
1. 背景
在單體應(yīng)用中,事務(wù)問題通常可以依賴數(shù)據(jù)庫的本地事務(wù)來解決,例如 BEGIN TRANSACTION / COMMIT / ROLLBACK。但是在 微服務(wù)架構(gòu) 下,一個(gè)完整的業(yè)務(wù)流程往往需要多個(gè)服務(wù)協(xié)同完成。例如“下單”場景:
- ? 訂單服務(wù):創(chuàng)建訂單
- ? 庫存服務(wù):扣減庫存
- ? 賬戶服務(wù):扣減余額
- ? 配送服務(wù):生成運(yùn)單
這些服務(wù)往往分布在不同的數(shù)據(jù)庫甚至不同的系統(tǒng)中,本地事務(wù)已經(jīng)無法保證跨服務(wù)的一致性。此時(shí)就需要 分布式事務(wù) 方案。
2. 常見分布式事務(wù)模式
2.1 模式對比表
模式 | 實(shí)現(xiàn)原理 | 一致性保證 | 性能 | 開發(fā)成本 | 典型場景 |
XA (兩階段提交) | 協(xié)調(diào)器 + 各分支數(shù)據(jù)庫 prepare/commit | 強(qiáng)一致 | 低 | 低(透明) | 小范圍分布式事務(wù)、同構(gòu)數(shù)據(jù)庫 |
TCC | 每個(gè)操作定義 Try/Confirm/Cancel | 強(qiáng)一致 | 中 | 高(需寫三段邏輯) | 資金類、資源預(yù)留類業(yè)務(wù) |
本地消息表 / 可靠消息 | 本地事務(wù)寫消息 + MQ 投遞 | 最終一致 | 高 | 中(依賴 MQ,維護(hù)消息表) | 電商訂單、異步通知 |
Saga | 長事務(wù)拆解 + 補(bǔ)償 | 最終一致 | 高 | 中(需寫補(bǔ)償邏輯) | 長流程、跨服務(wù)業(yè)務(wù)(下單、支付、物流) |
2.2 模式簡述
? 兩階段提交(XA):一致性最好,但性能差,適合小范圍使用。
? TCC:靈活但開發(fā)成本高,需要三段邏輯。
? 本地消息表:依賴 MQ,適合異步場景。
? Saga:通過補(bǔ)償保證最終一致性,適合跨服務(wù)長流程。
3. Seata 簡介
Seata 是一款開源的分布式事務(wù)解決方案,由阿里開源并捐贈(zèng)給 CNCF。它支持多種事務(wù)模式:
- ? AT 模式:基于數(shù)據(jù)源代理,自動(dòng)生成回滾日志,開發(fā)最透明。
- ? TCC 模式:顯式定義 Try/Confirm/Cancel。
- ? Saga 模式:基于狀態(tài)機(jī)編排業(yè)務(wù)流程,支持補(bǔ)償。
- ? XA 模式:標(biāo)準(zhǔn)兩階段提交。
其中,Saga 模式特別適合業(yè)務(wù)流程長、跨多個(gè)服務(wù)、需要最終一致性的場景。
4. Seata Saga 模式原理
Saga 模式核心思想是:
? 將業(yè)務(wù)拆分為一系列 有序的 ServiceTask。
? 每個(gè) ServiceTask 都要定義 正向執(zhí)行方法 和 補(bǔ)償方法。
? Seata Saga 引擎負(fù)責(zé)執(zhí)行狀態(tài)機(jī),并在失敗時(shí)自動(dòng)回滾補(bǔ)償。
Saga 執(zhí)行流程
1. 客戶端發(fā)起事務(wù),加載對應(yīng)的狀態(tài)機(jī)定義(JSON)。
2. 引擎依次調(diào)用各個(gè)服務(wù)方法。
3. 若所有步驟成功,事務(wù)結(jié)束。
4. 若某步驟失敗,啟動(dòng)補(bǔ)償流程,按反向順序調(diào)用補(bǔ)償方法,直到完成或人工介入。
5. Saga 狀態(tài)與定義會(huì)落庫,支持自動(dòng)恢復(fù)。
Saga 正向與補(bǔ)償流程圖

5. 實(shí)戰(zhàn)案例:訂單創(chuàng)建 Saga 流程
5.1 業(yè)務(wù)場景
? 創(chuàng)建訂單 → 扣庫存 → 扣余額 → 確認(rèn)訂單
? 如果扣庫存失敗,則取消訂單;
? 如果扣余額失敗,則退還庫存 + 取消訂單。
5.2 Saga 狀態(tài)機(jī)定義(order_place_saga.json)
{
"Name":"order_place_saga",
"StartState":"CreateOrder",
"Version":"1.0.0",
"States":{
"CreateOrder":{
"Type":"ServiceTask",
"ServiceName":"orderService",
"ServiceMethod":"createPending",
"CompensateState":"CancelOrder",
"Next":"DeductInventory"
},
"CancelOrder":{
"Type":"ServiceTask",
"ServiceName":"orderService",
"ServiceMethod":"cancel",
"End":true
},
"DeductInventory":{
"Type":"ServiceTask",
"ServiceName":"inventoryService",
"ServiceMethod":"deduct",
"CompensateState":"CompensateInventory",
"Next":"DeductBalance"
},
"CompensateInventory":{
"Type":"ServiceTask",
"ServiceName":"inventoryService",
"ServiceMethod":"compensate",
"Next":"CancelOrder"
},
"DeductBalance":{
"Type":"ServiceTask",
"ServiceName":"accountService",
"ServiceMethod":"deduct",
"CompensateState":"RefundBalance",
"Next":"ConfirmOrder"
},
"RefundBalance":{
"Type":"ServiceTask",
"ServiceName":"accountService",
"ServiceMethod":"refund",
"Next":"CompensateInventory"
},
"ConfirmOrder":{
"Type":"ServiceTask",
"ServiceName":"orderService",
"ServiceMethod":"confirm",
"End":true
}
}
}5.3 服務(wù)實(shí)現(xiàn)(示例,無數(shù)據(jù)庫,僅內(nèi)存模擬)
@Service("orderService")
publicclassOrderService {
privatefinal Map<Long, String> orderStore = newConcurrentHashMap<>();
publicvoidcreatePending(Long orderId) {
System.out.println("創(chuàng)建訂單 pending " + orderId);
orderStore.put(orderId, "PENDING");
}
publicvoidconfirm(Long orderId) {
System.out.println("確認(rèn)訂單 " + orderId);
orderStore.put(orderId, "CONFIRMED");
}
publicvoidcancel(Long orderId) {
System.out.println("取消訂單 " + orderId);
orderStore.put(orderId, "CANCELED");
}
}庫存和余額服務(wù)類似,分別實(shí)現(xiàn) deduct/compensate、deduct/refund 方法。
5.4 啟動(dòng) Saga 流程
@RestController
@RequestMapping("/order")
publicclassOrderController {
@Autowired
private StateMachineEngine stateMachineEngine;
@PostMapping("/place")
public String place(@RequestBody PlaceOrderCmd cmd) {
Map<String, Object> params = newHashMap<>();
params.put("orderId", cmd.getOrderId());
params.put("productId", cmd.getProductId());
params.put("count", cmd.getCount());
params.put("userId", cmd.getUserId());
params.put("amount", cmd.getAmount());
varinstance= stateMachineEngine.start("order_place_saga",
"ORDER-" + cmd.getOrderId(),
params);
return"SAGA status: " + instance.getStatus();
}
}6. Saga 模式的最佳實(shí)踐
1. 冪等性:所有服務(wù)方法必須支持冪等(如通過事務(wù) ID 去重)。
2. 補(bǔ)償可行性:每個(gè)步驟必須設(shè)計(jì)可逆的補(bǔ)償操作。
3. 自動(dòng)恢復(fù):開啟 Saga 引擎的失敗恢復(fù)機(jī)制,避免事務(wù)懸掛。
4. 可觀測性:記錄業(yè)務(wù)鍵(訂單號)、事務(wù) ID,方便鏈路追蹤與問題排查。
5. 人工兜底:部分場景無法自動(dòng)補(bǔ)償,需要設(shè)計(jì)對賬與人工介入機(jī)制。
7. 總結(jié)
? XA 適合強(qiáng)一致的小范圍分布式事務(wù),但性能差。
? TCC 靈活但開發(fā)成本高。
? 本地消息表 適合基于 MQ 的異步場景。
? Saga 則更適合長流程、跨服務(wù)、需要最終一致性的場景。
在微服務(wù)落地中,Seata Saga 提供了成熟的狀態(tài)機(jī)引擎,開發(fā)者只需定義業(yè)務(wù)步驟與補(bǔ)償邏輯,就能較低成本地實(shí)現(xiàn)可靠的分布式事務(wù)。


































