oracle报错ORA-01013: user requested cancel of current operation. 解除锁定的表 、查看锁表语句

场景

oracle报错:ORA-01013: user requested cancel of current operation
分析: 一般是因为update的时候被锁定了。

解决方案

使用有解锁权限的用户,解锁即可。

查看被锁住的session

SELECT
  s.username,  -- 用户(表空间)
  s.sid,  -- session_id
  s.serial#, --   serial# 是序列号
  s.logon_time,  -- 登录时间
  l.locked_mode
FROM
    v$session s,  -- v$Session 是session视图
    v$locked_object l
WHERE
    s.sid = l.session_id
ORDER BY
    s.logon_time;

查看被锁住的session(带上dba_objects)

dba_objects.object_name可以显示对象名等,如表名。这样在kill的时候更有把握些。

SELECT
  s.sid,  -- session_id
  s.serial#, --   serial# 是序列号
  s.username,  -- 用户(表空间)
  s.logon_time,  -- 登录时间
  o.owner,
  o.object_name
FROM
    v$session s,  -- v$Session 是session视图
    v$locked_object l,
    dba_objects o
WHERE
    s.sid = l.session_id
    and l.object_id = o.object_id
ORDER BY
    s.logon_time;

解锁

alter system kill session '5633,12566';  -- sid和serial# 共同确定一条数据

批量解锁语句

实测可用, 如果session不是很多,建议一个一个kill。

select 
a.object_name,b.session_id,c.serial#,
'alter system kill session '''||b.session_id||','||c.serial#||'''; ' as a,
c.program,c.username,c.command,c.machine,c.lockwait
from all_objects a,v$locked_object b,v$session c 
where a.object_id=b.object_id and c.sid=b.session_id;

查看锁表的sql

通过kill的方法,可以临时解决锁表问题,但不是长久办法,最好是找到锁表的语句,从根本上解决问题。
分为2部分,一部分阻塞blocking。一部分是被阻塞(blocked)。要看2个,union all即可。
要用到3个视图: lock,session,sql。

查看阻塞(blocking)他人语句

有2点需要注意:
1、s.prev_sql_id = sq.sql_id 用prev_sql_id关联 sq.sql_id
2、l.block =‘1’ block值为1,表示阻塞别人

select
	decode(l.block, 1, 'blocking') blocking,  -- block(是否阻塞他人) 0不阻塞  1阻塞
	decode(l.request, 0, 'null', 'blocked') blocked,   -- request(是否被阻塞) 0没被阻塞 1-6被阻塞
	l.request,
	s.sid, s.username, s.logon_time, s.machine,
	sq.sql_id, sq.sql_text, sq.hash_value, sq.module
from v$lock l, v$session s, v$sql sq
where 1 = 1
and l.sid = s.sid
and s.prev_sql_id = sq.sql_id
and l.block ='1';

查看被阻塞(blocked)语句

select
	decode(l.block, 1, 'blocking') blocking,  -- block(是否阻塞他人) 0不阻塞  1阻塞
	decode(l.request, 0, 'null', 'blocked') blocked,   -- request(是否被阻塞) 0没被阻塞 1-6被阻塞
	l.request,
	s.sid, s.machine, s.username, s.logon_time,
	sq.sql_id, sq.sql_text, sq.hash_value, sq.module
from v$lock l, v$session s, v$sql sq
where 1 = 1
and l.type ='TX'
and l.sid = s.sid
and s.sql_id = sq.sql_id;

blocking和blocked的关联和小区别

lock和session始终用sid关联即可(以下2条件一般不用变):

l.type ='TX'  
l.sid = s.sid  

blocking的条件

blocking有2个注意点:
1、block=‘1’ 1表示阻塞了别人。
2、要用prev_sql_id 去关联,因为它最近执行的语句阻塞了别人。

block='1' 
s.pre_sql_id= sq.sql_id 

blocked的条件

blocked注意点:
1、这里要看request字段,不是0的话,表示该sql请求 被阻塞了。
不要看block字段,因为block是是否阻塞别人。 在这里用不到。
2、用sql_id关联即可。因为就是当前sql被阻塞了。

-- 注: 这里不用加 block = '1' 字段
s.sql_id = sq.sql_id 

session和sql还可以用address关联

