首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> Oracle 資料庫 >> Oracle 資料庫基礎

Oracle 並發控制機制深度解析:鎖、一致性讀與 DB2 差異對比

     編輯:Oracle 資料庫基礎

前言:並發是資料庫的靈魂

獨立測試通過不代表生產就緒。數十用戶並發修改同一訂單時,若未理解 Oracle 並發機制,將面臨更新丟失、資料不一致、效能雪崩、無法擴展四大災難。本文從 Oracle 獨特的鎖架構、多版本並發控制(MVCC)切入,並對比 DB2 根本差異,建立正確的並發設計思維。

Oracle 核心並發三大支柱

1. 僅在修改時加行級鎖,不升級

  • INSERT/UPDATE/DELETE 針對受影響行加排他鎖(TX,模式 6)
  • 絕不 自動升級為區塊鎖或表鎖(極少數兩階段提交瞬間除外)
  • 鎖資訊儲於資料塊頭部(ITL 槽),無中央鎖管理器,擴展性極強

2. 讀不加鎖,寫不阻塞讀

這是 Oracle 最獨特、最被誤解的特性:

  • SELECT 不加任何鎖(除 SELECT ... FOR UPDATE)
  • 讀取器構建一致性讀映像(CR)來自 UNDO 區段,不訪問當前塊
  • 寫入器持有行鎖時,讀取器仍可查詢該行的 提交前版本

「讀不阻塞寫,寫不阻塞讀」—— 這是 Oracle 高並發的基石,也是與 SQL Server、DB2、MySQL(預設 RR 隔離級別)根本不同之處。

3. 寫寫阻塞:先到先得

  • Session A 更新行 → 持有 TX 鎖
  • Session B 更新同一行 → 等待 enq: TX - row lock contention
  • Session A 提交/回滾 → 鎖釋放 → Session B 獲得鎖繼續

為什麼「讀取提交版本」會導致應用邏輯錯誤?

經典案例:餘額檢查後扣款

-- Session A                    -- Session B
SELECT balance FROM accounts     SELECT balance FROM accounts
WHERE id=1; -- 返回 100          WHERE id=1; -- 返回 100
-- 業務邏輯判斷 100 >= 50       -- 業務邏輯判斷 100 >= 80
UPDATE accounts SET balance=50   UPDATE accounts SET balance=20
WHERE id=1;                      WHERE id=1;
COMMIT;                          COMMIT;
-- 最終餘額 20,實際扣款 130!

兩個事務讀到相同的舊版本,各自基於舊版本計算新值,導致 更新丟失。Oracle 不會自動阻止這種情況。

正確解決方案

  1. 悲觀鎖:SELECT ... FOR UPDATE 讀取時加鎖,序列化存取
  2. 樂觀鎖:表加 VERSION 或 CHECKSUM 欄位,UPDATE ... SET bal=bal-50, ver=ver+1 WHERE id=1 AND ver=old_ver,檢查回傳行數
  3. 單一 SQL 原子化:UPDATE accounts SET balance=balance-50 WHERE id=1 AND balance>=50,利用陳述式級一致性

Oracle vs DB2:並發哲學的根本對決

維度OracleDB2
鎖架構意圖鎖 + 行級鎖標誌位(ITL),無鎖管理器意圖鎖 + 鎖列表,每個鎖佔用 Locklist 記憶體(X:64B, S:32B)
鎖升級不存在行鎖超過 LOCKLIST * MAXLOCKS 觸發升級為表鎖
純讀 SELECT零鎖,構建 CR 映像依隔離級別加鎖:RR=S, RS=IS, CS=IS, UR=無鎖
一致性模型陳述式級讀一致性(Statement-Level Read Consistency)交易級讀一致性(Transaction-Level,RR/RS/CS)
讀寫並發讀完全不阻塞寫讀鎖(S/IS)阻塞寫鎖(X/IX),除非 UR 級別讀未提交
版本儲存UNDO 表空間(自動管理)日誌緩衝區 + 表空間,版本清理由鎖驅動

實務啟示

  • 移植應用時 不可直接複製並發邏輯,Oracle 的「讀不加鎖」要求應用層顯式處理競態條件
  • DB2 開發者習慣「SELECT 就能保護資料」,到 Oracle 必須改用 FOR UPDATE 或樂觀鎖
  • Oracle 無鎖升級意味著大量並發 DML 不會突然轉為表鎖導致雪崩,適合高並發 OLTP

開發者必記的五條鐵律

  1. 預設 SELECT 看到的是「查詢開始時刻」的已提交資料快照
  2. 要「看到最新」或「鎖定」,必須寫 SELECT ... FOR UPDATE [NOWAIT|WAIT n|SKIP LOCKED]
  3. 不要在 PL/SQL 迴圈中逐行 COMMIT,會破壞陳述式級一致性並導致 ORA-01555
  4. 長查詢配合高 DML 環境需關注 UNDO 保留與 UNDO_RETENTION 設定
  5. 死鎖(ORA-00060)由 Oracle 自動偵測並犧牲一個事務,應用只需捕獲異常重試

掌握 Oracle 並發本質,不是為了炫技,而是為了寫出在生產環境真正可靠、可擴展的程式碼。記住:Oracle 給你自由,也給你責任。

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