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

Oracle 事務 ACID 特性深度解析:從原子性到持久性的實戰驗證

     編輯:Oracle 教學

Oracle 事務 ACID 特性深度解析:從原子性到持久性的實戰驗證

事務是數據庫區別於文件系統的核心特徵,ACID 四大特性構成了數據一致性的基石。本文結合實戰代碼與執行結果,逐層拆解 Oracle 如何落地這四大特性,並揭示開發中易忽視的陷阱。

一、原子性:要麼全做,要麼全不做

核心機制: Undo Segment + Redo Log。事務內所有 DML 產生的變更向量記入 Redo Log Buffer,舊映像寫入 Undo。COMMIT 觸發 LGWR 將 Redo 刷盤(SCN 推進),ROLLBACK 利用 Undo 逆向還原。

實戰驗證:轉賬業務的「全有或全無」

-- 建表與約束:余額不得為負
CREATE TABLE account_money (
  id NUMBER(4) PRIMARY KEY,
  name VARCHAR2(4),
  money NUMBER(5,2) CHECK (money >= 0)
);

-- 初始數據:張三兩賬戶 500 + 1 = 501
INSERT INTO account_money VALUES (1001, '張三', 500);
INSERT INTO account_money VALUES (1002, '張三', 1);

-- 轉賬 600:1001 扣款會觸發 CHECK 約束違規
BEGIN
  UPDATE account_money SET money = money - 600 WHERE id = 1001; -- 會報錯
  UPDATE account_money SET money = money + 600 WHERE id = 1002;
  COMMIT;
EXCEPTION
  WHEN OTHERS THEN ROLLBACK; -- 關鍵:異常捕獲並回滾
  DBMS_OUTPUT.PUT_LINE('轉賬異常,已回滾');
END;

結果: 若無 EXCEPTION ... ROLLBACK,第二條 UPDATE 仍會執行導致數據不一致(1001 變 -100,1002 變 601,總額仍 501 但單賬戶違規)。有回滾則兩賬戶餘額保持原樣。教訓:PL/SQL 異常處理必須顯式 ROLLBACK,否則部分提交風險極大。

二、一致性:業務規則的守護者

本質: 事務執行前後,數據庫從一個一致性狀態轉移到另一個一致性狀態。由約束、觸發器、應用邏輯共同保證。

實戰驗證:轉賬前後總額不變

DECLARE
  a_money NUMBER; b_money NUMBER;
BEGIN
  SELECT money INTO a_money FROM account_money WHERE id = 1001;
  SELECT money INTO b_money FROM account_money WHERE id = 1002;
  DBMS_OUTPUT.PUT_LINE('轉賬前總額:' || (a_money + b_money)); -- 501
  
  UPDATE account_money SET money = money - 100 WHERE id = 1001;
  UPDATE account_money SET money = money + 100 WHERE id = 1002;
  COMMIT;
  
  SELECT money INTO a_money FROM account_money WHERE id = 1001;
  SELECT money INTO b_money FROM account_money WHERE id = 1002;
  DBMS_OUTPUT.PUT_LINE('轉賬後總額:' || (a_money + b_money)); -- 501
END;

連續執行 5 次,總額恆定 501。第 6 次 A 賬戶餘額為 0,扣款 100 觸發 CHECK 違規,進入異常塊,ROLLBACK 生效,總額仍為 501。一致性不僅是總額守恆,更是所有約束(PK、FK、CHECK、NOT NULL)在事務邊界前後均滿足。

三、隔離性:併發世界的「平行時空」

Oracle 默認隔離級別 READ COMMITTED,通過「讀一致性」(多版本併發控制 MVCC)實現:查詢只看啟動瞬間的 SCN 快照,不加讀鎖,寫不阻塞讀,讀不阻塞寫。

實戰驗證:兩會話併發更新同一行

初始狀態: 1001 賬戶 0 元,1002 賬戶 501 元。

  • 會話 1: UPDATE account_money SET money = money + 300 WHERE id = 1001; (未提交)
  • 會話 2: SELECT * FROM account_money; → 看到舊值(0, 501),不被阻塞。
  • 會話 2: UPDATE account_money SET money = money + 300 WHERE id = 1001; → 等待會話 1 提交/回滾(行級排他鎖衝突)。
  • 會話 1 提交後: 會話 2 獲得鎖,更新生效,查詢見新值(600, 501)。

關鍵結論: 讀操作從不等待寫操作(得益於 Undo 快照);寫操作若遇未提交的同行寫鎖則必須等待。這就是「讀寫不阻塞,寫寫串行化」。

四、持久性:COMMIT 之後的鐵律

COMMIT 完成時,LGWR 已將該事務 Redo 寫入線上日志文件(配置 COMMIT_WRITE 可控制是否等待寫盤),數據庫緩衝區中的髒塊由 DBWn 延寫回數據文件。即便實例瞬間崩潰,實例恢復階段通過 Redo 前滾、Undo 回滾,也能保證已提交事務不丟失、未提交事務不殘留。

開發者必知的 5 大避坑指南

  1. 業務邏輯邊界即事務邊界: 一個業務操作(如下單扣庫存)必須在一個事務中完成,禁用自動提交模式。
  2. 異常處理必須 ROLLBACK: PL/SQL 塊、Java/C# 代碼捕獲異常後,第一時間調用連接的 rollback()。
  3. 長事務是性能殺手: 持有鎖時間長、Undo 膨脹、阻塞併發,拆解為多個短事務或用樂觀鎖(版本號)替代。
  4. 死鎖預防: 多表更新統一順序(如按表名字母序),設置 LOCK_TIMEOUT 避免無限等待。
  5. 分佈式事務慎用: 兩階段提交(2PC)性能差、協調者單點故障風險高,微服務架構優先考慮最終一致性(Saga、TCC、本地消息表)。

透徹理解 ACID 在 Oracle 的具體實現,能讓你在設計高併發系統、排查數據不一致、制定備份恢復策略時,擁有直擊核心的理論支撐與實戰判斷力。

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