ORACLE锁机制研究

纯抄的,连接可能失效,保存自己看。

锁概念基础
 
数据库是一个多用户使用的共享资源。当多个用户并发地存取数据时,在数据库中就会产生多个事务同时存取同一数据的情况。若对并发操作不加控制就可能会读取和存储不正确的数据,破坏数据库的一致性。
加锁是实现数据库并发控制的一个非常重要的技术。当事务在对某个数据对象进行操作前,先向系统发出请求,对其加锁。加锁后事务就对该数据对象有了一定的控制,在该事务释放锁之前,其他的事务不能对此数据对象进行更新操作。
在数据库中有两种基本的锁类型:排它锁(Exclusive Locks,即X锁)和共享锁(Share Locks,即S锁)。当数据对象被加上排它锁时,其他的事务不能对它读取和修改。加了共享锁的数据对象可以被其他事务读取,但不能修改。数据库利用这两种基本的锁类型来对数据库的事务进行并发控制。

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仍然建议是用悲观封锁,因为这样会更安全。

阻塞
定义:
当一个会话保持另一个会话正在请求的资源上的锁定时,就会发生阻塞。被阻塞的会话将一直挂起,直到持有锁的会话放弃锁定的资源为止。4个常见的dml语句会产生阻塞
INSERT
UPDATE
DELETE
SELECT…FOR UPDATE

INSERT
Insert发生阻塞的唯一情况就是用户拥有一个建有主键约束的表。当2个的会话同时试图向表中插入相同的数据时,其中的一个会话将被阻塞,直到另外一个会话提交或会滚。一个会话提交时,另一个会话将收到主键重复的错误。回滚时,被阻塞的会话将继续执行。
UPDATE 和DELETE当执行Update和delete操作的数据行已经被另外的会话锁定时,将会发生阻塞,直到另一个会话提交或会滚。
Select …for update
当一个用户发出select..for update的错作准备对返回的结果集进行修改时,如果结果集已经被另一个会话锁定,就是发生阻塞。需要等另一个会话结束之后才可继续执行。可以通过发出 select… for update nowait的语句来避免发生阻塞,如果资源已经被另一个会话锁定,则会返回以下错误:Ora-00054:resource busy and acquire with nowait specified.

死锁-deadlock
定义:当两个用户希望持有对方的资源时就会发生死锁.
即两个用户互相等待对方释放资源时,oracle认定为产生了死锁,在这种情况下,将以牺牲一个用户作为代价,另一个用户继续执行,牺牲的用户的事务将回滚.
例子:
1:用户1对A表进行Update,没有提交。
2:用户2对B表进行Update,没有提交。
此时双反不存在资源共享的问题。
3:如果用户2此时对A表作update,则会发生阻塞,需要等到用户一的事物结束。
4:如果此时用户1又对B表作update,则产生死锁。此时Oracle会选择其中一个用户进行会滚,使另一个用户继续执行操作。
起因:
Oracle的死锁问题实际上很少见,如果发生,基本上都是不正确的程序设计造成的,经过调整后,基本上都会避免死锁的发生。
DML锁分类表


表1 Oracle的TM锁类型
锁模式 锁描述 解释 SQL操作
0 none    
1 NULL Select
2 SS(Row-S) 行级共享锁,其他对象只能查询这些数据行
Select for update、Lock for update、Lock row share
3 SX(Row-X) 行级排它锁,在提交前不允许做DML操作
Insert、Update、Delete、Lock row share
4 S(Share) 共享锁 Create index、Lock share
5 SSX(S/Row-X) 共享行级排它锁 Lock share row exclusive
6 X(Exclusive) 排它锁
Alter table、Drop able、Drop index、Truncate table 、Lock exclusive
 
1. 关于V$lock 表和相关视图的说明
 
Column
Datatype
Description
ADDR
RAW(4 | 8)
Address of lock state object
KADDR
RAW(4 | 8)
Address of lock
SID
NUMBER
Identifier for session holding or acquiring the lock
TYPE
VARCHAR2(2)
Type of user or system lock
The locks on the user types are obtained by user applications. Any process that is blocking others is likely to be holding one of these locks. The user type locks are:
TM - DML enqueue   
TX - Transaction enqueue
UL - User supplied
-- 我们主要关注TXTM两种类型的锁
--UL 锁用户自己定义的,一般很少会定义,基本不用关注
-- 其它均为系统锁,会很快自动释放,不用关注
ID1
NUMBER
Lock identifier #1 (depends on type)
ID2
NUMBER
Lock identifier #2 (depends on type)
--- lock type TM时,id1DML-locked objectobject_id
--- lock type TX时,id1usn+slot,而id2seq
-- lock type为其它时,不用关注
LMODE
NUMBER
Lock mode in which the session holds the lock:
  • 0 - none
  • 1 - null (NULL)
  • 2 - row-S (SS)
  • 3 - row-X (SX)
  • 4 - share (S)
  • 5 - S/Row-X (SSX)
  • 6 - exclusive (X)
