实际开发和测试中,经常遇到数据对象有意无意锁住的情况。怎么样最快速准确的定位到锁住对象表的用户和机器呢?
下面是笔者写的一段脚本,可以发现指定对象(特定Schema和特定对象)上所有加锁对象和用户。在Oracle中,只有独占锁是唯一的,共享锁可能存在多个用户同时锁住。笔者曾经遇到过使用pl/sql developer添加数据,程序反复崩溃,最后发现这个对象表上加三次锁的情况。
下面是针对这种情况,笔者试着写的一段脚本。执行环境可以是sql*plus或者pl/sql developer command window均可。只要输入锁住对象所在的schema和名称,就可以定位到锁定用户信息。
select p.SPID, p.SERIAL# as proserial,p.PID as pid, s.SID as sessionid,
s.SERIAL# as sessseri, s.USERNAME,s.OSUSER,s.MACHINE,
s.PROGRAM,s.ACTION,s.STATUS
from v$session s, v$process p
where s.PADDR=p.ADDR and s.SID in (
select session_id from v$locked_object
where object_id in (select object_id from dba_objects
where wner='&schema' and object_name='&name'));
其中的结果列中:
|
列名 |
含义 |
备注 |
|
SPID |
操作系统级别的进程编号 |
在Linux/Unix环境下,使用ps –ef可以查看到进程编号;而在windows环境下,是指定Oracle.exe进程中的线程编号!要注意区别! |
|
PROSERIAL |
操作系统级别的进程系列号 |
进程有一个系列号,在查找和kill的时候要使用; |
|
PID |
在Oracle内部进程编号 |
Oracle内部的编号,可以查找v$process视图进行定位; |
|
SESSIONID |
锁表会话ID |
会话id; |
|
SESSSERI |
锁表会话系列号 |
会话系列号,使用alter system kill session; |
|
USERNAME |
会话登录用户名 |
登录用户的数据库登录名; |
|
OSUSER |
登录操作系统用户名 |
登录用户在操作系统上的名称; |
|
MACHINE |
登录客户端机器 |
客户端主机名; |
|
PROGRAM |
登录程序名称 |
登录程序名,如plsqldev.exe或者sqlplus.exe; |
|
ACTION |
进行行为描述 |
进行行为的简单描述; |
|
STATUS |
当前会话状态 |
当前会话状态,可以为Active、InActive等; |
使用该脚本比较简单,只要把里面的SQL粘贴在sqlplus或者其他开发工具上就可以了。下面以sqlplusw为例,进行演示。
SQL*Plus: Release 10.2.0.1.0 - Production on 星期三 2月 16 14:08:51 2011
Copyright (c) 1982, 2005, Oracle. All rights reserved.
SQL> conn sys/acca@otstest as sysdba;
已连接。
SQL> select * from scott.emp for update nowait; //尝试获取独占!
select * from scott.emp for update nowait
*
第 1 行出现错误:
ORA-00054: 资源正忙, 但指定以 NOWAIT 方式获取资源 //报错,emp对象被锁住了!!
调用脚本。
SQL> select p.SPID, p.SERIAL# as proserial,p.PID as pid, s.SID as sessionid,
2 s.SERIAL# as sessseri, s.USERNAME,s.OSUSER,s.MACHINE,
3 s.PROGRAM,s.ACTION,s.STATUS
4 from v$session s, v$process p
5 where s.PADDR=p.ADDR and s.SID in (
6 select session_id from v$locked_object
7 where object_id in (select object_id from dba_objects
8 where wner='&schema' and object_name='&name'));
输入 schema 的值: SCOTT
输入 name 的值: EMP
原值 8: where wner='&schema' and object_name='&name'))
新值 8: where wner='SCOTT' and object_name='EMP'))
SPID PROSERIAL PID SESSIONID SESSSERI USERNAME OSUSER MACHINE PROGRAM ACTION STATUS
------------ ---------- ---------- ---------- ---------- ------------------------------ ------------------------------ ---------------------------------------------------------------- ---------------------------------------------------------------- -------------------------------- --------
3584 205 15 159 9529 SCOTT ACCA-119HS63YXC\Guest WORKGROUP\XXX-PC plsqldev.exe Command Window - New INACTIVE
这样,就可以找到用户信息了。
下面解决就容易了,可以联系相关人员解开对象锁,或者联系管理员kill掉会话和进程。这部分讨论可以参见笔者之前的blog:
kill会话过程分析(http://space.itpub.net/17203031/viewspace-683786 )。
最后,有兴趣可以来分析一下脚本使用的原数据视图。
ü v$process:记录了Oracle进程的信息,包括Server Process和后台进程,其中的SPID,Serial,以及ADDR是描述进程在操作系统层面的信息;
ü v$session:记录会话信息,表示一个登录对应的会话内容;
ü v$lock_objects:当前被锁住对象的信息;
ü dba_objects:所有数据库对象的列表,提供唯一object_id的来源;
ü
有一点要注意,并不只是数据表会被锁住,存储过程代码、视图等对象都有被锁住和引起连带锁住的风险。在本脚本中数据这些对象的名称,也是有一定作用的。
原则上说,当我们强制断开数据库Oracle客户端之后,实例会自动检测到连接的断开,并且回收资源、撤销事务和解锁对象。这种操作一般是要有一定时间(几秒钟或者十几分钟),如果长时间没有释放资源,也就需要DBA的干预进行手工对象解锁。
手工解锁对象有两个层面,会话层面和OS层面。一般我们采用从低到高的选择方案。当会话层面的kill不能完全释放资源的时候,才会考虑使用OS层面。一些特殊情况,如JOB进程,是需要直接OS层面杀死进程的。
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/17203031/viewspace-687333/ ,如需转载,请注明出处,否则将追究法律责任。