根据保护的数据不同,ORACLE的数据库锁分为以下几大类:
1.DML锁(data locks数据锁),用于保护数据的完整性;
2.DDL锁(dictionary locks字典锁),用于保护数据库对象的结构,例如表、索引的结构定义;
3.内部锁或闩(internal locks or latches),用于保护内部结构
在 Oracle 数据库中,DML锁主要包括 TM 锁和 TX 锁,其中 TM 锁称为表级锁,TX 锁称为事务锁或行级锁。
当 Oracle 执行 DML 语句时,系统自动在所要操作的表上申请 TM 类型的锁。当 TM 锁获得后,系统再自动申请 TX 类型的锁,并将实际锁定的数据行的锁标志位进行置位。这样在事务加锁前检查 TX 锁相容性时就不用再逐行检查锁标志,而只需检查 TM 锁模式的相容性即可,大大提高了系统的效率。TM 锁包括了 SS、SX、S、X 等多种模式,在数据库中用 0-6 来表示。不同的 SQL 操作产生不同类型的 TM 锁。
在数据行上只有 X 锁(排他锁)。在 Oracle 数据库中,当一个事务首次发起一个 DML 语句时就获得一个 TX 锁,该锁保持到事务被提交或回滚。当两个或多个会话在表的同一条记录上执行 DML 语句时,第一个会话在该条记录上加锁,其他的会话处于等待状态。当第一个会话提交后,TX 锁被释放,其他会话才可以加锁。
当 Oracle 数据库发生 TX 锁等待时,如果不及时处理常常会引起 Oracle 数据库挂起,或导致死锁的发生,产生 ORA-60 的错误。这些现象都会对实际应用产生极大的危害,如长时间未响应,大量事务失败等。
ORACLE里锁有以下几种模式:
0:none
1:null 空
2:Row-S 行共享(RS):共享表锁,sub share
3:Row-X 行独占(RX):用于行的修改,sub exclusive
4:Share 共享锁(S):阻止其他DML操作,share
5:S/Row-X 共享行独占(SRX):阻止其他事务操作,share/sub exclusive
6:exclusive 独占(X):独立访问使用,exclusive
上面提到 TX 锁也称事务锁或者行级锁,是控制数据库并发访问的一项重要技术,也是数据完整性和一致性的重要保证。下面就一起看看如何快速查杀锁。
方案 1
--行锁查看
col LOCK_ID1 for a10
col "Locked Mode" for a15
col MACHINE for a20
set line 345 pages 345
col "Kill" for a40
select s.SID,s.SERIAL#,s.MACHINE,s.TYPE,l.TYPE,l.CTIME,l.BLOCK,l.REQUEST,l.LMODE,
decode(l.lmode,
0,
'None',
1,
'Null',
2,
'Row-S (SS)',
3,
'Row-X (SX)',
4,
'Share',
5,
'S/Row-X (SSX)',
6,
'Exclusive',
substr(to_char(l.lmode), 1, 13)) as "Locked Mode",
DECODE(L.TYPE,
'MR',
'File_ID:' || L.ID1,
'TM',
t.NAME,
'TX',
'USN:' || to_char(TRUNC(L.ID1 / 65536)) || 'RWO:' ||
nvl(r.NAME, 'None'),
L.ID1) as LOCK_ID1,
'alter system kill session ''' || s.SID || ',' || s.SERIAL# || '''immediate;' as "Kill"
from gv$process p
inner join gv$session s
on s.PADDR = p.ADDR
inner join v$lock l
on l.SID = s.SID
left join sys.obj$ t
on l.ID1 = t.obj#
left join sys.obj$ r
on s.ROW_WAIT_OBJ# = r.obj#
where 1 = 1
and l.TYPE != 'MR'
and l.TYPE = 'TM'
and l.lmode = 3
order by s.SID;
通过上方 SQL 可定位锁以及查杀语句,但无法确定锁源头,可通过如下 SQL 查到 SID 定位锁源头信息,如查出的锁源头(SID 位于最左边)在上边结果集中出现,则可使用上面 SQL 直接查杀锁,方便快速。
set linesize 132
col sid format a12
col event format a30
col sql_text format a40
col object_name format a25
WITH sessions AS
(SELECT /*+materialize*/
sid
,sql_id
,event
,blocking_session
,row_wait_obj#
FROM gv$session)
SELECT LPAD(' ', LEVEL ) || sid sid
,sql_id
,event
,owner||decode(owner,null,null,'.')||object_name object_name
,substr(sql_text,1,40) sql_text
FROM sessions s
LEFT OUTER JOIN dba_objects ON (object_id = row_wait_obj#)
LEFT OUTER JOIN v$sql USING (sql_id)
WHERE sid IN (SELECT blocking_session FROM sessions)
OR blocking_session IS NOT NULL
CONNECT BY PRIOR sid = blocking_session
START WITH blocking_session IS NULL;
方案 2
使用 v$session 中的 final_blocking_instance 和 final_blocking_session 定位锁源头,具体 SQL 如下:
SQL> select 'alter system kill session ''' || ss.sid || '' || ',' || ss.serial# || ',@' ||
ss.inst_id || ''' immediate;' db_kill_session
from gv$session s,
gv$session ss
where s.final_blocking_session is not null
and s.final_blocking_instance = ss.inst_id
and s.final_blocking_session = ss.sid
and s.sid <> ss.sid;
DB_KILL_SESSION
--------------------------------------------------
alter system kill session '161,5579,@1' immediate;
alter system kill session '161,5579,@1' immediate;
--如下,可通过操作系统层面直接 kill 查杀锁。
SQL> select p.inst_id, 'kill -9 ' || p.spid os_kill_session
from gv$session s,
gv$session ss,
gv$process p
where s.final_blocking_session is not null
and s.final_blocking_instance = ss.inst_id
and s.final_blocking_session = ss.sid
and ss.paddr = p.addr
and ss.inst_id = p.inst_id
and s.sid <> ss.sid;
INST_ID OS_KILL_SESSION
---------- --------------------------------
1 kill -9 12349
最后在分享一些通用的杀会话的 SQL 脚本:
1、kill某个等待事件对应的spid:
set linesize 260 pagesize 10000
select 'kill -9 ' || a.spid
from v$process a, v$session b
where a.addr = b.paddr
and a.background is null
and b.type = 'USER'
and b.event like '%' || '&eventname' || '%'
and b.status = 'ACTIVE';
-- 对应的alter system kill session的语法:
set linesize 260 pagesize 1000
col machine for a50
col kill_session for a60;
select machine,
'alter system kill session ' || ''''||sid|| ',' || serial# ||''''|| 'immediate;' kill_session,
status
from v$session
where type='USER' and event like '%event_name%' and status = 'ACTIVE';
2、kill某个sql_id对应的spid:
set linesize 260 pagesize 10000
select 'kill -9 ' || a.spid
from v$process a, v$session b
where a.addr = b.paddr
and a.background is null
and b.type = 'USER'
and b.sql_id = '&sql_id'
and b.status = 'ACTIVE';
-- 对应的alter system kill session的语法:
set linesize 260 pagesize 10000
col machine for a60
select machine,
'alter system kill session ' || ''''||sid|| ',' || serial# ||''''|| 'immediate;',
status
from v$session
where sql_id = '&sql_id' and type='USER' and status='ACTIVE';
3、被kill会话的类型:
set linesize 260 pagesize 10000
select b.osuser,b.machine,b.program,b.sql_id,b.PREV_SQL_ID,a.spid,to_char(LAST_CALL_ET) as seconds,b.BLOCKING_SESSION,b.BLOCKING_INSTANCE
from v$process a, v$session b
where a.addr = b.paddr
and a.inst_id=b.inst_id
and a.background is null
and b.type = 'USER'
and b.event='&event_name'
and b.status = 'ACTIVE';
4、blocking会话类型和kill blocking会话:
set linesize 260 pagesize 10000
col machine for a50
col kill_session for a60
SELECT
blocking_instance,
blocking_session,
BLOCKING_SESSION_STATUS,
FINAL_BLOCKING_INSTANCE,
FINAL_BLOCKING_SESSION,
COUNT(*)
FROM
v$session
WHERE
upper(event) LIKE '%&cursor%'
GROUP BY
blocking_instance,
blocking_session,
BLOCKING_SESSION_STATUS,
FINAL_BLOCKING_INSTANCE,
FINAL_BLOCKING_SESSION
order by blocking_instance,count(*);
-- kill blocking会话
select
inst_id,
machine,
'alter system kill session ' || ''''||sid|| ',' || serial# ||''''|| 'immediate;' kill_session,
status
from gv$session a
where a.type='USER' and (a.inst_id,a.sid) in
(
select
BLOCKING_INSTANCE,
BLOCKING_SESSION
from v$session
where upper(event) like '%&cursor%'
)
order by inst_id;
5、所有含有关键字“LOCAL=NO”的进程是Oracle数据库中远程连接进程的共同特点,因此通过以下命令可以kill掉所有的进程
ps -ef|grep -v grep|grep LOCAL=NO|awk '{print $2}'|xargs kill -9