下面回顾一下为解决前面触发器死锁问题所碰到过的知识:
1、Oracle锁信息查看:v$lock
主要字段含义:
kaddr -> v$session.lockwait
type ->锁类型,除系统本身的锁外,我们只要关注TX/TM/UL三种。而UL是用户自定义的锁,通常关注度不大。所以只剩下TX和TM两种。
TX是一种行级锁,通常以一个事务为单位的形式存在,也就是说并不是若干行被锁住了就出现若干个TX行级锁。
SQL> conn scott/tiger;
已连接。
SQL> select * from dept;
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING00 NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
SQL> select * from salgrade;
GRADE LOSAL HISAL
---------- ---------- ----------
1 700 1200
2 1201 1400
3 1401 2000
4 2001 3000
5 3001 9999
SQL> update dept d set d.dname = d.dname || '00' where d.deptno = 10;
已更新 1 行。
SQL> select * from v$lock l where l.type in('TX','TM');
ADDR KADDR SID TY ID1 ID2 LMODE REQUEST CTIME BLOCK
-------- -------- ---------- -- ---------- ---------- ---------- ---------- ---------- ----------
67B92964 67B92A70 12 TX 65543 3265 6 0 3 0
67B4E074 67B4E088 12 TM 30137 0 3 0 3 0
SQL> update salgrade s set s.losal = s.losal + 10 where s.grade=1;
已更新 1 行。
SQL> select * from v$lock l where l.type in('TX','TM');
ADDR KADDR SID TY ID1 ID2 LMODE REQUEST CTIME BLOCK
-------- -------- ---------- -- ---------- ---------- ---------- ---------- ---------- ----------
67B92964 67B92A70 12 TX 65543 3265 6 0 18 0
67B4E0F8 67B4E10C 12 TM 30142 0 3 0 3 0
67B4E074 67B4E088 12 TM 30137 0 3 0 18 0
从以上实验可以看出
第一次update dept表的时候就出现了一个TX锁,另一个TM锁表示的是锁定的对象(dept)。
此时对应的ID1就是对应的object_id(v$lock.type='TM'时)。
第二次update salgrade表的时候,出现了两个TM锁,因为此时锁定了两个对象(dept和salgrade)。
但是,对应的TX锁却还是只有一个。
为什么第二次明明锁定了两行,却只有一个锁呢?这是由于实际TX锁并不是真正意义上的行级锁,而是事务级的。相面这段话是引用funyucat写的《Oracle多粒度封锁机制研究》中的一段话:
TX的本义是Transaction(事务),当一个事务第一次执行数据更改(Insert、Update、Delete)或使用SELECT… FOR UPDATE语句进行查询时,它即获得一个TX(事务)锁,直至该事务结束(执行COMMIT或ROLLBACK操作)时,该锁才被释放。所以,一个TX锁,可以对应多个被该事务锁定的数据行。(在我们用的时候多是其一个事务,然后SELECT… FOR UPDATE NOWAIT)这段话让我少走了不少弯路,我一直在追求能够从v$lock中获取每行锁定标识的踪影。从这里发现我这个想法是不现实的。
这点从v$session表里面也可以看出。虽然v$session表有记录锁表信息的四个字段:
row_wait_obj#
row_wait_file#
row_wait_block#
row_wait_row#
(注:这个四个字段分别对应了rowid的四个组成信息:即object_id、relative_fileno、block_number、row_number。
不过这里需要注意的一点是row_id中的object_id实际上是指user_object中的data_object_id。这点在转换的时候需要注意。)
但是实际上这里指的是最后一次锁定的行的信息。并不是查找到当时所有锁定行的信息。 这点很重要,直接导致了不能找到这些锁定行的信息。
SQL> select s.SID, ta.deptno, o.object_name, ta.rowid
2 from v$session s, dept ta, user_objects o, v$locked_object lo
3 where lo.SESSION_ID = s.SID
4 and lo.OBJECT_ID = o.object_id
5 and dbms_rowid.rowid_object(ta.rowid) = o.data_object_id
6 and o.object_id = s.ROW_WAIT_OBJ#
7 and dbms_rowid.rowid_relative_fno(ta.rowid) = s.ROW_WAIT_FILE#
8 and dbms_rowid.rowid_block_number(ta.rowid) = s.ROW_WAIT_BLOCK#
9 and dbms_rowid.rowid_row_number(ta.rowid) = s.ROW_WAIT_ROW#;
SID DEPTNO OBJECT_NAME ROWID
---------- ---------- --------------------------- --------------------------------------------------
13 10 DEPT AAAHW5AABAAAMUSAAA
sid字段:指的是当前所在session的id,即v$session.sid
id1/id2字段:
id1字段上面已经提到过,当v$lock.type='TM'的时候,id1字段指的就是object_id。类似信息也可以从v$locked_object中找到:
SQL> desc v$locked_object;
名称 是否为空? 类型
----------------------------------------------------- -------- ------------------------------------
XIDUSN NUMBER
XIDSLOT NUMBER
XIDSQN NUMBER
OBJECT_ID NUMBER
SESSION_ID NUMBER
ORACLE_USERNAME VARCHAR2(30)
OS_USER_NAME VARCHAR2(30)
PROCESS VARCHAR2(12)
LOCKED_MODE NUMBER
这个表对应有xidusn、xidslot、xidsqn的信息,这个可以跟v$transaction表对应的字段进行关联。
这三个字段可以在v$lock.type='TX'的时候换算得到:
xidusn=(id1/65536)
xidslot=mod(id1,65536)
xidsqn=id2
lmode及block字段:
lmode这个字段显示了锁定的模式:
锁模式:0(None),1(null),2(row share), 3(row exclusive),4 (share),5(share row exclusive),6(exclusive)
share是指共享锁(select等)。exclusive是指排他锁(update/delete/select ...for update(nowait)/)。
通常共享锁可以多个锁定(都为s锁),且不能加x锁。
而排他锁则是单个锁定(x锁),不能再加其他锁。
block字段显示当前锁是否阻塞了其他锁:
0, 未阻塞其他进程;
1, 阻塞了其他进程;
2, 'Global', /* This lock is global, so we can't tell */
当lmode>0且block=1时即表明该表处于死锁状态,也就是说当时真锁定了一行记录,但同时又阻塞了其他进程,下面一条记录显示了死锁的情况:
SQL> select * from v$lock l where l.type in('TM','TX');
ADDR KADDR SID TY ID1 ID2 LMODE REQUEST CTIME BLOCK
-------- -------- ---------- -- ---------- ---------- ---------- ---------- ---------- ----------
67BA143C 67BA1548 10 TX 393255 4314 6 0 81 1
................
已选择6行。
当发生死锁后,Oracle会自动解锁(>9ir1),并提示“等待资源时检测到死锁”
SQL> update dept d set d.dname = d.dname || '00' where d.deptno = 10;
已更新 1 行。
SQL> update salgrade s set s.losal = s.losal + 10 where s.grade=1;
update salgrade s set s.losal = s.losal + 10 where s.grade=1
*
ERROR 位于第 1 行:
ORA-00060: 等待资源时检测到死锁
自动解锁后,刚才的blcok=1及lmode>0的情况消失。
另外两个字段:
ctime:Time since current mode was granted
request:
Lock mode in which the process requests the lock:
0 - none
1 - null (NULL)
2 - row-S (SS)
3 - row-X (SX)
4 - share (S)
5 - S/Row-X (SSX)
6 - exclusive (X)
--大于0时,表示当前会话被阻塞,其它会话占有改锁的模式
以上是关于v$lock视图的一些字段说明。另外也涉及到了v$session、v$locked_object等视图的信息。