ORA-02049: timeout: distributed transaction waiting for lockdistributed_lock_timeout = 60 秒 (預設)ORA-02049: timeout: distributed transaction waiting for lock
Cause: Exceeded INIT.ORA distributed_lock_timeout seconds waiting for lock.
Action: Treat as a deadlock.
分散式交易等待鎖超過 distributed_lock_timeout 秒,Oracle 視同死鎖處理並回滾該交易。
無明確死鎖追蹤 (ORA-00060),推測為鎖等待超時而非傳統死鎖。
SELECT
LPAD(' ', DECODE(l.xidusn, 0, 3, 0)) || l.oracle_username AS user_name,
o.owner, o.object_name, o.object_type,
s.sid, s.serial#, s.status, s.logon_time
FROM v$locked_object l
JOIN dba_objects o ON l.object_id = o.object_id
JOIN v$session s ON l.session_id = s.sid
ORDER BY o.object_id, xidusn DESC;
發現: 4 個 Session (SID 50, 41, 16, 34) 持有 4 張回傳相關表的鎖,狀態皆為 ACTIVE,LOGON_TIME 為前日中午,疑似前一次回傳程式異常中斷但鎖未釋放。
SELECT DECODE(request, 0, 'Holder: ', 'Waiter: ') || sid AS sess,
id1, id2, lmode, request, type
FROM v$lock
WHERE (id1, id2, type) IN (
SELECT id1, id2, type FROM v$lock WHERE request > 0
)
ORDER BY id1, request;
確認新回傳 Session 正在等待上述 4 個 Session 持有的鎖 (TX mode 6 排他鎖)。
前一日中午回傳程式異常終止 (網路中斷/Client Crash),所持有的 TX 鎖未正常釋放 (PMON 未及時清理或分散式交易狀態殘留),導致後續回傳請求排隊等待,超過 60 秒觸發 ORA-02049。
ALTER SYSTEM KILL SESSION '50,3558';
-- 返回 ORA-00031: session marked for kill
-- 重新查詢鎖,仍被持有,未釋放
Windows 平台 KILL SESSION 僅標記,需等待回滾完成,若進程卡住則無法釋放。
-- 1. 查詢對應 OS 執行緒 ID (SPID)
SELECT s.sid, p.spid
FROM v$session s, v$process p
WHERE s.sid IN (50, 41, 16, 34) AND s.paddr = p.addr;
-- 結果:
-- SID SPID
-- 50 4496
-- 41 1436
-- 16 6140
-- 34 4360
-- 2. 使用 Orakill (Oracle 安裝目錄\bin\orakill.exe)
C:\> orakill ORCL 4496
C:\> orakill ORCL 1436
C:\> orakill ORCL 6140
C:\> orakill ORCL 4360
-- 返回: Kill of thread id XXXX in instance ORCL successfully signalled.
再次查詢 v$locked_object,4 張表無 Session 持有鎖,後續回傳程式恢復正常。
| 面向 | 措施 |
|---|---|
| 應用程式 | 1. 分散式交易加入重試邏輯 (指數退避)。 2. 縮短交易範圍,盡早 COMMIT/ROLLBACK。 3. 異常處理中確保釋放資源 (finally 區塊關閉連線)。 |
| 資料庫參數 | 視業務容忍度調整 distributed_lock_timeout (預設 60 秒),如設為 300 秒減少誤殺,但會拉長阻塞時間。 |
| 監控告警 | 建立排程查詢 v$lock + v$session_wait,若等待時間 > 30 秒發送告警。 |
| 定期巡檢 | 每日檢查長時間 ACTIVE 且持有鎖的 Session (LOGON_TIME 超過 1 小時)。 |
| 升級版本 | Oracle 9i 已停止支援,建議升級至 19c,PMON 清理機制更完善,支援 DBMS_LOCK 更精細控制。 |
-- check_long_lock.sql
SET LINES 200 PAGES 1000
COL username FOR A15
COL machine FOR A20
COL object_name FOR A30
COL logon_time FOR A20
COL wait_event FOR A30
SELECT s.sid, s.serial#, s.username, s.machine, s.logon_time,
o.object_name, sw.event AS wait_event, sw.seconds_in_wait
FROM v$session s
JOIN v$lock l ON s.sid = l.sid
JOIN v$locked_object lo ON l.sid = lo.session_id
JOIN dba_objects o ON lo.object_id = o.object_id
LEFT JOIN v$session_wait sw ON s.sid = sw.sid
WHERE l.lmode > 0 AND s.status = 'ACTIVE'
AND s.logon_time < SYSDATE - 1/24 -- 超過 1 小時
ORDER BY s.logon_time;
核心教訓:分散式交易鎖超時往往源於 Client 端非正常斷線導致 Server 端資源殘留。Windows 平台需熟悉 orakill 使用時機;長期解決仍在應用端強化異常處理與交易邊界控制。