查锁的方法:
有用的SQL:
1:检查系统中锁的简单脚本:
Select s.username, s.sid, l.type, l.id1, l.id2, l.lmode, l.request, p.spid PID
From v$lock l, v$session s, v$process p
Where s.sid = l.sid And p.addr = s.paddr And s.username is not null
Order By id1, s.sid,request;
获取用户SID,PID,锁的种类,锁的类型等信息
2:获取数据库锁的信息(用户ID,OBJECT,SQL)
REM *****************************************************************
REM TITLE : Generic Script which displays SQL Text, REM SID
and Object name of the locks currently REM being held in the database.
REM MODULE : lock_held.sql
Set pagesize 60
Set linesize 132
select s.username username, a.sid sid, a.owner||'.'||a.object object, s.lockwait, t.sql_text SQL
from v$sqltext t, v$session s, v$access a
where t.address = s.sql_address and t.hash_value = s.sql_hash_value
and s.sid = a.sid and a.owner != 'SYS'
and upper(substr(a.object,1,2)) != 'V$' ;
REM REM End of "Lock Monitoring Script" REM
3:产生等待锁的用户报告
SELECT sn.username,m.sid, m.type, DECODE(m.lmode, 0, 'None', 1, 'Null', 2, 'Row Share',
3, 'Row Excl.', 4, 'Share', 5, 'S/Row Excl.', 6, 'Exclusive', lmode,
ltrim(to_char(lmode,'990'))) lmode,
DECODE(m.request, 0, 'None', 1, 'Null', 2, 'Row Share', 3, 'Row Excl.', 4, 'Share',
5, 'S/Row Excl.', 6, 'Exclusive', request, ltrim(to_char(m.request,'990'))) request,
m.id1, m.id2
FROM v$session sn, v$lock m |
WHERE (sn.sid = m.sid AND m.request != 0)
OR ( sn.sid = m.sid AND m.request = 0 AND lmode != 4 AND (id1, id2) IN (SELECT s.id1, s.id2
FROM v$lock s
WHERE request != 0 AND s.id1 = m.id1 AND s.id2 = m.id2) )
ORDER BY id1, id2, m.request;
4:显示持有锁的信息:
select nvl(S.USERNAME,'Internal') username, nvl(S.TERMINAL,'None') terminal, L.SID||','||S.SERIAL# Kill, U1.NAME||'.'||substr(T1.NAME,1,20) tab,
decode(L.LMODE, 1,'No Lock', 2,'Row Share', 3,'Row Exclusive', 4,'Share',
5,'Share Row Exclusive', 6,'Exclusive',null) lmode,
decode(L.REQUEST,1,'No Lock', 2,'Row Share', 3,'Row Exclusive', 4,'Share',
5,'Share Row Exclusive', 6,'Exclusive',null) request
from V$LOCK L, V$SESSION S, SYS.USER$ U1, SYS.OBJ$ T1
where L.SID = S.SID and T1.OBJ# = decode(L.ID2,0,L.ID1,L.ID2) and U1.USER# = T1.OWNER# and S.TYPE != 'BACKGROUND'
order by 1,2,5
SQL脚本
1. 诊断系统中的锁
为了找出系统中那些用户锁住资源以及那些用户在等待相应的资源,可使用以下语句(其中的/*+ NO_MERGE(..) */千万不可省略, 否则会很慢):
-- looklock.sql
-- use the NO_MERGE hints can speed up the query
select /*+ NO_MERGE(a) NO_MERGE(b) NO_MERGE(c) */ 'Wait' "Status", a.username, a.machine, a.sid, a.serial#, a.last_call_et "Seconds", b.id1, c.sql_text "SQL"
from v$session a, v$lock b, v$sqltext c
where a.username is not null
and a.lockwait = b.kaddr
and c.hash_value =a.sql_hash_value
union
select /*+ NO_MERGE(a) NO_MERGE(b) NO_MERGE(c) */ 'Lock' "Status", a.username, a.machine, a.sid, a.serial#, a.last_call_et "Seconds", b.id1, c.sql_text "SQL"
from v$session a, v$lock b, v$sqltext c
where b.id
(select /*+ NO_MERGE(d) NO_MERGE(e) */ distinct e.id1
from v$session d, v$lock e
where d.lockwait = e.kaddr)
and a.username is not null
and a.sid = b.sid
and b.request=0
and c.hash_value =a.sql_hash_value;
执行后的结果如下所示:
Stat USERNAME MACHINE SID SERIAL# Seconds ID1
---- ------------------------------ ---------------- --------- --------- --------- ---------
SQL
----------------------------------------------------------------
Lock CIQUSR CIQDULMACER 12 966 245 131089
select * from c_trade_mode for update
Wait CIQUSR CIQDULMACER 10 735 111 131089
update c_trade_mode set x_name = 'zzz' where x_code='5'
Wait CIQUSR CIQDULMACER 15 106 1094 131089
select * from c_trade_mode for update
其中:
Status有两种状态,LOCK表明该进程锁住了某个资源,WAIT表示该进程正在等待某个资源。
Username, Machine分别为ORACLE用户名及机器名
SID,SERIAL#可用于随后的解锁操作
Seconds表示该进程最后一次进行操作至当前的时间(秒)
ID1, 锁标识。某个LOCK状态的ID1与某个WAIT状态的ID1相同,可说明锁的正是另一个进程等待的。
SQL: 锁住资源的SQL语句
2. 解除锁
诊断出锁的状态后,若发现该阻塞其它用户进程的进程是正常操作中,则可通知该用户对其进行提交,从而达到释放锁资源的目的;若为非正常操作,即,其状态为 "inactive",且其Seconds已为较多长时间,则可执行以下语句将该进程进行清除,系统会自动对其进行回滚,从而释放锁住的资源。
alter system kill session 'sid, serial#';
例如: 对于上例中显示的结果, 可用以下语句清除锁住资源的进程:
alter system kill session '12, 966';
关于你所说:在网络断掉(通过拔掉网线)或非正常终止进程(通过task manager强行关闭sql*plus)时,oracle在有限的时间内(我只观查了5-10分)内,oracle未能对该进程作任何处理。
这个处理与TCP协议有关,因为SQL NET在使用TCP/IP协议进行网络连接时是一种短连接,当ORACLE连接异常终止时,因为是异常终止,终止信号并没有通过网络通知server端,因此只有下次server有结果从服务器端返回需与client通信时,server才会发现此client已经端掉。因此出现你前面所提ORACLE处理异常终止进程延时情况.
查锁语句:查询产生锁的用户锁sql
select a.username username, a.sid sid, a.serial# serial,b.id1 id1, c.sql_text sqltext
from v$session a, v$lock b, v$sqltext c
where b.id
(select distinct e.id1
from v$session d, v$lock e
where d.lockwait = e.kaddr)
and a.sid = b.sid
and c.hash_value = a.sql_hash_value
and b.request = 0;
一个加速查询锁表和等待锁的方法
--- 这些锁定中有"只读锁"、"排它锁","共享排它锁"等多种类型,而且每种类型又有"行级锁"(一次锁住一条记录),"页级锁"(一次锁住一页,即数据库中存储记录的最小可分配单元),"表级锁"(锁住整个表)
--- 若为"行级排它锁",则除被锁住的该行外,该表中其它行均可被其它的用户进行修改(update)或删除(delete)操作,若为"表级排它锁",则所有其它用户只能对该表进行查询(select)操作,而无法对其中的任何记录进行修改或删除。当程序对所做的修改进行提交(commit)或回滚后 (rollback)后,锁住的资源便会得到释放,从而允许其它用户进行操作。
在oracle 8.0.x 中执行"获取正在等待锁资源的用户名"的查询语句十分缓慢
select a.username, a.sid, a.serial#, b.id1
from v$session a, v$lock b
where a.lockwait = b.kaddr
查找阻塞其它用户的用户进程也十分缓慢
select a.username, a.sid, a.serial#, b.id1
from v$session a, v$lock b
where b.id
(select distinct e.id1
from v$session d, v$lock e
where d.lockwait = e.kaddr)
and a.sid = b.sid
and b.request = 0
一个解决办法。即通过将问题发生时的 v$lock,v$session视图中的相关记录保存于自己建立的表中,再对该表进行查询,则速度大大提高,可以迅速发现问题。经实际使用,效果非常好。在接到用户反映后,几秒钟即可查出由于锁住资源而影响其它用户的进程,并进行相应的处理。
首先,以 dba 身份(不一定为system)登录入数据库中,创建三个基本表:my_session,my_lock, my_sqltext,并在将会进行查询的列上建立相应的索引。语句如下:
rem 从 v$session 视图中取出关心的字段,创建 my_session 表,并在查询要用到的字段上创建索引,以加快查询速度
drop table my_session;
create table my_session
as
select a.username, a.sid, a.serial#,
a.lockwait, a.machine,a.status,
a.last_call_et,a.sql_hash_value,a.program
from v$session a
where 1=2 ;
create unique index my_session_u1 on my_session(sid);
create index my_session_n2 on my_session(lockwait);
create index my_session_n3 on my_session(sql_hash_value);
---- rem 从 v$lock 视图中取出字段,创建 my_lock 表,并在查询要用到的字段上创建索引,以加快查询速度
drop table my_lock;
create table my_lock
as
select id1, kaddr, sid, request,type
from v$lock
where 1=2;
create index my_lock_n1 on my_lock(sid);
create index my_lock_n2 on my_lock(kaddr);
---- rem 从 v$sqltext 视图中取出字段,创建 my_sqltext 表,并在查询要用到的字段上创建索引,以加快查询速度
drop table my_sqltext;
create table my_sqltext
as
select hash_value , sql_text
from v$sqltext
where 1=2;
create index my_sqltext_n1 on my_sqltext ( hash_value);
-- 然后,创建一个 sql 脚本文件,以便需要时可从 sql*plus 中直接调用。其中,首先用 truncate table 表名命令将表中的记录删除。之所以用 truncate 命令,而不是用delete 命令,是因为delete 命令执行时,将会产生重演记录,速度较慢,而且索引所占的空间并未真正释放,若反复做 insert及delete,则索引所占的空间会不断增长,查询速度也会变慢。而 truncate命令不产生重演记录,速度执行较delete快,而且索引空间被相应地释放出来。删除记录后,再将三个视图中的相关记录插入自己创建的三个表中。最后,对其进行查询,由于有索引,同时由于在插入时条件过滤后,记录数相对来说较少,因而查询速度很快,马上可以看到其结果。
---- 此时,若发现该阻塞其它用户进程的进程是正常操作中,则可通知该用户对其进行提交,从而达到释放锁资源的目的;若为未正常操作,即,其状态为 "inactive",且其last_call_et已为较多长时间,则可执行以下语句将该进程进行清除,系统会自动对其进行回滚,从而释放锁住的资源。
alter system kill session ''sid, serial#''
-- sql 脚本如下:
set echo off
set feedback off
prompt ''删除旧记录.....''
truncate table my_session;
truncate table my_lock;
truncate table my_sqltext;
prompt ''获取数据.....'
insert into my_session
select a.username, a.sid, a.serial#,
a.lockwait, a.machine,a.status,
a.last_call_et,a.sql_hash_value,a.program
from v$session a
where nvl(a.username,''null'')< >''null”;
insert into my_lock
select id1, kaddr, sid, request,type
from v$lock;
insert into my_sqltext
select hash_value , sql_text
from v$sqltext s, my_session m
where s.hash_value=m.sql_hash_value;
column username format a10
column machine format a15
column last_call_et format 99999 heading "seconds"
column sid format 9999
prompt "正在等待别人的用户"
select a.sid, a.serial#,
a.machine,a.last_call_et, a.username, b.id1
from my_session a, my_lock b
where a.lockwait = b.kaddr;
prompt "被等待的用户"
select a.sid, a.serial# ,a.machine ,a.last_call_et ,a.username
,b.type ,a.status,b.id1
from my_session a, my_lock b
where b.id
(select distinct e.id1
from my_session d, my_lock e
where d.lockwait = e.kaddr)
and a.sid = b.sid
and b.request=0;
prompt "查出其 sql "
select a.username, a.sid, a.serial#,
b.id1, b.type, c.sql_text
from my_session a, my_lock b, my_sqltext c
where b.id
(select distinct e.id1
from my_session d, my_lock e
where d.lockwait = e.kaddr)
and a.sid = b.sid
and b.request=0
and c.hash_value =a.sql_hash_value;