死鎖是並發資料庫系統的經典難題。本文以「兩會話、兩表、各一列」最小模型,逐步演示從阻塞到死鎖的必然演進,解析 Oracle 死鎖偵測機制、犧牲者選擇邏輯、錯誤碼 ORA-00060 內容,並提供預防與排查實務指南。
-- 建表
CREATE TABLE a (id NUMBER PRIMARY KEY);
CREATE TABLE b (id NUMBER PRIMARY KEY);
INSERT INTO a VALUES (1);
INSERT INTO b VALUES (1);
COMMIT;
UPDATE a SET id = 2 WHERE id = 1; -- 鎖定表 A 第 1 列
-- 此處暫停,不提交
UPDATE b SET id = 2 WHERE id = 1; -- 鎖定表 B 第 1 列
-- 此處暫停,不提交
-- 會話 B 嘗試更新表 A
UPDATE a SET id = 3 WHERE id = 1; -- 阻塞,等待會話 A 釋放鎖
-- 會話 A 此時若 COMMIT/ROLLBACK,會話 B 即可繼續 → 只是阻塞
-- 會話 A 轉而嘗試更新表 B
UPDATE b SET id = 3 WHERE id = 1; -- 死鎖!雙方互相等待對方釋放鎖
LCK0 / LMD0 (RAC) 定期掃描等待圖,發現環狀等待。ORA-00060: deadlock detected while waiting for resource,並附帶追蹤檔路徑。
*** 2024-01-15 10:30:45.123
DEADLOCK DETECTED ( ORA-00060 )
[Transaction Deadlock]
The following deadlock is not an ORACLE error. It is a
deadlock due to user error in the design of an application
or in issuing incorrect ad-hoc SQL.
Current SQL Statement for this session (Session 1):
UPDATE b SET id = 3 WHERE id = 1
Waiter (Session 1) holds:
TM lock on table A (mode 3 - Row-X)
Waiter (Session 1) waits for:
TX lock on table B (mode 6 - Exclusive)
Holder (Session 2) holds:
TM lock on table B (mode 3 - Row-X)
Holder (Session 2) waits for:
TX lock on table A (mode 6 - Exclusive)
追蹤檔位置:USER_DUMP_DEST / DIAGNOSTIC_DEST 下的 ora_。
| 維度 | 阻塞 | 死鎖 |
|---|---|---|
| 等待鏈 | 單向(A 等 B) | 環狀(A 等 B,B 等 A) |
| 自動解除 | 否(需等待持有者提交/回滾) | 是(Oracle 強制犧牲者回滾) |
| 錯誤碼 | 無(單純等待) | ORA-00060 |
| 業務影響 | 延遲 | 其中一筆交易全部回滾 |
SELECT * FROM v$session_wait WHERE event LIKE 'enq%';DBA_HIST_ACTIVE_SESS_HISTORY 過濾 event = 'enq: TX - row lock contention'。ORA-00060 郵件告警。SQLState 61000 / ErrorCode 60,指數退避重試(最多 3 次)。死鎖本質是應用設計缺陷在資料庫層面的投影。透過統一存取順序、縮短交易、索引外鍵,可從根源消除 90% 以上死鎖;剩餘極少數由 Oracle 自動偵測犧牲,配合應用層重試機制,即可構建健壯的高併發系統。