--原理
--Oracle数据库的锁类型
--根据保护的对象不同,Oracle数据库锁可以分为以下几大类:DML锁(data locks,数据锁),用于保护数据的完整性;DDL锁(dictionary locks,字典锁),用于保护数据库对象的结构,如表、索引等的结构定义;内部锁和闩(internal locks and latches),保护数据库的内部结构。
--DML锁的目的在于保证并发情况下的数据完整性。在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的错误。这些现象都会对实际应用产生极大的危害,如长时间未响应,大量事务失败等
--悲观封锁
--锁在用户修改之前就发挥作用:
Select ..for update(nowait)
Select * from tab1 for update
--用户发出这条命令之后,oracle将会对返回集中的数据建立行级封锁,以防止其他用户的修改。
--如果此时其他用户对上面返回结果集的数据进行dml或ddl操作都会返回一个错误信息或发生阻塞。
--1:对返回结果集进行update或delete操作会发生阻塞。
--2:对该表进行ddl操作将会报:Ora-00054:resource busy and acquire with nowait specified.
--原因分析
--此时Oracle已经对返回的结果集上加了排它的行级锁,所有其他对这些数据进行的修改或删除操作都必须等待这个锁的释放,产生的外在现象就是其他的操作将发生阻塞,等待这个操作commit或rollback.
--同样这个查询的事务将会对该表加表级锁,不允许对该表的任何ddl操作,否则将会报出ora-00054错误::resource busy and acquire with nowait specified.
--乐观封锁
--乐观的认为数据在select出来到update进去并提交的这段时间数据不会被更改。这里面有一种潜在的危险就是由于被选出的结果集并没有被锁定,是存在一种可能被其他用户更改的可能。因此Oracle仍然建议是用悲观封锁,因为这样会更安全。
--阻塞
--定义:当一个会话保持另一个会话正在请求的资源上的锁定时,就会发生阻塞。被阻塞的会话将一直挂起,直到持有锁的会话放弃锁定的资源为止。
--是否有锁
--有请求等待(等待者)
select * from gv$lock where request>0;
--有阻塞别人的进程(源头)
select * from gv$lock where block=1;
--通过查找dba_waiters和dba_blockers
--dba_blockers:记录锁住其它session的session sid
--dba_waiters:记录被锁的session信息以及拥有锁的session信息
--阻塞了别的session_id
select * from dba_blockers;
--阻塞及被阻塞的session,HOLDING_SESSION为锁session ,WAITING_SESSION为等待的session
select holding_session,waiting_session,lock_type,mode_held,mode_requested from dba_waiters;
--holder tree
SELECT DECODE(request,0,'Holder: ','Waiter: ')|| sid sess, id1, id2, lmode, request, type
FROM gv$LOCK
WHERE (id1, id2, type) IN (SELECT id1, id2, type FROM GV$LOCK WHERE request>0)
ORDER BY id1, request;
--锁信息监控
--持锁(BLOCKED)
--被锁(WAITING)
select /*+ rule */
lpad('--', decode(b.BLOCK, 1, 0, 4)) || s.username username,
b.TYPE,
o.owner || '.' || o.object_name object_name,
s.sid,
s.serial#,
decode(b.REQUEST, 0, 'BLOCKED', 'WAITING') status
from dba_objects o, v$session s, v$lock v, v$lock b
where v.ID1 = o.object_id
and v.SID = s.SID
and v.SID = b.SID
and (b.BLOCK = 1 or b.REQUEST > 0)
and v.TYPE = 'TM'
order by b.ID2, v.ID1, username desc;
--持有锁的对象
--查询造成死锁的所有进程
SELECT a.session_id,
username,
TYPE,
mode_held,
mode_requested,
lock_id1,
lock_id2
FROM v$session b, dba_blockers c, dba_locks a
WHERE c.holding_session = a.session_id
AND c.holding_session = b.sid;
--查看持有锁的sql_text
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));
--谁锁了某张表(session为被锁表的会话)
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 owner = '&schema'
and object_name = '&name'));
--解决方法:
--查找HOLDING_SESSION(SID)
select * from dba_blockers;
--查询session的SID及SERIAL#信息
select sid,serial# from v$session where sid=SID;
--kill--session(需要等待pmon)
alter system kill session 'SID,SERIAL#';
--可以使用如下sql来快速回滚事物、释放会话的相关锁、立即返回当前会话的控制权
ALTER SYSTEM KILL SESSION 'sid,serial#' IMMEDIATE;
--kill--session(操作系统级别os)
! kill -9 12959