數據庫是一個多用戶使用的共享資源。當多個用戶並發地存取數據時,在數據庫中就會產生多個事務同時存取一數據的情況。若對並發操作不加控制就可能會讀取和存儲不正確的數據,破壞數據庫的一致性。
在數據庫中有兩種基本的鎖類型:排他鎖(Exclusive Locks,即X鎖)和共享鎖(Share Locks,即S鎖)。當數據對象被加上排他鎖時,其他的事務不能對它讀取和修改;加了共享鎖的數據對象可以被其他事務讀取,但不能修改。
根據保護對象的不同,Oracle數據庫鎖可以分為以下幾大類:
(1). DML lock (data locks,數據鎖):用於保護數據的完整性;
(2). DDL lock(dictionary locks,字典鎖):用於保護數據庫對象的結構(例如表、視圖、索引的結構定義);
(3). Internal locks和latches(內部鎖與閂):保護內部數據庫結構;
(4). Distributed locks(分布式鎖):用於OPS(並行服務器)中;
(5). PCM locks (並行高速緩存管理鎖):用於OPS(並行服務器)中。
在Oracle中最主要的鎖是DML(也可稱為data locks,數據鎖)鎖。DML鎖的目的在於保證並發情況下的數據完整性。在Oracle數據庫中,DML鎖主要包括TM鎖和TX鎖,其中TM鎖稱為表級鎖,TX鎖稱為事務鎖或行級鎖。
Oracle TM鎖的類型
鎖模式 |
鎖的描述 | 含義 | 鎖定表的SQL |
0 | None | ||
1 | Null | 空,本模式是oracle預留模式 | |
2 | Row Share(RS)又叫(SS) | 行級共享鎖,是限制最少的TM鎖,可以提供最高程度的並發性,其他會話可以對鎖定的表進行任何類型的DML操作,還可以與其他會話鎖並存。 |
Lock table t in row share mode; |
3 | Row Exlusive Table Lock(RX)又叫(SX) | 行級排他鎖,通常已經有事務在修改或者select...for update修改結果集。允許其他事務對鎖定的表進行select、insert、update、delete或lock table 同時鎖定一張表 | Lock table t in exclusive mode; |
4 | Share Table Lock(S) | 共享鎖,其他事務可以查詢鎖定的表但不能修改,只允許當前事務修改,但可以多個事務持有它。 | Lock table t in share mode; |
5 | Share Row Exclusive Table Lock (SRX)又叫SSX | 共享行級排他鎖,同一時間只允許一個事務持有和修改鎖定的表,其他事務可以查詢但不能修改。 | Lock table t in share row exclusive mode; |
6 | Exclusive Table Lock(X) | 排他鎖,是限制最高的TM鎖,禁止其他事務執行任何類型的DML語句或者鎖表,一個表一般只能一個6號鎖 。 | Lock table t in exclusive mode; |
1.行級共享鎖(lmode=2,Row Share Table Lock,RS)
加鎖語法:Lock Table TableName In Row Share Mode;
一個行級鎖(有時稱為Subshare Table Lock,簡稱,子共享鎖)需要該事務在被鎖定行的表上用update的形式加鎖。當有下面語句被執行的時候行級鎖自動加在操作的表上。
Select...from tablename...for update of ...;
允許的操作:行級共享鎖由一個事務控制,允許其它事務查詢、插入、更新、刪除或同時在同一張表上鎖定行。因此其它事務可以同時在同一張表上得到行級鎖、共享行級排他鎖、行級排他鎖、排他鎖。
禁止的操作:擁有行級鎖的事務不允許其它事務執行排他鎖,即:
Lock Table TableName In Exclusive Mode;
2.行級排他鎖(lmode=3,Row Exclusive Table Lock,RX)
加鎖語法:LOCK TABLE TableName IN ROW EXCLUSIVE MODE;
行級排他鎖(亦稱為Subexclusive Table Lock,簡稱SX,子排他鎖)通常需要事務擁有的鎖在表上被更新一行或多行。當有下面語句被執行的時候行級排他鎖被加在操作的表上。
INSERT INTO TableName...;
UPDATE TableName...;
DELETE FROM TableName...;
LOCK TABLE TableName IN ROW EXCLUSIVE MODE;
行級排他鎖比行級鎖稍微多一些限制。
允許的操作:行級排他鎖由一個事務擁有允許其它事務執行查詢、修改、插入、刪除或同時在同一張表上鎖定行。只有行級排他鎖的事務允許其它事務在同一張表上同時得到共享鎖和行級排他鎖。
禁止的操作:行級排他鎖由一個事務擁有防止其它事務手動鎖定表來排除其它事務的讀寫權限。因此,其它事務不允許在同一張表上使用以下的語句來執行行鎖事務。
LOCK TABLE table IN SHARE MODE;
LOCK TABLE table IN SHARE EXCLUSIVE MODE;
LOCK TABLE table IN EXCLUSIVE MODE;
3.共享鎖(lmode=4,Share Table Lock,S)
加鎖語法:Lock Table TableName In Share Mode;
允許的操作:一個共享鎖由一個事務控制,僅允許其它事務查詢被鎖定的表。一個有效的共享鎖明確地有Select..for update 形式鎖定行,或執行Lock Table TableName In Share Mode語法鎖定整個表,不允許被其它事務更新。
禁止的操作:一個共享鎖由一個事務來控制,防止其它事務更新該表或執行下面的語句:
Lock Table Tablename In Share Row Exclusive Mode;
Lock Table Tablename In row Exclusive Mode;
4.共享行級排他鎖(lmode=5,Share Row Exclusive Table Lock,SRX)
共享行級排他鎖有時也稱共享子排他鎖(Share Subexclusive Table Lock,SSX),它比共享鎖有更多限制。定義共享行級排他鎖的語法為:
LOCK TABLE TableName IN SHARE ROW EXCLUSIVE MODE;
允許的操作:僅允許一個事務在某一時刻得到的行級排他鎖。擁有行級排他鎖事務允許其它事務在被鎖定的表上執行查詢或使用Select...From TableName For Update...來准確的鎖定行而不能更新行。
禁止的操作:擁有行級排他鎖的事務不允許其它事務有除共享鎖外的其它形式的鎖加在同一張表上或更新該表。即下面的語句是不被允許的:
LOCK TABLE TableName IN SHARE MODE;
LOCK TABLE TableName IN SHARE ROW EXCLUSIVE MODE;
LOCK TABLE TableName IN ROW EXCLUSIVE MODE;
LOCK TABLE TableName IN EXCLUSIVE MODE;
5.排他鎖(lmode=6)
排他鎖是在鎖機制中限制最多的一種類型,允許加排他鎖的事務獨自控制對表的寫權限。
加鎖語法:Lock Table Tablename In Exclusive Mode;
允許的操作:在一個表中只能有一個事務對該表實行排他鎖,排他鎖僅允許其它事務查詢該表。
禁止的操作:擁有排他鎖的事務禁止其它事務執行其它DML類型的語句或在該表上加任何其它類型的鎖。
關於鎖的一些實驗:
1.分別模擬insert,update和delete造成阻塞的示例,並對v$lock中的相應的信息進行說明,給出SQL演示。
1.1、該實驗中將使用的兩個session:
1 SQL> select sid from v$mystat where rownum=1; 2 3 SID 4 ---------- 5 1
1 SQL> select sid from v$mystat where rownum=1; 2 3 SID 4 ---------- 5 48
使用到的一張表:
1 SQL> create table txt (id int primary key,name varchar2(10)); 2 3 Table created. 4 5 SQL> desc txt; 6 Name Null? Type 7 ------------------ -------- ------------------------
8 ID NOT NULL NUMBER(38) 9 NAME VARCHAR2(10)
1.2、模擬insert造成的阻塞
在Session 1中插入一個語句:
1 SQL> insert into txt values (1,'aa'); 2 3 1 row created.
此時Session 1還沒有commit,但是Session 48中插入下面語句:
使用下面的語句查詢得到:
1 SQL> select sid,type,id1,id2,lmode,request,block from v$lock where type in ('TX','TM') order by 1,2; 2 3 SID TY ID1 ID2 LMODE REQUEST BLOCK 4 ---------- -- ---------- ---------- ---------- ---------- ---------- 5 1 TM 73545 0 3 0 0 6 1 TX 589837 745 6 0 1 7 48 TM 73545 0 3 0 0 8 48 TX 524309 964 6 0 0 9 48 TX 589837 745 0 4 0
從上圖可以看到sid=1的BLOCK=1表示這個會話正在阻塞其他的會話,LMODE=6表示鎖的級別是6,在sid=48中REQUEST=4表示當前會話正在等待一個LMODE=4的鎖,意思是,這個會話正在被阻塞。
1.3、模擬update造成的阻塞
我們先查詢一下表txt的內容:
1 SQL> select * from txt; 2 3 ID NAME 4 ---------- ---------- 5 1 aa
在Session 1中更改這一行:
1 SQL> update txt set name='zz' where id=1; 2 3 1 row updated.
此時Session 1不執行commit,在Session 48中也去更改該行:
使用下面語句查詢得:
1 SQL> select sid,type,id1,id2,lmode,request,block from v$lock where type in ('TX','TM') order by 1,2; 2 3 SID TY ID1 ID2 LMODE REQUEST BLOCK 4 ---------- -- ---------- ---------- ---------- ---------- ---------- 5 1 TM 73545 0 3 0 0 6 1 TX 589841 745 6 0 1 7 48 TM 73545 0 3 0 0 8 48 TX 589841 745 0 6 0
此處不做說明,情況與insert大體類似。
1.4、模擬delete造成的阻塞
在Session 1中執行delete語句:
1 SQL> delete txt; 2 3 1 row deleted.
此時Session 48也執行delete語句:
使用下面語句查詢得:
1 SQL> select sid,type,id1,id2,lmode,request,block from v$lock where type in ('TX','TM') order by 1,2; 2 3 SID TY ID1 ID2 LMODE REQUEST BLOCK 4 ---------- -- ---------- ---------- ---------- ---------- ---------- 5 1 TM 73545 0 3 0 0 6 1 TX 458783 613 6 0 1 7 48 TM 73545 0 3 0 0 8 48 TX 458783 613 0 6 0
1.5、對v$lock中的信息說明:
1 SQL> select sid,type,id1,id2,lmode,request,block from v$lock where type in ('TX','TM') order by 1,2; 2 3 SID TY ID1 ID2 LMODE REQUEST BLOCK 4 ---------- -- ---------- ---------- ---------- ---------- ---------- 5 1 TM 73545 0 3 0 0 6 1 TX 458783 613 6 0 1 7 48 TM 73545 0 3 0 0 8 48 TX 458783 613 0 6 0
其中:
SID:表示持有鎖的會話信息
TYPE:表示鎖的類型,值包括TM和TX等。
ID1:表示所的對象標識
ID2:ID1+ID2定位回滾段上的一個地址(即修改之前數據鏡像地址)
LMODE:鎖模式
REQUEST:申請鎖的模式
BLOCK:A value of either 0 or 1, depending on whether or not the lock in question is the blocker
2.模擬RI鎖定導致阻塞的場景,並分析v$lock相應的鎖定信息,給出SQL演示。
該實驗將創建兩張新表:
1 SQL> create table tab1 (id int primary key,name varchar2(10)); 2 3 Table created. 4 5 SQL> create table tab2 (id references tab1(id),num varchar2(10)); 6 7 Table created.
2.1、在Session 1中對表tab1插入一條記錄:
1 SQL> insert into tab1 values(1,'aa'); 2 3 1 row created. 4 5 SQL> select sid,type,id1,id2,lmode,request,block from v$lock where type in ('TX','TM') order by 1,2; 6 7 SID TY ID1 ID2 LMODE REQUEST BLOCK 8 ---------- -- ---------- ---------- ---------- ---------- ---------- 9 1 TM 73547 0 3 0 0 10 1 TM 73549 0 3 0 0 11 1 TX 327703 784 6 0 0 12 13 SQL> select object_name from dba_objects where object_id in (73547,73549); 14 15 OBJECT_NAME 16 -------------------------------------------------------------------------------- 17 TAB1 18 TAB2
從上圖可以看出,當在主表執行insert語句的時候,會給主表和從表都加上lmode=3的鎖,這時在Session 48中對表tab2插入一條記錄:
1 SQL> select sid,type,id1,id2,lmode,request,block from v$lock where type in ('TX','TM') order by 1,2; 2 3 SID TY ID1 ID2 LMODE REQUEST BLOCK 4 ---------- -- ---------- ---------- ---------- ---------- ---------- 5 1 TM 73547 0 3 0 0 6 1 TM 73549 0 3 0 0 7 1 TX 327703 784 6 0 1 8 48 TM 73549 0 3 0 0 9 48 TM 73547 0 3 0 0 10 48 TX 589851 746 6 0 0 11 48 TX 327703 784 0 4 0 12 13 7 rows selected.
這時就會造成阻塞,而且Session 48會申請一個lmode=4的鎖。
2.2、在Session 1中在表tab2插入一條記錄,但之前先看一下tab1是表的內容:
1 SQL> select * from tab1; 2 3 ID NAME 4 ---------- ---------- 5 1 aa 6 7 SQL> insert into tab2 values (2,'bb'); 8 insert into tab2 values (2,'bb') 9 * 10 ERROR at line 1: 11 ORA-02291: integrity constraint (JACK.SYS_C0010811) violated - parent key not 12 found
直接給從表插入記錄,如果主表沒有的話會報錯違反引用完整性約束,沒有主表依據。
2.3、在Session 1中對表tab執行delete操作:
1 SQL> delete from tab2; 2 3 1 row deleted. 4 5 SQL> select sid,type,id1,id2,lmode,request,block from v$lock where type in ('TX','TM') order by 1,2; 6 7 SID TY ID1 ID2 LMODE REQUEST BLOCK 8 ---------- -- ---------- ---------- ---------- ---------- ---------- 9 1 TM 73547 0 3 0 0 10 1 TM 73549 0 3 0 0 11 1 TX 131085 737 6 0 0 12 13 SQL> select object_name from dba_objects where object_id in (73547,73549); 14 15 OBJECT_NAME 16 -------------------------------------------------------------------------------- 17 TAB1 18 TAB2
當對從表執行delete操作的時候會給主表和從表加上lmode=3的鎖,這時在Session 48中對表tab1中更改記錄:
1 SQL> update tab1 set name='33' where id=1; 2 3 1 row updated. 4 5 SQL> select * from tab1; 6 7 ID NAME 8 ---------- ---------- 9 1 33 10 11 SQL> select sid,type,id1,id2,lmode,request,block from v$lock where type in ('TX','TM') order by 1,2; 12 13 SID TY ID1 ID2 LMODE REQUEST BLOCK 14 ---------- -- ---------- ---------- ---------- ---------- ---------- 15 1 TM 73549 0 3 0 0 16 1 TM 73547 0 3 0 0 17 1 TX 131085 737 6 0 0 18 48 TM 73547 0 3 0 0 19 48 TX 524313 972 6 0 0
在主表中並沒有被阻塞。
3.給出一個導致死鎖的SQL實例。
該實驗中將使用到表txt。
首先查看txt的內容,再在一個會話中插入一條記錄:
Session 1:
1 SQL> select * from txt; 2 3 no rows selected 4 5 SQL> insert into txt values (2,'cc'); 6 7 1 row created.
在另一個會話中插入另一個記錄:
Session 48:
1 SQL> insert into txt values (3,'gg'); 2 3 1 row created.
此時在Session 1中插入與Session 48一樣的記錄:
在Session 62中插入與Session最初的記錄:
此時兩個Session都被阻塞了,而且是session 48先阻塞了Session 1,Session 1再阻塞了Session 48,這就產生了死鎖。這時Oracle會自動解決死鎖的問題,把先前鎖住的會話強制結束,並報出ORA 60錯誤。
1 SQL> insert into txt values (3,'gg'); 2 insert into txt values (3,'gg') 3 * 4 ERROR at line 1: 5 ORA-00060: deadlock detected while waiting for resource