數據庫事務
事務的概念:
事務是多用戶數據庫的重要機制,事務處理是所有RDBMS(當然包括Oracle)的核心。事實上,沒有事務處理,就沒有RDBMS實現的可能性。事務處理技術主要包括數據庫的恢復技術和並發控制技術。據庫恢復技術和並發控制技術是數據庫管理系統的重要組成部分。數據的安全保護措施是否有效,是衡量數據庫好壞的主要性能指標之一。
Oracle提供了一種“事務”的控制機制,能夠保證對數據進行有效、安全的操作,使數據庫中的數據始終牌一個數據一致性的狀態。在關系數據庫中,
事務(Transaction)是一系列的數據庫操作,由一條或多條相關的SQL語句組成,是數據庫應用程序的基本邏輯單位。數據存儲的邏輯單位是數據塊,數據操作的邏輯單位是事務。事務與應用程序是兩個不同的概念。通常情況下,一個應用程序包括多個事務。事務是用戶定義的一個數據庫操作序列,這些操作要麼全做,要麼全不做,是一個不可分割的工作單位。
事務就是一組包含一條或多條語句的邏輯單元,每個事物都是一個原子單位,在事物中的語句被作為一個整體,要麼被一起提交,作用在數據庫上,使數據庫數據永久的修改,要麼一起撤銷對數據庫不做任何修改。
事務的ACID特性:
事務具有四大性質,包括原子性(Atomicity)、一致性(Consistency)、隔離性(Isolation)和持續性(Durability)。它們可簡稱為事務的ACID特性。
------------------------
原子性:
事務是數據庫中一個不可分割的邏輯單位(或操作序列),一個事務中的所有操作要麼都被成功地做完,要麼都不做。只要有一個不成功,就會被自動回退。否則數據庫中的數據就會處於一個不一致狀態。未提交對數據的更新操作是可以被退回的,提交之後的更新操作是不能被回退的,可以手動回退或手動提交。
------------------------
一致性:
一致性是指一個語句、一個事務操作後的結果(事務執行的結果)必須要使數據庫中的所有數據處於一種邏輯上的一致性狀態。換言之,數據庫必須從一個一致性狀態到另一個一致性狀態。如在語句、事務處理開始之前,數據庫中的所有數據都是滿足已經設置的各種約束條件或業務規則的,在語句、事務處理完成之後,數據雖然不同以往,但它們必須仍然滿足先前設置的約束條件或業務規則。
------------------------
隔離性:
隔離性是指事務的執行不能被其他的事務所干擾。即一個事務內部的操作與使用的數據,對其他事務是隔離的,並發執行的各個事務之間不能互相干擾。在提交之前,只有該事務的用戶才能夠操作以正在修改的數據,而其他事務的用戶只能看到修改之前的數據。
隔離性是數據庫允許對其中的數據並發修改和讀取的能力。如果沒有隔離性,一個事務就有可能讀取另一個事務正在操作的、但還沒有提交的、設置還可能處於回退的中間數據,而因此做出的錯誤的操作或決策。
------------------------
持續性:
持續性也稱為永久性,它是指一個事務一旦被提交成功,它對數據庫中的數據所做的修改也就永久地保存下來了。這些修改不會由於系統故障或錯誤而消失。換言之,這個數據一致性的狀態是可以被恢復的,不會被丟失的。接下來的其他操作都是在些基礎上進行的,不會對其執行結果有任何影響。
Oracle提供的事務控制是隱式自動開始的,它不需要用戶顯示地使用語句開始事務處理。
但該事務控制也包括以下幾種語言:
提交事務(COMMIT)、
回退事務(ROLLBACK)、
設置保存點(SAVEPOINT)、
回退到保存點(ROLLBACK TO SAVEPOINT)、設
置事務的屬性(SET TRANSACTION)、
設置可延遲約束的檢驗時機(SET CONSTRANTS)。
事物在沒有提交之前可以回滾、而且在提交前用戶可以查看已經修改的數據,但其他用戶查看不到,一旦事物提交就不能再撤銷修改了。
Oracle的事物基本控制語句有如下幾個:
SET TRANSACTION:設置事物的屬性
COMMIT:提交事物
SAVEPOINT:設置保存點
ROLLBACK:回滾事物
ROLLBACK TO SAVEPOINT:回滾至保存點
事務的開始與結束
開始
事務是用來分隔數據庫操作的邏輯單位,因此事務是有起點和終點的。Oracle的一個重要特點就是沒有“開始事務處理”的語句。用戶不能也不必顯式地開始一個事務處理。
當發生如下事件之一時,事務就開始了。
(1)連接到數據庫,並開始執行第一條DML語句時;
(2)當一個事務結束或者執行一條會自動提交事務的語句時。
結束
當發生如下事件之一,事務就結束了:
(1)執行一條會自動提交事務的語句時;
(2)顯式地使用提交(Commit)語句或回滾(Rollback)語句時;
(3)執行一條DML語句該語句卻失敗了,此時會因為這個失敗的DML語句而自動回退事務;
(4)在SQL*PLUS中,正常的退出(使用exit命令或是quit命令),Oracle會自動對事務進行提交;
(5)如果直接關閉SQL*PLUS,會自動對事務進行回滾;
(6)在SQL*PLUS中,Autocommit設置為ON,事務也會自動提交;
(7)在執行SQL*PLUS中,如遇到計算機突然斷電、崩潰等,就會自動執行Rollback語句;
(8)執行一條DDL語句(如Create Table, Alter Table, Drop Table, Alter System)後;
(9)執行一條DCL語句(如 Grant, Revoke, Audit, Noaudit, Rename)後;
(10)斷開與數據庫的連接時。
提交與回滾事務
一般來說,當一個事務的邏輯單位結束時,或者應該用Commit語句提交,或者應該用Rollback語句回退。
提交事務
在用DML語句對數據庫進行操作之後,如果要在數據庫中永久性地保存操作結果,就需要使用Commit命令來提交事務。也就是說,通知數據庫管理系統將該事務對數據庫所做的操作全部保存到操作系統中。注意:至少要先保存到“重做日志文件”中,然後再保存到“數據文件”中。提交成功之後,其他會話就可以查看到操作後的數據了。
回退事務
如果不想讓執行的SQL語句生效,就需要使用Rollback語句回退事務。回退一個事務就意味著在該事務中對數據庫進行的全部操作被撤銷,Oracle利用回退來修改存儲前的數據,通過重做日志來記錄對數據所做的修改。也就是說,回退可以終止用戶的事務處理,並撤銷用戶已經進行的所有更改。
從事務開始到回退時的所有SQL語句的操作都不會被記錄到數據庫中。換言之,如果在對數據庫的修改過程中出現了程序數據庫方面的錯誤,或用戶不打算保存他們所做的操作結果時就可以使用Rollback命令來回退全部事務。將該事務目前對數據庫所做的更改全部回退到上一個提交成功的狀態。
與提交事務不同,回退事務所需要的時間取決於所要撤銷的數據庫操作或逐句的數量。顯然,這是因為回退事務必須要在物理上撤銷所做的操作。回退的功能是一個對錯誤的異常處理辦法,非常時期(系統停/斷電、人為破壞等特殊情況)是必需的。正常時不要使用,否則就無法在數據庫中保留操作的結果了。
當進行部分回退的時候,Oracle執行的任務是:
(1)撤銷保存點之後所胡已經執行的更改,但保留保存點之前的更改。
(2)釋放保存點之後各個SQL語句所占用的系統資源,並解除對所涉及的操作對象的鎖定。但保留保存點之前各個SQL語句所占用的系統資源和對所涉及的操作對象的鎖定。
(3)給用戶返回一個回退到保存點的成功提示的代碼。
(4)用戶可以繼續執行當前的事務。
並發事務
並發事務的問題:
對數據的保護包含兩方面的內容:
防止合法用戶的操作對數據庫造成意外的破壞;
防止非法用戶的操作對數據庫造成故意的破壞。
數據庫的基本特征就是允許多用戶並發訪問。雖然很多的用戶並發訪問,能提高數據資源的使用效率,但是也會引起資源的爭用和數據不一致等一系列的問題。
事務是並發控制的基本單位,保證了事務ACID特性是事務處理的重要任務。事務的特性受到破壞原因之一就是多個事務對數據庫的並發操作所造成的結果
在並發事務情況下,存在3種現象會對數據庫造成破壞:
(1)讀“髒”數據 (Dirty Read)--髒讀:
一個事務讀取到了另一個事務中還沒有提交、更改過的數據。其效果就像是打開了其他人正在更改的WORD,不是最後的定稿的文檔。在這種情況下,數據可能並不是一致性的。
(2)不可重復讀(Non-Repeatable Read):
當一個事務T1讀取了一些數據後,另一個事務T2修改了這些數據並進行了提交。這樣一來,當該T1事務再次讀這些數據時,發現這些數據已經被修改了。
(3)丟失修改(Lost Update):
兩個事務T1和T2讀入同一數據並修改,T2提交的結果破壞了T1提交的結果,導致T1的修改被丟失。
事務鎖
數據庫是一個龐大的多用戶數據管理系統,由於在多用戶的系統中,同一時刻多個用戶同時操作某相同資源的情況時有發生,邏輯上這些用戶想同時操作該資源是不可能的,數據庫利用鎖消除多用戶操作同一資源時可能產生的隱患。
分類
並發操作帶來的3大問題的原因就是在於並發操作破環了事務的隔離性。那麼該如何解決這3個問題呢?我們所使用的方法就是—鎖。
鎖就是對某個資源或是對象加以鎖定,從而起到限制和防止其他用戶訪問的作用,保證數據的一致性和完整性。
鎖是Oracle數據庫用來控制並發訪問的一種很重要的機制。
它的基本類型分為兩種:一種是排它鎖,另一種是共享鎖。
排他鎖(Exclusive Lock)又稱X鎖:
如果一個事務在某個數據對象上建立了排他稱,那麼只有該事務可以對該數據對象進行修改、插入和刪除等更新操作,而其他事務則不能對該數據對象加上任何類型的鎖;
共享鎖(Share Lock)又稱S鎖。
如果一個事務在某個數據對象上建立了共享鎖,則該事務可以對數據對象進行讀操作,進而能進行修改、刪除等更新操作,其他的事務也只能對該數據對象加上S鎖進行讀取,而不能加上X鎖進行修改、刪除等更新操作。
加鎖
一般情況下,鎖是由Oracle數據庫自動維護的,一般的查詢語句是不用加任何鎖的。執行DDL、DML操作的時候,Oracle數據庫會自動進行加鎖,這時你也可以選擇手動的方式用語句進行加鎖。
手動方式加鎖分為LOCK TABLE語句對表加鎖和SELECT FOR UPDATE語句對行記錄進行加鎖
1)LOCK TABLE語句
LOCK TABLE語句的語法格式為:
Lock table talbe_name in lockmode nowait/wait n
其中,table_name指定要鎖定的表或視圖;lockmode表示鎖定的模式,選項有row exclusive、share、row share、 share row exclusive和exclusive;wait關鍵字表示用於指定等待其他用戶釋放鎖的秒數,防止無期限的等待;nowait關鍵字表示不必等待要鎖定的表上的鎖釋放而直接返回。
(2)select for update語句
select for update語句允許用戶一次鎖定多條記錄。
它的語法格式為:
select 語句 for update[of column][wait n/nowait][skip lock]
其中,of關鍵字指出將要更新的列,鎖定行上的特定的列;wait關鍵字表示等待其他用戶釋放鎖的秒數,防止 無何止的等待;nowait關鍵字表示不必等待鎖定的數據行上的鎖釋放可直接返回;使用skip lock子句表示可超過鎖定的行,不會報告由wait n引發的異常報告。
鎖等待
在某些情況下由於占用的資源不能及時釋放,而造成鎖等待,也可叫鎖沖突。例如,當一個會話修改表A的記錄時,它會對該記錄加鎖,而此時另一個會話也來修改此記錄,那麼第二個會話因得不到排他鎖而一直等待,會出現長時間沒有響應的現象,直到第一個會話把事物提交,釋放鎖,第二個回話才能對數據庫進行操作。
死鎖
死鎖是鎖等待的一個特例,通常發生在兩個或多個會話間。假設一個會話想要修改兩個資源對象,修改兩個資源的操作在一個事物當中。當它修改第一個對象時需要對其鎖定,然後等待第二個對象。這是如果另外一個會話也需要修改這兩個資源對象,並且已經獲得並鎖定了第二個對象,那麼就會出現死鎖。因為當前會話鎖定了第一個對象等待第二個對象,而另一個會話鎖定了第二個對象等待第一個對象,這樣兩個會話都得不到想要得到的對象,於是出現死鎖。
應用實例:
--建表語句
-- Create table
create table EMP
(
empno NUMBER(4) not null,
ename VARCHAR2(10),
job VARCHAR2(9),
mgr NUMBER(4),
hiredate DATE,
sal NUMBER(7,2),
comm NUMBER(7,2),
deptno NUMBER(2)
)
tablespace USERS
pctfree 10
initrans 1
maxtrans 255
storage
(
initial 64K
minextents 1
maxextents unlimited
);
-- Create/Recreate primary, unique and foreign key constraints
alter table EMP
add constraint PK_EMP primary key (EMPNO)
using index
tablespace USERS
pctfree 10
initrans 2
maxtrans 255
storage
(
initial 64K
minextents 1
maxextents unlimited
);
alter table EMP
add constraint FK_DEPTNO foreign key (DEPTNO)
references DEPT (DEPTNO);
-
--Oracle默認開啟事務:
insert into emp(empno,ename) values(8461,'test');
rollback;
commit;
--關閉/開啟事務:
set autocommit on/off;
--保存點
-- 在事物中可以根據自己的需要設置保存點,保存點可以設置在事物的任何地方,也可以設置多個點,這樣就可以把比較長的事物根據需要分成小段,
--當數據出現問題時可以不用全部回滾,只需要回滾到保存點即可。
insert into emp(empno,ename) values(8471,'test1');
savepoint first;
insert into emp(empno,ename) values(8472,'test1');
savepoint second;
insert into emp(empno,ename) values(8473,'test3');
savepoint third;
insert into emp(empno,ename) values(8474,'test4');
#回滾到savepoint second,保存點second之前的test1和test2將會被提交
rollback to second;
commit;