锁的查询及解锁
Oracle中的锁的功能很多,当然锁的属性不单是Oracle中所独有的,而是在DBMS中的基础特性所规定的。也就是说无论使用何种DBMS,其S、X、SX等锁的基本属性都是一致的。只是各自的实现形式上的差别。当然每个DBMS在内部实现上都会所不同,Oracle在这方面具有极大的优势。关于这一点,可以参见Tom的《9i&10g Programming Techniques And Solutions》。下面介绍一下:
一、死锁
死锁是指:两个事务同时希望锁定已经被另一事务锁定的数据,而形成的互相等待的情况。
举例:
SQL-1> lock table t1 in exclusive mode; --session1对t1表加X锁
Table(s) Locked.
SQL-2
> lock table t2 in share mode;--session2对t2表加S锁
Table(s) Locked.
SQL-1> lock table t2 in exclusive mode; --session1对t2表加X锁,等待
SQL-2
> lock table t1 in share mode;--session2对t1表加S锁,等待
SQL-1>
*
ERROR at line 1:
ORA-00060: deadlock detected while waiting for resource
--
最终session1中报错产生死锁
注:Oracle系统会自动、有效地对死锁进行检测,当检测到死锁后会选择一个事务退出,而另一个事务继续。
遇到死锁时,事务接到报错也不会自动回滚,而是继续等待,所以只有另一个事务用户提交,或者杀死进程才能最终解锁。
二、锁的查询
可以通过动态表v$lock和v$locked_object来查看当前锁的情况,具体表结构:
SQL> desc v$lock
Name Type Comments
------- ----------- -------------------------
ADDR RAW(4) 在内存中锁定的对象的地址
KADDR RAW(4) 在内存中锁的地址
SID NUMBER SESSION标识
TYPE VARCHAR2(2) 锁类型:TX=行锁或事务锁;TM=表锁或DML锁;UL=PL/SQL用户锁
ID1 NUMBER 锁的第1标识号。TM-将要被锁定的对象标识号;TX-撤销段号码的十进制值
ID2 NUMBER 锁的第2标识号。TM-0;TX-交换次数
LMODE NUMBER 锁的模式:0None;1Null;2RS;3RX;4S;5SRX;6X
REQUEST NUMBER 会话申请的锁的模式,与LMODE中的模式相同
CTIME NUMBER 已持有或等待锁的时间,以秒为单位
BLOCK NUMBER 当前锁是否阻塞另一个锁。0不阻塞;1阻塞
可以通过和v$session表关联,找出某个用户的锁。
SQL> desc v$locked_object
Name Type Comments
--------------- ------------ -------------------------
XIDUSN NUMBER 回滚段号码
XIDSLOT NUMBER 被锁定的对象在撤销段中的位置
XIDSQN NUMBER 序列号
OBJECT_ID NUMBER 被锁定的对象的标识号
SESSION_ID NUMBER 持有锁的session标识号
ORACLE_USERNAME VARCHAR2(30) 持有锁的用户ID
OS_USER_NAME VARCHAR2(30) 持有所的操作系统ID
PROCESS VARCHAR2(12) 操作系统进程号
LOCKED_MODE NUMBER 锁模式0None;1Null;2RS;3RX;4S;5SRX;6X
同理,可以关联dba_objects表,来查找某个表现有的锁。
SQL> desc dba_locks
Name Type Comments
--------------- ------------ -------------------------
SESSION_ID NUMBER 保持或申请锁的session标识号
LOCK_TYPE VARCHAR2(26) 锁的类型,同v$lock.type
MODE_HELD VARCHAR2(40) 保持的锁的模式
MODE_REQUESTED VARCHAR2(40) 申请的锁的模式
LOCK_ID1 VARCHAR2(40) 锁的第1标识号
LOCK_ID2 VARCHAR2(40) 锁的第2标识号
LAST_CONVERT NUMBER 已持有或等待锁的时间,以秒为单位
BLOCKING_OTHERS VARCHAR2(40) 当前锁是否阻塞另一个锁。Not Blocking不阻塞;Blocking阻塞
这个表基本等同于v$lock
SQL> desc dba_waiters
Name Type Comments
--------------- ------------ -------------------------
WAITING_SESSION NUMBER 等待锁(被阻塞)的session标识号
HOLDING_SESSION NUMBER 保持锁(正阻塞)的session标识号
LOCK_TYPE VARCHAR2(26) 锁的类型
MODE_HELD VARCHAR2(40) 保持的锁的模式
MODE_REQUESTED VARCHAR2(40) 申请的锁的模式
LOCK_ID1 NUMBER 锁的第1标识号
LOCK_ID2 NUMBER 锁的第2标识号
这个表是比较重要的,可以看出是哪些在等待的锁影响了系统
SQL> desc
dba_blockers
Name Type Comments
--------------- ------ -------------------------
HOLDING_SESSION NUMBER 阻塞了其他session的那些session标识号
三、解锁
-----------------------------------------------------------------------------
由sys用户通过查询与锁相关的视图来了解锁,了解阻塞会话与被阻塞会话的sid、serial#、
用户名及其所使用的DML操作语句。
-----------------------------------------------------------------------------
set pagesize 40 linesize 150
column blockers format a45
column waiters format a45
select '阻塞者('||sb.sid||':'||sb.serial#||'-'||sb.username||')-'||qb.sql_text blockers,
'等待者('||sw.sid||':'||sw.serial#||'-'||sw.username||')-'||qw.sql_text waiters
from v$lock lb,v$lock lw,v$session sb,v$session sw,v$sql qb,v$sql qw
where lb.sid=sb.sid
and lw.sid=sw.sid
and sb.prev_sql_addr=qb.address
and sw.sql_address=qw.address
and lb.id1=lw.id1
and sb.lockwait is null
and sw.lockwait is not null
and lb.block=1;
---------------------------------------------------------------------------
要了解哪些数据库用户的会话锁定了对象、锁定的模式是什么、对应的操作系统
用户是在哪台计算机上进行操作的、被锁定的对象及其类型等信息
---------------------------------------------------------------------------
set pagesize 40 linesize 150
column username format a9
column sid format 9999
column serial# format 99999999
column mode_locked format a12
column os_user_name format a16
column object_name format a12
column object_type format a12
select s.username,s.sid,s.serial#,
decode(lo.locked_mode,
0,'none',
1,'null',
2,'row-s(ss)',
3,'row-x(sx)',
4,'share',
5,'s/row-x(ssx)',
6,'exclusive',
to_char(lo.locked_mode)) mode_locked,
lo.os_user_name,
do.object_name,do.object_type
from v$session s,v$locked_object lo,dba_objects do
where
lo.object_id=do.object_id;
---------------------------------------------------------------------------
要了解阻塞者会话的sid、serial#信息
---------------------------------------------------------------------------
set pagesize 40 linesize 150
column username format a9
column sid format 9999
column serial# format 99999999
select s.username,s.sid,s.serial#
from v$session s,dba_blockers dbab
where s.sid=dbab.holding_session;
---------------------------------------
解锁
---------------------------------------
alter system kill session 'sid,serial#';
-----------------------------------------------------------
declare
cursor cur_lock IS
select sid,type,lmode,request,ctime,block from v$lock;
mysid v$lock.sid%type;
mytype v$lock.type%type;
mylmode v$lock.lmode%type;
myrequest v$lock.request%type;
myctime v$lock.ctime%type;
myblock v$lock.block%type;
icount integer;
begin
open cur_lock;
loop
fetch cur_lock into mysid,mytype,mylmode,myrequest,myctime,myblock;
exit when cur_lock%NOTFOUND;
select count(*) into icount from v$lock where sid=mysid;
if icount>0 then
insert into lock_test (sid,type,lmode,request,ctime,block) values
(mysid,mytype,mylmode,myrequest,myctime,myblock);
else
dbms_output.put_line('no data');
end if;
end loop;
close cur_lock;
EXCEPTION
WHEN OTHERS THEN
RAISE_APPLICATION_ERROR(-20000,'ERROR trg_pets_upper_pet_kind: '|| SQLERRM);
end;
/