首页 > 代码库 > Oracle Lock(Enqueues)
Oracle Lock(Enqueues)
数据库是一个多用户使用的共享资源。当多个用户并发地存取数据时,在数据库中就会产生多个事务同时存取一数据的情况。若对并发操作不加控制就可能会读取和存储不正确的数据,破坏数据库的一致性。
在数据库中有两种基本的锁类型:排他锁(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 SID4 ----------5 1
1 SQL> select sid from v$mystat where rownum=1;2 3 SID4 ----------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? Type7 ------------------ -------- ------------------------
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 BLOCK4 ---------- -- ---------- ---------- ---------- ---------- ----------5 1 TM 73545 0 3 0 06 1 TX 589837 745 6 0 17 48 TM 73545 0 3 0 08 48 TX 524309 964 6 0 09 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 NAME4 ---------- ----------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 BLOCK4 ---------- -- ---------- ---------- ---------- ---------- ----------5 1 TM 73545 0 3 0 06 1 TX 589841 745 6 0 17 48 TM 73545 0 3 0 08 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 BLOCK4 ---------- -- ---------- ---------- ---------- ---------- ----------5 1 TM 73545 0 3 0 06 1 TX 458783 613 6 0 17 48 TM 73545 0 3 0 08 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 BLOCK4 ---------- -- ---------- ---------- ---------- ---------- ----------5 1 TM 73545 0 3 0 06 1 TX 458783 613 6 0 17 48 TM 73545 0 3 0 08 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 010 1 TM 73549 0 3 0 011 1 TX 327703 784 6 0 012 13 SQL> select object_name from dba_objects where object_id in (73547,73549);14 15 OBJECT_NAME16 --------------------------------------------------------------------------------17 TAB118 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 010 48 TX 589851 746 6 0 011 48 TX 327703 784 0 4 012 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 not12 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 010 1 TM 73549 0 3 0 011 1 TX 131085 737 6 0 012 13 SQL> select object_name from dba_objects where object_id in (73547,73549);14 15 OBJECT_NAME16 --------------------------------------------------------------------------------17 TAB118 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 3310 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 BLOCK14 ---------- -- ---------- ---------- ---------- ---------- ----------15 1 TM 73549 0 3 0 016 1 TM 73547 0 3 0 017 1 TX 131085 737 6 0 018 48 TM 73547 0 3 0 019 48 TX 524313 972 6 0 0
在主表中并没有被阻塞。
3.给出一个导致死锁的SQL实例。
该实验中将使用到表txt。
首先查看txt的内容,再在一个会话中插入一条记录:
Session 1:
1 SQL> select * from txt;2 3 no rows selected4 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