【Oracle】淺析Oracle中的事務
1. 什麽是事務
在數據庫中事務是工作的邏輯單元,一個事務是由一個或多個完成一組的相關行為的SQL語句組成,通過事務機制確保這一組SQL語句所作的操作要麽都成功執行,完成整個工作單元操作,要麽一個也不執行。
如:網上轉帳就是典型的要用事務來處理,用以保證數據的一致性。
2. 事務特性
SQL92標準定義了數據庫事務的四個特點:
- 原子性(Atomicity):一個事務裏面所有包含的SQL語句是一個執行整體,不可分割,要麽都做,要麽都不做。
- 一致性(Consistency):事務開始時,數據庫中的數據是一致的,事務結束時,數據庫的數據也應該是一致的。
- 隔離性(Isolation):是指數據庫允許多個並發事務同時對其中的數據進行讀寫和修改的能力,隔離性可以防止事務的並發執行時,由於他們的操作命令交叉執行而導致的數據不一致狀態。
- 持久性 (Durability) : 是指當事務結束後,它對數據庫中的影響是永久的,即便系統遇到故障的情況下,數據也不會丟失。
一組SQL語句操作要成為事務,數據庫管理系統必須保證這組操作的原子性(Atomicity)、一致性(consistency)、隔離性(Isolation)和持久性(Durability),這就是ACID特性。
3. 數據異常
因為Oracle中支持多個事務並發執行,所以會出現下面的數據異常。
3.1 臟讀
當一個事務修改數據時,另一事務讀取了該數據,但是第一個事務由於某種原因取消對數據修改,使數據返回了原狀態,這是第二個事務讀取的數據與數據庫中數據不一致,這就叫臟讀。
如:事務T1修改了一條數據,但是還未提交,事務T2恰好讀取到了這條修改後了的數據,此時T1將事務回滾,這個時候T2讀取到的數據就是臟數據。
3.2 不可重復讀
是指一個事務讀取數據庫中的數據後,另一個事務則更新了數據,當第一個事務再次讀取其中的數據時,就會發現數據已經發生了改變,這就是不可重復讀取。不可重復讀取所導致的結果就是一個事務前後兩次讀取的數據不相同。
如:事務T1讀取一行記錄,緊接著事務T2修改了T1剛剛讀取的記錄,然後T1再次查詢,發現與第一次讀取的記錄不同。
3.3 幻讀
如果一個事務基於某個條件讀取數據後,另一個事務則更新了同一個表中的數據,這時第一個事務再次讀取數據時,根據搜索的條件返回了不同的行,這就是幻讀。
如:事務T1讀取一條指定where條件的語句,返回結果集。此時事務T2插入一行新記錄,恰好滿足T1的where條件。然後T1使用相同的條件再次查詢,結果集中可以看到T2插入的記錄,這條新紀錄就是幻讀。
事務中遇到的這些異常與事務的隔離性設置有關,事務的隔離性設置越多,異常就出現的越少,但並發效果就越低,事務的隔離性設置越少,異常出現的越多,並發效果越高。
4. 事務隔離級別
針對讀取數據時可能產生的不一致現象,在SQL92標準中定義了4個事務的隔離級別:
隔離級別 | 臟讀 | 不可重復讀 | 幻讀 |
---|---|---|---|
Read uncommitted(讀未提交) | 是 | 是 | 是 |
Read committed(讀已提交) | 否 | 是 | 是 |
Repeatable read(可重復讀) | 否 | 否 | 是 |
Serializable(串行讀) | 否 | 否 | 否 |
Oracle默認的隔離級別是read committed。
Oracle支持上述四種隔離級別中的兩種:read committed 和serializable。除此之外,Oralce中還定義Read only和Read write隔離級別。
Read only:事務中不能有任何修改數據庫中數據的操作語句,是Serializable的一個子集。
Read write:它是默認設置,該選項表示在事務中可以有訪問語句、修改語句,但不經常使用。
設置隔離級別
設置一個事務的隔離級別:
- SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
- SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
- SET TRANSACTION READ ONLY;
- SET TRANSACTION READ WRITE;
註意:這些語句是互斥的,不能同時設置兩個或兩個以上的選項。
設置單個會話的隔離級別:
- ALTER SESSION SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
- ALTER SESSION SET TRANSACTION ISOLATION SERIALIZABLE;
5. 事務控制命令
5.1 提交事務
在執行使用COMMIT
語句可以提交事務,當執行了COMMIT語句後,會確認事務的變化,結束事務,刪除保存點,釋放鎖。當使用COMMIT語句結束事務之後,其他會話將可以查看到事務變化後的新數據。
5.2 回滾事務
保存點(savepoint):是事務中的一點,用於取消部分事務,當結束事務時,會自動的刪除該事務所定義的所有保存點。當執行ROLLBACK時,通過指定保存點可以回退到指定的點。
設置保存點:
sql> Savepoint a;
刪除保存點:
sql> Release Savepoint a;
回滾部分事務:
sql> Rollback To a;
回滾全部事務:
sql> Rollback;
6. 數據庫鎖
數據庫是一個多用戶使用的共享資源。當多個用戶並發地存取數據時,在數據庫中就會產生多個事務同時存取同一數據的情況。若對並發操作不加控制就可能會讀取和存儲不正確的數據,破壞數據庫的一致性。
在數據庫中有兩種基本的鎖類型:排它鎖(Exclusive Locks,即X鎖)和共享鎖(Share Locks,即S鎖)。當數據對象被加上排它鎖時,其他的事務不能對它讀取和修改;加了共享鎖的數據對象可以被其他事務讀取,但不能修改。
6.1 鎖分類
根據保護對象的不同,Oracle數據庫鎖可分為:
- DML lock(data locks,數據鎖):用於保護數據的完整性。
- DDL lock(dictionary locks,字典鎖):用於保護數據庫對象的結構(例如表、視圖、索引的結構定義)。
- Internal locks 和latches(內部鎖與閂):保護內部數據庫結構。
- Distributed locks(分布式鎖):用於OPS(並行服務器)中。
- PCM locks(並行高速緩存管理鎖):用於OPS(並行服務器)中。
在Oracle中最主要的鎖是DML鎖,DML鎖的目的在於保證並發情況下的數據完整性。在Oracle數據庫中,DML鎖主要包括TM鎖和TX鎖,其中TM鎖稱為表級鎖,TX鎖稱為事務鎖或行級鎖。
鎖出現在數據共享的場合,用來保證數據的一致性。當多個會話同時修改一個表時,需要對數據進行相應的鎖定。
鎖有“共享鎖”、“排它鎖”,“共享排它鎖”等多種類型,而且每種類型又有“行級鎖” (一次鎖住一條記錄),“頁級鎖” (一次鎖住一頁,即數據庫中存儲記錄的最小可分配單元),“表級鎖” (鎖住整個表)。
6.2 共享鎖(S鎖)
可通過lock table in share mode命令添加該S鎖。在該鎖定模式下,不允許任何用戶更新表。但是允許其他用戶發出select …from for update命令對表添加RS鎖。
6.3 排他鎖(X鎖)
可通過lock table in exclusive mode命令添加X鎖。在該鎖定模式下,其他用戶不能對表進行任何的DML和DDL操作,該表上只能進行查詢。
6.4 行級共享鎖(RS鎖)
通常是通過select … from for update語句添加的,同時該方法也是我們用來手工鎖定某些記錄的主要方法。比如,當我們在查詢某些記錄的過程中,不希望其他用戶對查詢的記錄進行更新操作,則可以發出這樣的語句。當數據使用完畢以後,直接發出rollback命令將鎖定解除。當表上添加了RS鎖定以後,不允許其他事務對相同的表添加排他鎖,但是允許其他的事務通過DML語句或lock命令鎖定相同表裏的其他數據行。
6.5 行級排他鎖(RX鎖)
當進行DML操作時會自動在被更新的表上添加RX鎖,或者也可以通過執行lock命令顯式的在表上添加RX鎖。在該鎖定模式下,允許其他的事務通過DML語句修改相同表裏的其他數據行,或通過lock命令對相同表添加RX鎖定,但是不允許其他事務對相同的表添加排他鎖(X鎖)。
6.6 共享行級排他鎖(SRX鎖)
通過lock table in share row exclusive mode命令添加SRX鎖。該鎖定模式比行級排他鎖和共享鎖的級別都要高,這時不能對相同的表進行DML操作,也不能添加共享鎖。
上述幾種鎖模式中,RS鎖是限制最少的鎖,X鎖是限制最多的鎖。它們的兼容關系如下:
基本上所有的鎖都可以由Oracle內部自動創建和釋放,但是其中的DDL和DML鎖是可以通過命令進行管理的,命令語法:
LOCK table_name IN
[row share][row exclusive][share][share row exclusive][exclusive] MODE
[NOWAIT];
下圖列出產生鎖定模式的SQL語句:
當程序對所做的修改進行提交(Commit)或回滾(Rollback)後,鎖住的資源便會得到釋放,從而允許其他用戶進行操作。如果兩個事務,分別鎖定一部分數據,而都在等待對方釋放鎖才能完成事務操作,這種情況下就會發生死鎖
7. 數據庫事務實現機制
幾乎所有的數據庫管理系統中,事務管理的機制都是通過使用日誌文件來實現的,我們來簡單介紹一下日誌的工作方式。
當用戶執行一條修改數據庫的DML語句時,DBMS自動在日誌文件中寫一條記錄,顯示被這條語句影響的每一條記錄的兩個副本。一個副本顯示變化前的記錄,另一個副本顯示變化後的記錄。當日誌寫完之後,DBMS才實際對磁盤中的記錄進行修改。
如果用戶隨後執行COMMIT語句,事務結束也被記錄在事務日誌中。如果用戶執行ROLLBACK語句,DBMS檢查日誌,找出自事務開始以來被修改的記錄“以前”的樣子,然後使用這些信息恢復它們以前的狀態,有效地撤銷事務期間對數據庫所做的修改。
如果系統出錯,系統操作員通常通過運行DBMS提供的特殊恢復程序來復原數據庫。恢復程序檢查到事務日誌末尾,查找故障之前沒有被提交的事務。恢復程序回滾沒有完全完成的事務,以便僅有被提交的事務反映到數據庫中,而故障中正處理的事務被回滾。
事務日誌的使用明顯增加了更新數據庫的開銷。在實際中,主流商用DBMS產品使用的日誌技術比上述描述的方案更復雜,用以減小這種開銷。此外,事務日誌通常被存儲在高速磁盤驅動器中,不同於存儲數據庫的磁盤,以減小磁盤訪問競爭。某些個人計算機DBMS產品允許關閉事務日誌性能,以提高DBMS的性能。
8. 示例
銀行轉帳的例子是最經典的事務示例:
用戶把錢從一個銀行賬號轉賬至另一個銀行賬號,需要將資金從一個銀行賬號中取出,然後再存入另一個銀行賬號中。理想來說,這兩次操作都應該成功。但是,如果有錯誤發生,則兩次操作都應該失敗,否則的話,操作之後其中一個賬號中的金額將會是錯誤的,整個操作過程應該是原子性的,兩個操作都是一個原子事務操作的一部分。
示例:
-- 從賬戶一向賬戶二轉賬
DECLARE
v_money NUMBER(8, 2); -- 轉賬金額
v_balance account.balance%TYPE; -- 賬戶余額
BEGIN
v_money := &轉賬金額; -- 輸入轉賬金額
-- 從賬戶一減錢
UPDATE account SET balance = balance - v_money WHERE id=&轉出賬戶
RETURNING balance INTO v_balance;
IF SQL%NOTFOUND THEN
RAISE_APPLICATION_ERROR(-20001, ‘沒有該賬戶:‘||&轉出賬戶);
END IF;
IF v_balance < 0 THEN
RAISE_APPLICATION_ERROR(-20002, ‘賬戶余額不足‘);
END IF;
-- 向賬戶二加錢
UPDATE account SET balance = balance + v_money WHERE id=&轉入賬戶;
IF SQL%NOTFOUND THEN
RAISE_APPLICATION_ERROR(-20001, ‘沒有該賬戶:‘||&轉入賬戶);
END IF;
-- 如果沒有異常,則提交事務
COMMIT;
DBMS_OUTPUT.PUT_LINE(‘轉賬成功‘);
EXCEPTION
WHEN OTHERS THEN
ROLLBACK; -- 出現異常則回滾事務
DBMS_OUTPUT.PUT_LINE(‘轉賬失敗:‘);
DBMS_OUTPUT.PUT_LINE(SQLERRM);
END;
原文連接:Oracle數據庫之事務
【Oracle】淺析Oracle中的事務