--大于0时表示 当前会话以某种模式 占有 该锁,等于0时表示当前会话正在等待该锁资源,即表示该会话被阻塞。
-- 往往在发生TX锁时,伴随着TM锁,比如一个sid=9会话拥有一个TM锁,一般会拥有一个或几个TX锁,但他们的id1id2是不同的,请注意
REQUEST
NUMBER
Lock mode in which the process requests the lock:
  • 0 - none
  • 1 - null (NULL)
  • 2 - row-S (SS)
  • 3 - row-X (SX)
  • 4 - share (S)
  • 5 - S/Row-X (SSX)
  • 6 - exclusive (X)
-- 大于0时,表示当前会话被阻塞,其它会话占有改锁的模式
CTIME
NUMBER
Time since current mode was granted
BLOCK
NUMBER
The lock is blocking another lock
0, 'Not Blocking', /* Not blocking any other processes */
1, 'Blocking', /* This lock blocks other processes */
2, 'Global', /* This lock is global, so we can't tell */

- - 该锁是否阻塞了另外一个锁
 

2.其它相关视图说明

视图名 描述 主要字段说明
v$session 查询会话的信息和锁的信息。
sid,serial#:表示会话信息。
program:表示会话的应用程序信息。
row_wait_obj#:表示等待的对象,和dba_objects中的object_id相对应。
lockwait :该会话等待的锁的地址,与v$lock的kaddr对应.
v$session_wait 查询等待的会话信息。
sid:表示持有锁的会话信息。
Seconds_in_wait:表示等待持续的时间信息
Event:表示会话等待的事件,锁等于enqueue
     
dba_locks 对v$lock的格式化视图。
Session_id:和v$lock中的Sid对应。
Lock_type:和v$lock中的type对应。
Lock_ID1: 和v$lock中的ID1对应。
Mode_held,mode_requested:和v$lock中
的lmode,request相对应。
v$locked_object 只包含DML的锁信息,包括回滚段和会话信息。
Xidusn,xidslot,xidsqn:表示回滚段信息。和
v$transaction相关联。
Object_id:表示被锁对象标识。
Session_id:表示持有锁的会话信息。
Locked_mode:表示会话等待的锁模式的信
息,和v$lock中的lmode一致。
 
1.查询数据库中的锁

select * from v$lock;
select * from v$lock where block=1;

2.查询被锁的对象

select * from v$locked_object;

3.查询阻塞

查被阻塞的会话
select * from v$lock where lmode=0 and  type in ('TM','TX');

查阻塞别的会话锁
select * from v$lock where lmode>0 and  type in ('TM','TX');

4.查询数据库正在等待锁的进程

select * from v$session where lockwait is not null;

5.查询会话之间锁等待的关系
select  a.sid holdsid,b.sid waitsid,a.type,a.id1,a.id2,a.ctime from v$lock a,v$lock b
where a.id1=b.id1 and a.id2=b.id2 and a.block=
1  and b.block= 0 ;
6.查询锁等待事件
select * from v$session_wait where event='enqueue';
 
_________________________________________________________________
行级锁
       行级锁只对用户正在访问的行进行锁定。如果该用户正在修改某行,那么其他用户就可以更新同一表中该行之外的数据。例如:如果用户1正在更新Jobs 表中的第一行,则用户2可以同时修改该表中的第二行。 也就是说除了该表中的第一行,其他用户可以修改任意行,但是第一行的数据其他用户只能select。
      行级锁是一种排他锁,防止其他事务修改此行,但是不会阻止读取此行的操作。在使用INSERT、UPDATE、DELETE 和SELECT…FOR UPDATE 等 语句时,Oracle 会自动应用行级锁锁定。SELECT...FOR UPDATE 语句允许用户每次选择多行记录进行更新,这些记录会被锁定,且只能由发起查询的用户进行编辑。只有在回滚或提交事务之后,锁定才会释放,其他用户才可以编辑这些记录。
     SELECT...FOR UPDATE 语句的语法如下:  
