(c)DML锁
根据锁定的粒度和意向,DML锁有几种模式。
RS(Row Share)或SS(Sub Share)
RX(Row eXclusive)或SX(Sub eXclusive)
S(Share)
SRX(Share Row eXclusive)或SSX(Share Sub eXclusive)
X(eXclusive)
可以从%ORACLE_HOME%/RDBMS /ADMIN目录中的dbmslock.sql脚本中找到DML锁的模式的相容规则。
==================
-- These are the various lock modes (nl -> "NuLl", ss -> "Sub Shared",
-- sx -> "Sub eXclusive", s -> "Shared", ssx -> "Shared Sub eXclusive",
-- x -> "eXclusive").
--
-- A sub-share lock can be used on an aggregate object to indicate that
-- share locks are being aquired on sub-parts of the object. Similarly, a
-- sub-exclusive lock can be used on an aggregate object to indicate
-- that exclusive locks are being aquired on sub-parts of the object. A
-- share-sub-exclusive lock indicates that the entire aggregate object
-- has a share lock, but some of the sub-parts may additionally have
-- exclusive locks.
--
-- Lock Compatibility Rules:
-- When another process holds "held", an attempt to get "get" does
-- the following:
--
-- held get-> NL SS SX S SSX X
-- NL SUCC SUCC SUCC SUCC SUCC SUCC
-- SS SUCC SUCC SUCC SUCC SUCC fail
-- SX SUCC SUCC SUCC fail fail fail
-- S SUCC SUCC fail SUCC fail fail
-- SSX SUCC SUCC fail fail fail fail
-- X SUCC fail fail fail fail fail
==================
(d)数据库级别锁定
Oracle可以在数据库、表、行这3个级别上使用锁。
锁定数据库有2种方法:将数据库设置成受限方式、将数据库更改成只读方式。
在shutdown数据库后,通过startup restrict命令来在启动数据库时将其设置成受限方式。也可以通过alter system enable restricted session或alter system disable restricted session语句来在打开数据库的情况下启用或禁用数据库的受限方式。当数据库处于受限方式时,只允许具有 restrictive session系统权限的用户(如sys用户)连接到数据库。已经连接的会话不受alter system enable restricted session语句的影响,只有试图进行连接的新的会话会被限制。
将数据库更改成read only(只读)方式后,只能查询数据而不允许对数据进行任何DML操作。
SQL> STARTUP MOUNT
SQL> ALTER DATABASE OPEN READ ONLY;
如果要将数据库再按读写方式打开,可以用STARTUP FORCE命令重启数据库。
set transaction read only语句可以将事务设置成只读事务,即将数据库"冻结"到该事务开始的那一点上,直至显式地发布了COMMIT或ROLLBACK命令或隐式提交(执行DDL)。在只读事务中所查询到的数据反映的是该事务开始时已经存在的数据库中的数据,即便在此同时其他事务更改并提交了数据库中的数据也是如此,但并不锁定数据库。
(e)表级锁定和行级锁定
在Oracle数据库中,DML锁主要包括TM锁和TX锁,其中TM锁称为表级锁,TX锁称为事务锁或行级锁。
当发布一个insert、update、 delete、select......for update语句时都会自动在被操作的表上加表级别的锁(即TM类型的锁)。也可以用Lock Table语句专门设置一个表级别的锁。表级别的锁被用于在操作表中数据期间(还没提交或回退事务),防止其他用户对表的结构进行更改。
Lock Table语句的语法是:LOCK TABLE [schema.]table_name IN lock_mode MODE [NOWAIT];
lock_mode表示锁的模式,取值是SHARE、ROW SHARE、ROW EXCLUSIVE、SHARE ROW EXCLUSIVE、EXCLUSIVE;
当发布一个insert、update、 delete、select......for update语句时都会自动在被操作的行上加行级别的锁(即TX类型的锁)。行级别的锁被用于在操作表中数据期间(还没提交或回退事务),防止其他用户对正在操作的行的数据进行更改。
当Oracle 执行DML语句时,系统自动在所要操作的表上申请TM类型的锁。当TM锁获得后,系统再自动申请TX类型的锁,并将实际锁定的数据行的锁标志位进行置位。要想获得某个表上的TX锁,事务必须首先获得该表上的TM锁。这样在事务加锁前检查TX锁相容性时就不用再逐行检查锁标志,而只需检查TM锁模式的相容性即可。TM锁包括了SS、SX、S、SSX、X 等多种模式,在数据行上只有X锁(排他锁)。
在Oracle中,查询(不带for update子句的select语句)语句不会锁定数据。即使一个事务已经锁定了几行记录或整个表,查询总是不需要为加锁而等待的。Oracle的查询是通过使用在撤销表空间中所存储的、数据被锁定之前的前映像,来不需要等待地、成功地执行的。这种读取前映像的方法提高了数据的并发度,还保证了事务不会读取到脏数据。
Select...For Update语句所加的锁与update语句所加的锁相同:一个行级别的EXCLUSIVE锁、一个表级别的ROW EXCLUSIVE锁。
Select...for update of column_list, 在多表连接锁定时,可以指定要锁定的是哪几张表,而如果表中的列没有在for update of 后面出现的话,就意味着这张表其实并没有被锁定,其他用户是可以对这些表的数据进行update操作的。这种情况经常会出现在用户对带有连接查询的视图进行操作场景下。用户只锁定相关表的数据,其他用户仍然可以对视图中其他原始表的数据来进行操作。
在Oracle中,锁的数量不受限制且不会自动升级。(有些数据库中,某个表上的行级锁达到一定数量后,这些行级锁就会被升级为该表上的1个表级锁,而取消这些行级锁)
(f)与锁相关的数据字典
V$LOCK视图/DBA_LOCKS视图:所有会话保持或申请的锁的信息
V$LOCKED_OBJECT视图:所有会话锁定的对象以及使用的锁的模式
DBA_WAITERS视图:显示所有被阻塞会话及其申请的锁和阻塞该会话的会话保持的锁的信息
DBA_BLOCKERS视图:显示阻塞了其他会话的那些会话
V$LOCK视图的各个列的说明如下:
列名 数据类型 说明
ADDR RAW(4) 在内存中锁定的对象的地址
KADDR RAW(4) 在内存中锁的地址
SID NUMBER 保持或申请锁的会话的标识号
TYPE VARCHAR2(2) 锁的类型:TX=行锁或事务锁;TM=表锁或DML锁;UL=PL/SQL用户锁
ID1 NUMBER 锁的第1标识号。TM锁,该值表示将要被锁定的对象的标识号;TX锁,该值表示撤销段号码的十进制值
ID2 NUMBER 锁的第2标识号:TM锁,该值为0;TX锁,该值表示交换次数
LMODE NUMBER 会话保持的锁的模式。0=None;1=Null;2=Row-S (SS);3=Row-X (SX);
4=Share;5=S/Row-X (SSX);6=Exclusive
REQUEST NUMBER 会话申请的锁的模式。与LMODE中的模式相同
CTIME NUMBER 以秒为单位的,获得当前锁(或转换成当前锁的模式)以来的时间
BLOCK NUMBER 当前锁是否阻塞另一个锁。0=不阻塞;1=阻塞
SQL> SELECT USERENV('SID') FROM DUAL;
USERENV('SID')
--------------
126
2
SQL> UPDATE T1 SET COL='B' WHERE COL='A';
1 row updated.
3
SQL> UPDATE T2 SET COL='B' WHERE COL='A';
1 row updated.
4
SQL> UPDATE T1 SET COL='B' WHERE COL='A';
5
SQL> UPDATE T2 SET COL='B' WHERE COL='A';
6
UPDATE T1 SET COL='B' WHERE COL='A'
*
ERROR at line 1:
ORA-00060: deadlock detected while waiting for resource
SQL>
用sys账户登陆,
SQL> SHOW PARAMETER BACKGROUND_DUMP_DEST;
NAME TYPE VALUE
-------------------------------------------------------
background_dump_dest string e:/app/ww/diag/rdbms/mytest/mytest/trace
打开E:/app/ww/diag/rdbms/mytest/mytest/trace/alert_mytest.log,最后面有如下记录:
Thu May 19 09:41:00 2011
ORA-00060: Deadlock detected. More info in file e:/app/ww/diag/rdbms/mytest/mytest/trace/mytest_ora_684.trc.
打开mytest_ora_684.trc可以看到详细死锁信息
(f) 检测锁争用
上面例子,第6步中oracle检测到死锁并产生ORA-00060错误放弃了会话126的第4步update操作后,会话120的第5步的表T2更新操作依然需要等待会话126的第3步的表T2更新操作提交或者回滚。可以用如下的SQL语句来查询阻塞的会话。
SQL> SET LINESIZE 120;
SQL> COLUMN BLOCKERS FORMAT A20;
SQL> COLUMN WAITERS FORMAT A50;
SQL> SELECT SB.SID||':'||SB.SERIAL#||' '||SB.USERNAME BLOCKERS,
2 SW.SID||':'||SW.SERIAL#||' '||SW.USERNAME||' '||SQLW.SQL_TEXT WAITERS
3 FROM V$LOCK LB, V$LOCK LW, V$SESSION SB, V$SESSION SW, V$SQLAREA SQLW
4 WHERE LB.ID1=LW.ID1
5 AND LB.BLOCK=1
6 AND LW.BLOCK=0
7 AND LB.SID=SB.SID
8 AND LW.SID=SW.SID
9 AND SW.SQL_ID=SQLW.SQL_ID;
BLOCKERS WAITERS
-------------------- --------------------------------------------------
170:6 HR 155:14 HR UPDATE T2 SET COL='B' WHERE COL='A'
SQL>
当出现检测到锁争用的信息后,用户自己(执行commit语句或rollback语句,使事务结束,释放所加的锁。还可以使用 ALTER SYSTEM KILL SESSION 'sid, serial#' 语句来杀死会话,强行解决锁争用。