分佈式事務:本地事務 + RPC 的“隱形炸彈”

只要系統被拆成多個微服務,“分佈式事務”就繞不過去。
很多同學只記住了 @Transactional,卻忽略了一個關鍵事實:
它只對本地數據庫負責,對遠端 RPC 一無所知。
真正的坑,往往就埋在“本地事務裏嵌套 RPC 調用”這一行代碼裏。

一、問題場景:A 調 B,要保證狀態一致

典型面試題:

  • 系統 A、系統 B,通過 RPC 相互調用。
  • 有個業務要求:兩邊的狀態必須一致。
  • A 中有一個方法加了 @Transactional
  1. 先通過 RPC 調 B,B 把狀態更新為“生效中”。
  2. 根據 B 的返回結果,A 更新自己的本地數據為“生效中”。

問:可能出現什麼問題?


二、幾個常見的坑

1. 長時間 RPC 拖垮數據庫
  • A 在開啓本地事務後,發起一個執行時間很長的 RPC。
  • 事務期間,數據庫連接一直被佔用;
  • 高併發時,很容易把連接池拖到見底,引發雪崩。
2. B 成功,A 回滾:兩邊狀態不一致
  • B 更新成功,狀態“生效中”。
  • A 在本地更新時拋異常,事務回滾,A 依然是舊狀態。
  • 最終:B 生效,A 未生效,數據不一致。
3. B 實際成功,但 A 認為失敗
  • 網絡抖動導致 RPC 超時:
  • B 實際已經成功處理;
  • A 認為調用失敗,可能回滾本地事務;
  • 兩邊狀態再次不一致。
4. 超時後的“不確定性”
  • 超時發生時,A 無法判斷 B 是否執行成功。
  • 單靠本地事務,根本無法解決這種“不確定結果”的問題。

三、本質:@Transactional 只管本地,不管遠端

這一類問題的本質:

  • @Transactional 只對本地數據庫負責:
  • 能確保 A 自己的多條 SQL 是原子性的;
  • 無法保證包含 RPC 在內的整個調用鏈是一致的。

想拿 @Transactional 去覆蓋遠端服務,是思維上的錯位


四、主流解法一:本地消息表(最終一致)

1. 思路
  • 把“本地更新”和“對外發送一條消息”放在同一個本地事務中。
  • 事務提交成功後,代表:
  • A 的本地狀態已更新;
  • 要通知 B 的“消息”已經可靠地寫入消息表。
  • 後台有一個異步任務:
  • 從本地消息表裏撈出未發送或發送失敗的記錄;
  • 不停重試調用 B 的接口,直到成功或超過重試上限。
2. 特點
  • 獲得的是最終一致性,而不是強一致。
  • 避免了本地事務直接包含長時間 RPC。
  • 實現簡單,常用在中小規模系統中。

五、主流解法二:MQ 事務消息(交給消息中間件協調)

1. 思路

如果系統中已經有 MQ(如 RocketMQ):

  • A 首先發送一條“半消息”到 MQ;
  • 然後在本地開啓事務,更新自己的數據庫;
  • 本地事務成功後,告知 MQ 提交這條消息為“正式消息”;
  • B 訂閲消費這條消息,更新自己的狀態。

如果 MQ 一段時間內沒收到“提交/回滾”的反饋,它會:

  • 反查 A 的本地事務狀態;
  • 決定是提交還是丟棄這條消息。
2. 特點
  • 把“事務協調”的複雜度交給 MQ。
  • 適合對可靠性要求很高、鏈路較長的分佈式系統。

六、主流解法三:同步調用 + 業務補償(TCC 思路)

1. 思路
  • 執行主業務時,同時設計一條對應的補償路徑(rollback)。
  • 當任何一個環節失敗時,通過調用遠端的補償接口,把狀態拉回去。
  • 不刻意追求“瞬時強一致”,而是通過補償達到“最終一致”。
2. 特點
  • 非常考驗業務建模能力:
  • 要為每種變更設計清晰的“撤銷邏輯”。
  • 通常只在關鍵鏈路、金額類核心業務中使用。

七、異步任務失敗後的兜底

以本地消息表為例,面試官常追問:

如果異步任務也一直失敗怎麼辦?

可按以下層次回答:

  1. 在消息表中記錄重試次數
  2. 每次失敗次數 +1;
  3. 超過閾值(如 3 次或 5 次):
  • 標記為“死信”或“失敗狀態”;
  • 觸發告警,由運維或業務方人工干預;
  1. 也可以設計死信隊列,專門承接多次失敗的消息。

八、沒有銀彈,只有權衡

  • @Transactional 解決的是單服務內一致性。
  • 分佈式場景更多要接受:最終一致性 + 補償機制
  • 選型要在:
  • 實現複雜度
  • 鏈路時延
  • 一致性要求
    之間做平衡。

九、面試思路

  1. 先説明問題本質:
  • 本地事務 + RPC 是不可靠的,@Transactional 不等於分佈式事務。
  1. 再給出主流方案:
  • 本地消息表 → 最常見且易實現;
  • MQ 事務消息 → 更規範但對基礎設施依賴更高。
  1. 然後提到補償思路:
  • 對強依賴同步鏈路,可以設計補償接口落地 TCC。
  1. 最後回答面試官追問:
  • 本地消息表的重試機制、死信隊列、人工介入策略。