SELECT ... FOR UPDATE [OF column_list][WAIT n|NOWAIT];
其中:
OF 子句用于指定即将更新的列,即锁定行上的特定列。
WAIT 子句指定等待其他用户释放锁的秒数,防止无限期的等待。
“使用FOR UPDATE WAIT”子句的优点如下:
⒈防止无限期地等待被锁定的行;
⒉允许应用程序中对锁的等待时间进行更多的控制。
⒊对于交互式应用程序非常有用,因为这些用户不能等待不确定
举例:比如一个用户在SQL PLUS下输入这条语句:SQL>SELECT * FROM order_master WHERE vencode='V002' FOR UPDATE;
此时再开启一个SQL PLUS,以相同的用户登陆,执行下面的命令。
SQL<> SELECT * FROM order_master WHERE vencode='V02' FOR UPDATE WAIT 5;
由于要更新的行已经被锁定,上述命令在等待5秒钟之后返回,并给出如下的错误信息:
ERROR 位于第1行;
ORA-30006;资源已被占用;执行操作时出现WAIT超时。
PS:再开启的SQL PLUS 是以相同用户登陆的,用其他用户登陆更是不可能。
表级锁
      表级锁被锁定的表,暂时放在内存中,不提交不进去数据库,也就是说,多个用户在同一时间,同时修改同一个表的同一行时,同时点提交,但是还是按随机的先后被提交进数据库,而不是同时被提交,而是先随机存储后,被再次提交的覆盖。
     表级锁将保护表数据,在事务处理过程中,表级锁会限制对整个表的访问。可以使用LOCK TABLE 语句显示地锁定表。表级锁用来限制对表执行添加、更新和删除等修改操作。锁定表的语法如下:
   LOCK TABLE <table_name> IN <lock_mode> MODE [NOWAIT];
其中:
lock_mode 是锁定的模式。
NOWAIT 关键字用于防止无限期的等待其他用户释放锁。
表级锁的模式包括以下内容:
行共享 (ROW SHARE, RS): 允许其他用户访问和锁定该表,但是禁止排他锁定整个表。 行共享锁锁定后,在同一时刻,不同用户可以对同一个表中的被行共享锁锁定后的该行,具备增、删、改、查的功能。
行排他(ROW EXCLUSIVE, RX):与行共享模式相同,同时禁止其他用户在此表上使用共享锁。使用SELECT...FOR UPDATE 语句会在表上自动应用排他锁。 被行排他后,其他用户不能同时修改该行,但是可以插入行,可以查询该行,其他用户也不能再在该表上对此行进行排他。
共享(SHARE, S):共享锁将锁定表,仅允许其他用户查询表中的行,但不允许插入、更新或删除行。多个用户可以同时在同一个表中放置共享锁,即允许资源共享,因此得名“共享锁”。例如,如果用户每天都需要在结帐时更新日销售表,则可以在更改该表时使用共享锁以确保数据的一致性。 也就是说该表只能查,其他用户想修改表中行的数据,只需要对该表进行共享锁。
共享行排他(SHARE ROW EXCLUSIVE , SPX):执行比共享表更多的限制。防止其他事务在表上应用共享锁、共享行排他锁以及排他锁。 共享行排他是除了该行以外的其他行也不能增、删、改。只能在此表中加低级表。要是想在该表中更改其他行的数据,就只有其他用户对该行进行共享行排他锁,也仅仅只能修改被这个用户锁定的行,而其他的行也修改不了。
排他(EXCLUSIVE,E):对表执行最大限制。除了允许其他用户查询该表的记录,排他锁防止其他事务对表做任何更改或在表上应用任何类型的锁。 这个锁应该叫锁中之王,他锁住了的话,其他用户就只有查询的功能了,就别想在该表中干别的事了。
BTW:在能加很多锁的表中,如果第一个用户对该表锁定时,没有使用“NOWAIT”语句,是需要第一个用户对该表COMMIT或ROLLBACK 命令释放锁定后,其他用户才能对该表进行锁定。如果其他用户违反了该条,就会无期限的等待。。。。
SQL> LOCK TABLE order_mater IN SHARE MODE;
而使用下面的语句就可以很少的预防这种情况的存在:
SQL> LOCK TBALE order_mater IN SHARE MODE NOWAIT;
      下面对行级锁和表级锁来个总结,行级锁锁定的只是行,而表级锁是对表的锁定,但是在表级锁锁定表后,还可以在该表中使用行级锁。

猜你喜欢

转载自peak.iteye.com/blog/1833320
今日推荐