首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

WebLogic 交易恢復機制中 XAResource READ_ONLY 導致分支合併與 Rollback 異常分析

     編輯:Java 相關知識

前言

在分散式交易(JTA)測試中,發現一個反直覺現象:Global Prepare 成功、Global Commit 尚未執行時資料庫掛掉,WebLogic 重啟恢復時卻對已 Commit 的分支執行 Rollback,導致資料不一致。本文記錄完整複現步驟、日誌證據、根因分析(Oracle XA Driver READ_ONLY 行為)與 WebLogic Transaction Log (TLog) 記錄邏輯。

測試場景重現

  1. Standalone Client 呼叫 UserTransaction.begin()。
  2. 順序操作兩個 XA Resource(同一 Oracle DB,不同 DataSource):
    Connection1 → INSERT INTO test ...
    Connection2 → INSERT INTO test1 ...
  3. UserTransaction.commit() 進入 Two-Phase Commit (2PC):
    Phase 1: Global Prepare → 所有 Resource Prepare OK
    Phase 2: 寫入 TLog → Global Commit(此處設中斷點)
  4. Global Prepare 完成後、Global Commit 前停止 Database。
  5. 移除中斷點,WebLogic 嘗試送 Commit 指令失敗,交易資訊留在 TLog 等待恢復。

關鍵觀察與日誌證據

1. 資料庫重啟後、WebLogic 重啟前

資料庫端顯示交易分支狀態:Branch 1 (4F7261636C655841445331) 已 Commit,Branch 2 (4F7261636C65584144533) 狀態不明。

2. WebLogic TLog 內容

Transaction Log Dump
Class Name = weblogic.transaction.internal.ResourceCheckpoint
Object = ResourceCheckpoint={OracelXADS, OracleXADS1}

僅有 ResourceCheckpoint,無完整交易記錄。

3. WebLogic 重啟後 Debug 日誌

<Debug> <XAResourceDescriptor> ... to rollback tx, xid: BEA1-0000669563242B7A1CFC-4F726163656C58414453

WebLogic 對 xid 結尾為 4F7261636C6558414453 的分支發起 XAResource.rollback(),導致原本已 Commit 的 Branch 1 (..31) 也被回滾。

根因深度剖析

Oracle XA Driver 的 READ_ONLY 最佳化

  • 兩個 Connection 操作同一資料庫,Oracle 視為同一交易上下文。
  • Global Prepare 時,第一個 Resource 回傳 OK (0),第二個 Resource 判定「無資料異動」回傳 READ_ONLY (3)。
  • READ_ONLY 意味著:Resource 內部自行 Commit,不需等待 Global Commit。這解釋為何 Global Commit 未執行、資料庫卻已有 Commit 紀錄。

WebLogic TLog 記錄判斷邏輯

Global Prepare 結束後,WebLogic 統計 vote == OK (0) 的 Resource 數量:

  • count(OK) > 1 → 寫入完整 TLog 記錄(含交易狀態、參與者等)。
  • count(OK) ≤ 1 → 僅寫入 ResourceCheckpoint,不寫入交易記錄。

本案例:OK=1, READ_ONLY=1 → count(OK)=1 → 無完整 TLog。

恢復階段邏輯導致誤 Rollback

  1. WebLogic 啟動 → 讀取 TLog → 發現無完整交易記錄 → 當前 txMap 為空。
  2. 呼叫 XAResource.recover() 取得資料庫端 Pending XID 列表。
  3. 遍歷 XID:若 XID 不在 txMap → 判定為孤兒交易 → 呼叫 XAResource.rollback(Xid)。
  4. Oracle Driver 將兩個分支合併為同一實體交易,Rollback 一個分支導致另一個分支連帶回滾。

對照驗證:雙資料庫情境

改用兩個不同 Oracle Database 實例測試:
Global Prepare → 兩 Resource 均回傳 OK → count(OK)=2 > 1 → 寫入完整 TLog → 重啟後正常 Commit。
TLog 截圖顯示完整交易資訊,恢復成功。

補充:Global Prepare Resource 執行順序

  1. Remote XA Resource(Collaborator 在遠端)
  2. Local XA Resource(Collaborator 在本地)
  3. Non-XA Resource(LLR / Emulate 2PC,Prepare 直接回 OK)

此處「Remote/Local」指 Collaborator 位置,非 Resource 管理位置。

實務建議與避坑指南

  1. 避免同一交易內對同一 DB 使用多個 XA DataSource,改用單一 DataSource 取得多 Connection,或確保業務邏輯確實有資料異動以免觸發 READ_ONLY。
  2. 若必須跨 DataSource,可在應用層強制執行 SELECT 1 FROM DUAL 等無實質異動但能避免 READ_ONLY 的 SQL(需測試 Driver 行為)。
  3. 監控 TLog 記錄完整性:若發現僅有 ResourceCheckpoint,代表交易可能面臨恢復風險。
  4. Oracle 權限問題:恢復時若出現 ORA-06550: PLS-00201: identifier 'SYS.DBMS_SYSTEM' must be declared,需執行 GRANT EXECUTE ON SYS.DBMS_SYSTEM TO <user>;。

結語

此問題本質上是「Oracle XA Driver READ_ONLY 最佳化」與「WebLogic TLog 記錄門檻」交互導致。理解 2PC 中 READ_ONLY 語意(自行 Commit、不參與 Global Commit)與 WebLogic 以 OK 票數 > 1 決定是否落盤 TLog 的邏輯,才能在架構設計時規避此類資料一致性陷阱。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved