事務是數據庫區別於文件系統的核心特徵,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 元。
UPDATE account_money SET money = money + 300 WHERE id = 1001; (未提交)SELECT * FROM account_money; → 看到舊值(0, 501),不被阻塞。UPDATE account_money SET money = money + 300 WHERE id = 1001; → 等待會話 1 提交/回滾(行級排他鎖衝突)。關鍵結論: 讀操作從不等待寫操作(得益於 Undo 快照);寫操作若遇未提交的同行寫鎖則必須等待。這就是「讀寫不阻塞,寫寫串行化」。
COMMIT 完成時,LGWR 已將該事務 Redo 寫入線上日志文件(配置 COMMIT_WRITE 可控制是否等待寫盤),數據庫緩衝區中的髒塊由 DBWn 延寫回數據文件。即便實例瞬間崩潰,實例恢復階段通過 Redo 前滾、Undo 回滾,也能保證已提交事務不丟失、未提交事務不殘留。
rollback()。LOCK_TIMEOUT 避免無限等待。透徹理解 ACID 在 Oracle 的具體實現,能讓你在設計高併發系統、排查數據不一致、制定備份恢復策略時,擁有直擊核心的理論支撐與實戰判斷力。