1. 什麼是事務sql
在數據庫中事務是工做的邏輯單元,一個事務是由一個或多個完成一組的相關行爲的SQL語句組成,經過事務機制確保這一組SQL語句所做的操做要麼都成功執行,完成整個工做單元操做,要麼一個也不執行。數據庫
如:網上轉賬就是典型的要用事務來處理,用以保證數據的一致性。緩存
2. 事務特性服務器
SQL92標準定義了數據庫事務的四個特色:併發
一組SQL語句操做要成爲事務,數據庫管理系統必須保證這組操做的原子性(Atomicity)、一致性(consistency)、隔離性(Isolation)和持久性(Durability),這就是ACID特性。oracle
3. 數據異常分佈式
由於Oracle中支持多個事務併發執行,因此會出現下面的數據異常。性能
3.1 髒讀spa
當一個事務修改數據時,另外一事務讀取了該數據,可是第一個事務因爲某種緣由取消對數據修改,使數據返回了原狀態,這是第二個事務讀取的數據與數據庫中數據不一致,這就叫髒讀。日誌
如:事務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:它是默認設置,該選項表示在事務中能夠有訪問語句、修改語句,但不常用。
設置隔離級別
設置一個事務的隔離級別:
注意:這些語句是互斥的,不能同時設置兩個或兩個以上的選項。
設置單個會話的隔離級別:
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數據庫鎖可分爲:
在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;