值得一提的是。 不仅可以根据sql_id关联,通过address也是可以关联的,效果一样。
blocking中这么写:

block='1' 
s.pre_sql_address= sq.sql_address

blocked中这么写:

s.sql_address= sq.address

不显示真正的阻塞语句,却显示 begin :id := sys.dbms_transaction.local_transaction_id; end;

使用pl/sql等工具模拟阻塞的时候会出现这个。显示的pl/sql中session创建的事务信息。
如果在sqlplus中直接执行命令。 那么是可以显示原语句的。

有兴趣可以自己试一下:
cmd打开2个窗口,链接oracle并分别执行语句:
第一个cmd窗口:

sqlplus / as sysdba;
conn root/666666@47.104.176.200/helowin; -- 如果是本地,@后面的可以省略
savepoint a; -- 设置保存点a,相当于开启事务
select * from t_user for update; -- 锁住全表

第二个cmd窗口:

-- 链接命令同上,略
savepoint a;
select * from t_user where id = '1' for update; -- 这条被阻塞

然后执行 查看阻塞他人的语句 会显示 select * from t_user for update; 说明是原语句。

如果要显示pl/sql中的原语句,要看trace了,略。

获取被阻塞语句还可以这么写(比较简单)

select sql_text from v$sql where hash_value in   
(
	select sql_hash_value from v$session where sid in  
	(
		select session_id from v$locked_object
	)
);  

其他

查看阻塞的语句-白师兄

select b.sid,
	a.sql_id,
	a.sql_text,
	a.hash_value,
	b.username,  
	b.machine,  
	a.module,
	decode(c.block, 1, 'blocking') blocking,
	decode(c.request, 0, 'null', 'blocked') blocked,
	to_char(b.logon_time, 'yyyy-mm-dd hh24:mi:ss')
from v$sql a, v$session b, v$lock c
where c.type = 'TX'
and a.sql_id = b.sql_id
and b.sid = c.sid
union all
select b.sid,
	a.sql_id,
	a.sql_text,
	a.hash_value,
	b.username,
	b.machine,
	a.module,
	decode(c.block, 1, 'blocking') blocking,
	decode(c.request, 0, 'null', 'blocked') blocked,
	to_char(b.logon_time, 'yyyy-mm-dd hh24:mi:ss')
from v$sql a, v$session b, v$lock c
where c.type = 'TX'
and a.sql_id = b.prev_sql_id
and b.sid = c.sid
and c.block = 1;

查看阻塞的语句-赵师兄

lb,sb,qb b表示blocker。 l,s,q 是lock,session,sql 。
lw,sw,qw w表示waiter。l,s,q 是lock,session,sql 。
|| 是字符串连接符。

select 
	'blocker('||lb.sid||':'||sb.username||')-sql:'|| qb.sql_text blockers,
	'waiter ('||lw.sid||':'||sw.username||')-sql:'|| 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 sw.lockwait is not null
and sb.lockwait is null
and lb.block=1 ;

v$locked_object 和 dba_objects 关联

都是通过object_id来关联的。

SELECT
	o.owner,
	o.object_name,
	l.session_id,
	l.locked_mode 
FROM
	v$locked_object l,
	dba_objects o 
WHERE
	o.object_id = l.object_id;

查看被锁住的session(加强版)

/*+ rule */ 这种写法有助于效率提升。

select 
/*+ rule */ lpad(' ', decode(l.xidusn, 0, 3, 0)) || l.oracle_username user_name,
o.owner, o.object_name, o.object_type, s.sid, s.serial#, p.spid
from v$locked_object l, dba_objects o, v$session s, v$process p
where l.object_id = o.object_id and l.session_id = s.sid and s.paddr = p.addr
order by o.object_id, xidusn desc;

这个语句也可以查看被阻塞的语句(还没研究)

select 
	s.status,s.sid,s.serial#,p.spid,  
    s.last_call_et as exec_seconds,t.sql_text as curr_sql  
from gv$session s,v$process p,v$instance i,v$lock k, v$sqltext t  
where s.paddr = p.addr  
and s.type != 'BACKGROUND'  
and s.lockwait = k.kaddr  
and s.sql_hash_value = t.hash_value  
and s.username is not null;  

你可能感兴趣的:(oracle)