Oracle锁的一些实验与技术知识

下面回顾一下为解决前面触发器死锁问题所碰到过的知识:
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等视图的信息。

请使用浏览器的分享功能分享到微信等