資料庫為追求高並發效能,皆實作並發控制機制。但並發帶來資料存取衝突(髒讀、不可重複讀、幻讀、更新遺失),鎖定正是解決衝突的核心手段。主流資料庫採用兩大鎖策略:
SQL Server 早期採頁級鎖(Page Lock),同表並發插入需排隊;Oracle 設計為行級鎖,僅鎖定被修改的資料列,其他列完全不受影響,並發插入/更新效能極高,幾乎無阻塞。
-- Session 1
SELECT * FROM test WHERE id = 10 FOR UPDATE;
-- 該列被鎖定,Session 1 未 COMMIT/ROLLBACK 前,其他 Session 執行相同語句會阻塞等待
特點:第二個 Session 執行 SELECT ... FOR UPDATE 會 Hang 住,直到第一個 Session 釋放鎖。若第二個 Session 執行 不帶 FOR UPDATE 的純查詢,則不阻塞,Oracle 多版本一致性讀(MVCC)保證讀到提交前的舊版本。
SELECT * FROM test WHERE id = 10 FOR UPDATE NOWAIT;
若目標列已被鎖定,立即拋出 ORA-00054: resource busy and acquire with NOWAIT specified,應用層可捕獲異常並決定重試或報錯,避免無限期等待。
當 Session 1 持有 FOR UPDATE 鎖時:
UPDATE test SET value=2 WHERE id=10 同樣會阻塞,直到 Session 1 釋放鎖。結論:Oracle 的悲觀鎖本質是利用 資料庫連線(Session)持有的 TX 鎖 實現行級排他,效能損耗極小,但需注意應用邏輯避免死鎖。
Oracle 內建無樂觀鎖語法,常見做法:
VERSION NUMBER 或 LAST_UPDATED TIMESTAMP 欄位。UPDATE t SET val=:new, version=version+1 WHERE id=:id AND version=:old_versionSQL%ROWCOUNT = 0 表示版本已變,拋出樂觀鎖異常,應用層重新讀取並重試。| 場景 | 推薦策略 |
|---|---|
| 高衝突、短事務、需強一致性(如扣款、庫存扣減) | 悲觀鎖 FOR UPDATE NOWAIT + 應用層重試 |
| 低衝突、長事務、用戶思考時間長(如表單編輯) | 樂觀鎖(版本號/時間戳) |
| 純查詢報表 | 無需加鎖,依賴 MVCC 一致性讀 |