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

Oracle 當前模式讀 vs 一致性讀深度解析:UPDATE 子查詢延遲導致邏輯異常實戰案例

     編輯:Oracle 資料庫基礎

Oracle 多版本並發控制(MVCC)引入一致性讀與當前模式讀兩種存取模式。UPDATE 語句同時使用兩者:WHERE 子句用一致性讀過濾、SET 子句用當前模式讀修改。兩者間的時間差可能導致邏輯異常。本文透過兩個精心設計的並發案例深度剖析。

核心概念

讀取模式SCN 基準用途特性
一致性讀查詢啟動時的 SCN(Statement-level)或交易啟動 SCN(Transaction-level)SELECT、WHERE 子句過濾唯讀、快照隔離、不阻塞寫入
當前模式讀當前最新提交 SCNUPDATE/DELETE/MERGE 的 SET 子句、鎖定列可寫、需取得鎖、看到最新提交資料

案例一:子查詢延遲導致 WHERE 過濾失效

-- 初始狀態:t_test1 有一列 object_id=-1, secondary='A'
-- Session 1 (10:22:25) 啟動 UPDATE,含大型子查詢延遲 50 秒
UPDATE t_test1
SET    secondary = 'B'
WHERE  object_id = -1
AND    secondary = 'B'
AND    (SELECT COUNT(*) FROM t_test2 t1, t_test2 t2) > 0;

-- Session 2 (10:22:37) 更新同一列並提交
UPDATE t_test1 SET secondary='B' WHERE object_id=-1;
COMMIT;  -- 10:23:02

-- Session 1 子查詢結束,進入當前模式讀階段
-- 一致性讀看到 secondary='A'(Session 2 提交前狀態)
-- WHERE secondary='B' 不成立 → 0 rows updated

關鍵時序:

  1. Session 1 啟動,SCN=100。
  2. Session 2 更新並提交,SCN=105。
  3. Session 1 一致性讀回滾至 SCN=100 看到 secondary='A'。
  4. WHERE 過濾 secondary='B' 失效,無列進入當前模式讀修改階段。

案例二:一致性讀命中但當前模式讀發現已被修改

-- 初始狀態:secondary='A'
-- Session 1 (10:26:05) 啟動 UPDATE,WHERE secondary='A'
UPDATE t_test1
SET    secondary = 'B'
WHERE  object_id = -1
AND    secondary = 'A'
AND    (SELECT COUNT(*) FROM t_test2 t1, t_test2 t2) > 0;

-- Session 2 (10:26:16) 更新同一列為 'B' 並提交
UPDATE t_test1 SET secondary='B' WHERE object_id=-1;
COMMIT;  -- 10:26:41

-- Session 1 一致性讀看到 secondary='A'(滿足 WHERE)
-- 當前模式讀嘗試修改,但該列已被 Session 2 鎖定/修改
-- 結果:0 rows updated(或等待鎖釋放後發現值已變)

根因分析:時間窗口與 SCN 推進

UPDATE 執行階段:

  1. 階段一:一致性讀 — 依 WHERE 條件掃描索引/表,產生候選列集合(快照)。
  2. 階段二:當前模式讀 — 對候選列逐一取得排他鎖(TX),重新讀取最新區塊,驗證 WHERE 條件仍成立,執行 SET 修改。

若階段一到階段二之間,其他交易修改並提交了相同列,階段二會看到最新值,可能導致:

  • 原本滿足 WHERE 的列不再滿足 → 略過修改(案例二)。
  • 原本不滿足 WHERE 的列變為滿足 → 但已錯過階段一掃描,不會被修改(案例一反面)。

實務影響與應對策略

驗證腳本:觀察一致性讀 vs 當前模式讀行為

-- 建立測試表
CREATE TABLE t_test1 (object_id NUMBER, secondary VARCHAR2(1));
INSERT INTO t_test1 VALUES (-1, 'A');
COMMIT;

-- Session 1
UPDATE t_test1 SET secondary='B' WHERE object_id=-1 AND secondary='A' AND (SELECT COUNT(*) FROM dual CONNECT BY LEVEL <= 1e7) > 0;

-- Session 2 (另一終端機,Session 1 執行中立即執行)
UPDATE t_test1 SET secondary='C' WHERE object_id=-1;
COMMIT;

-- 觀察 Session 1 結果:0 rows updated(一致性讀看到 A,WHERE secondary='A' 成立,
-- 但當前模式讀發現已被 Session 2 改為 C,WHERE 重新驗證失敗)

結論

理解「UPDATE = 一致性讀過濾 + 當前模式讀修改」的雙階段本質,是診斷高併發更新異常、設計正確樂觀鎖、選擇適當隔離級的基石。避免在 WHERE 條件放置耗時操作,善用 FOR UPDATE、樂觀鎖版本欄位、分批提交,將雙階段時間窗口壓縮至最小,確保業務邏輯在 MVCC 下正確執行。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
場景風險緩解措施
長時間子查詢/函數在 WHERE 中一致性讀快照過舊,大量列被錯過將子查詢物化為暫存表/ WITH 子句,縮短階段一時間
高併發熱點列更新當前模式讀發現值已變,邏輯失效樂觀鎖:WHERE id=? AND version=? + SET version=version+1
批次更新大量列階段一掃描久,階段二衝突率高分批次 WHERE ROWNUM <= 1000 循環提交
需嚴格序列化業務邏輯要求「讀到即鎖定」SELECT ... FOR UPDATE 先鎖再改,或 SERIALIZABLE 隔離級