1. 概述
本方案書旨在解決分布式系統中事務一致性問題,重點闡述全局事務標識(XID)的傳遞與存儲機制、事務協調者(TC)的設計與部署,以及分布式事務失效場景的應對策略。基于業界成熟框架(如Seata)的最佳實踐,提供高可用、高性能的分布式事務解決方案。
2. 核心概念與架構
2.1 分布式事務模型
- AT模式(自動補償)
通過全局鎖和逆向SQL(undo log)實現自動回滾,適用于數據庫操作。 - TCC模式(手動補償)
業務層實現Try-Confirm-Cancel
接口,適用于復雜業務邏輯。 - Saga模式(長事務)
通過事件驅動和補償事務鏈實現最終一致性,適用于跨服務長流程。
2.2 核心組件
組件 | 角色 |
---|---|
事務協調者(TC) | 獨立服務,負責全局事務狀態管理和協調。 |
事務管理器(TM) | 集成在業務服務中,負責開啟/提交/回滾全局事務。 |
資源管理器(RM) | 管理本地資源(如數據庫、消息隊列),執行分支事務的提交/回滾。 |
3. XID 的傳遞與存儲機制
3.1 XID 的生成與作用
- 全局唯一性:由TC生成,格式示例:
192.168.1.100:8091:123456
。 - 核心功能:關聯所有分支事務,確保跨服務、跨資源的操作原子性。
3.2 XID 的傳遞方式
場景1:跨服務調用(RPC/HTTP)
- 隱式傳遞(推薦)
通過RPC框架(如Dubbo、Feign)的請求頭自動傳遞XID。// Feign 攔截器示例 public class XidFeignInterceptor implements RequestInterceptor { @Override public void apply(RequestTemplate template) { String xid = RootContext.getXID(); template.header("TX_XID", xid); } }
場景2:異步任務與多線程
- 手動傳遞(基礎方案)
顯式傳遞XID并在子線程中綁定:String xid = RootContext.getXID(); executor.submit(() -> { RootContext.bind(xid); try { // 業務邏輯 } finally { RootContext.unbind(); } });
- TransmittableThreadLocal(推薦)
使用阿里開源工具解決線程池上下文傳遞問題:private static TransmittableThreadLocal<String> XID = new TransmittableThreadLocal<>(); executor.submit(TtlRunnable.get(() -> { // 子線程自動繼承XID }));
場景3:消息隊列(如Kafka、RocketMQ)
- 消息頭傳遞
生產者將XID寫入消息屬性,消費者讀取后綁定到本地事務:// RocketMQ 生產者 Message msg = new Message(); msg.putUserProperty("XID", xid);
3.3 XID 的存儲管理
- 默認存儲:基于
ThreadLocal
,僅限當前線程訪問。public class RootContext { private static ThreadLocal<String> XID = new ThreadLocal<>(); }
- 擴展方案:
InheritableThreadLocal
:支持父子線程傳遞,但不適用于線程池。TransmittableThreadLocal
:支持線程池場景,需配合TtlRunnable
使用。
4. 事務協調者(TC)的設計與部署
4.1 TC 的核心職責
- 全局事務管理:注冊XID,維護
global_table
(全局事務狀態表)。 - 分支事務協調:記錄
branch_table
(分支事務表),驅動提交/回滾。 - 故障恢復:檢測超時事務,觸發自動回滾或重試。
4.2 部署模式
獨立服務部署(推薦)
- 啟動命令:
# Seata TC 服務啟動 sh seata-server.sh -p 8091 -m db -h 192.168.1.100
- 高可用設計:
- 數據庫模式:多個TC節點共享同一數據庫(如MySQL)。
- Raft模式:通過一致性協議實現集群選舉(Seata 1.5+)。
配置文件示例(Seata)
# registry.conf
registry { type = "nacos" nacos { serverAddr = "localhost:8848" namespace = "" cluster = "default" }
} config { type = "nacos" nacos { serverAddr = "localhost:8848" namespace = "" group = "SEATA_GROUP" }
}
4.3 事務恢復流程
- 回滾觸發:TM通知TC回滾指定XID。
- 分支查詢:TC從
branch_table
查詢所有關聯分支。 - 逆向執行:各RM根據
undo_log
執行補償操作。
5. 分布式事務失效場景與解決方案
5.1 常見失效場景
場景 | 原因 | 后果 |
---|---|---|
RPC調用未透傳XID | 未在請求頭中添加XID | 下游服務無法加入全局事務 |
異步任務未綁定XID | 子線程未繼承ThreadLocal 中的XID | 異步操作游離于事務外 |
TC單點故障 | 未部署TC集群 | 全局事務無法協調 |
5.2 解決方案
場景1:XID傳遞失敗
- 強制校驗:在事務入口攔截請求,校驗XID是否存在。
- 統一攔截器:通過RPC攔截器(如Spring AOP)自動透傳XID。
場景2:多線程XID丟失
- TransmittableThreadLocal:結合線程池包裝類(
TtlExecutors
)傳遞上下文。 - 框架集成:Spring異步任務支持:
@Configuration @EnableAsync public class AsyncConfig implements AsyncConfigurer { @Override public Executor getAsyncExecutor() { return TtlExecutors.getTtlExecutor(Executors.newFixedThreadPool(4)); } }
場景3:TC單點故障
- 集群部署:部署多個TC節點,通過Nacos或Raft協議實現高可用。
- 健康檢查:集成Prometheus監控TC節點狀態。
6. 最佳實踐與性能優化
6.1 最佳實踐
- 避免跨事務異步操作:將異步任務放在事務邊界外。
- 事務粒度控制:單個事務內操作不超過3個服務。
- 超時配置:全局事務超時時間建議為60秒,避免資源長時間鎖定。
6.2 性能優化
- 異步提交:Seata AT模式下,二階段提交異步化(默認開啟)。
- 全局鎖優化:減少熱點數據競爭,使用
SELECT ... FOR UPDATE
時指定索引。
6.3 監控與日志
- 監控指標:
- TC節點狀態(活躍事務數、TPS)。
- RM資源鎖競爭情況。
- 日志收集:
- 記錄全局事務ID(XID)到ELK或SkyWalking,支持快速定位問題。
7. 附錄
7.1 示例代碼倉庫
- Seata 官方示例
- TransmittableThreadLocal 使用指南
7.2 相關工具
- Seata Dashboard:可視化監控全局事務狀態。
- Prometheus + Grafana:監控TC集群性能指標。
版本記錄
- v1.0(2023-10-01):初版發布,涵蓋XID傳遞、TC設計、失效場景解決方案。
- v1.1(2023-10-05):補充異步任務配置示例與性能優化建議。
評審人:技術委員會
批準人:CTO
注:本方案書需結合具體業務場景調整,建議在預生產環境充分驗證。