同事報案,稍早發生Oracle錯誤導致有一批排程作業失敗,很快找到錯誤,修正後重新執行排程卻出現更新資料庫發生Timeout,而Oracle錯誤後才新増的項目則可順利執行無誤。
由以上線索,推測最可能的原因是「出問題的資料被鎖定了」。檢查程式碼,啟動Transaction但未使用try…catch主動於出錯時Rollback。依據過去經驗,Oracle的Transaction在Client Process意外結束後不會自動Rollback,過程產生的資料鎖定也一直留在特定資料上,如此即能解釋為何出錯的一批資料重新執行會Timeout(苦等不到鎖定釋放)而事後新増的項目卻沒問題。經驗裡SQL Server在Client Process不正常結束時會自動Rollback Transaction,而Oracle未Commit的Transaction則會持殘留,需要人為介入處理。請DBA查詢,證實Oracle殘留未結束Session及Lock,砍掉Session後再重跑排程,Timeout問題消失。
找到Stackoverflow一則討論有相關說明:
未Commit或Rollback的Oracle Transaction會導致無法登出,因此當Client端當掉或異常中止,可能會留下Session及鎖定,需由DBA手動刪除。查詢Sesion及鎖定可使用以下SQL語法:
SELECT ses.sid, ses.serial#, ses.username, ses.program, ses.osuser, ses.machine
FROM v$session ses,
dba_blockers blk
WHERE blk.holding_session = ses.sid
確認為異常Session後可使用以下指令刪除:
ALTER SYSTEM KILLSESSION'<<sid>>, <<serial#>>'
若想免除人為介入,Oracle有個Dead Connection Detection (DCD) 機制,定期偵測Client端是否還活著,主動砍掉Client掛點的無主Session。不過,最根本解決這個問題的做法是改良程式,使用 try … catch 區塊攔截錯誤,出錯時主動Rollback Transaction,比依賴第三方機制擦屁股更可靠更有效率。