阅读更多
文章转自: http://www.blogjava.net/imdosop/archive/2008/11/07/239266.html
Oracle锁机制锁问题的详细分析
在任何多用户数据库应用中,最终必然会出现两个用户希望同时处理相同记录的情况.这种情况在逻辑上是不可能的,并且数据库必须确保其在物理上也是不可能的.事务隔离性原则要求数据库保证:在 ,这个会话无法影响另一个会话,并且后者也无法看到前者.为了实现这个要求,数据库创行话并发的数据访问,甚至在多个会话请求访问相同的记录时,数据库也必须确保这些会话排队依次进行.
借助于记录和表锁定机制,我们可以实现并发的串行化.oracle数据库中的锁定是完全自动的.一般而言,只有在试图结合软件与自动锁定机制是或者编程人员编写的代码太糟糕时才会引发某些问题.
共享锁与排他锁
Oracle数据库中锁定的标准级别保证了最大可能的并发级别也就是说,如果某个会话正在更新一条记录,那么只有这条记录会被锁定.此外,锁定这条记录是为了防止其他会话对其进行更新,其他会话可以随时执行读取操作.只有在使用commit或rollback命令结束事务之后,锁定才会被解除.这种锁定是一个”排他锁”:在指定记录上请求排他锁的第一个会话会得到这个锁定,其他请求对该记录进行写访问的会话则必须等待.虽然这条记录已通过锁定会话进行了更新,但是对其进行读访问你是被允许的(而且经常会出现这种情况),并且这些读操作会涉及撤销数据的使用,从而确保都会回并不会看到任何未被提交的变化对于一条记录或一个完整表上的一个排他锁来说,每次只能有一个会话可以获得这个排他锁,不过许多会话可以同时获得相同对象上的”共享锁”.在一条记录上设置共享锁毫无意义,其原因在于锁定一条记录的唯一目的就是不允许其他会话更改它.共享锁被置于整个表上,同时许多会话可以获得同一个表上的共享锁.在一个表上放置共享锁的目的是为了防止另一个会话获得这个表上的排他锁(在已存在共享锁的情况下无法再获得排他锁).在表上防止排他锁是需要执行DDL语句.如果其他任何会话已经在一个表上放置了共享锁,那么我们就无法执行修改某个对象的语句(例如删除这个表的某一列).
为了在记录上执行DML语句,当前会话必须获取待更新记录上的排他锁以及包含这些记录的表上的共享锁.如果另一个会话已经获取了待更新记录上的排他锁,那么当前会话将被挂起,直至使用COMMIT或ROLLBACK命令解除这些锁定,如果另一个会话已经获取了待修改记录的表上的共享锁以及其他记录上的排他锁,那么就不存在任何问题.一个表上的排他锁会锁定这个表,但是,如果不需要执行DDL语句,那么我们就可以不锁定整个表的默认锁定机制.
提示:只有在特别请求并且编程人员具有充分理由的情况下,才可以要求在整个表上放置排他锁.
DML锁与DDL锁
所有DML语句都至少需要两种锁定:受影响记录上的排他锁,以及包含受影响记录的表上的共享锁.排他锁能够防止其他会话干预指定的记录,而共享锁则能够阻止其他会话使用DDL语句修改表的定义.这两种锁定会被自动请求.如果某条DML语句在指定记录上无法获取所需的排他锁,那么这条语句会被挂起直至获得所需的排他锁.
执行DDL命令需要使用所涉及对象上的排他锁.只有在针对指定表的所有DML事务结束,并且记录上的排他锁以及表上的共享锁都被解除之后,我们才可以获得执行DDL命令所需的排他锁,任何DDL语句所需的排他锁都是被自动请求的.但是,如果无法获取所需的排他锁(通常是因为其他会话已经获得用于DML语句的共享锁),那么DDL语句就会由于错误立即终止.
例子:
1. 使用SQL*PLUS,作为用户SYSTEM连接数据库.
2. 创建一个表,并且在这个表中插入一条记录.
>create table t1(c1 number);
>insert into t1 values(1);
>commit;
3.再次使用SQL*PLUS并作为用户SYSTEM进行连接,从而打开另一个会话.
4.在第一个会话中执行一个DML命令,这个命令会在插入的记录上放置一个排他锁,同时还会在创建的表上放置一个共享锁.
>update table t1 set c1=2 where c1=1;
5.如下所示,在第二个会话中执行第一条针对新建表的DDL语句.
>alter table t1 add(c2 date);
error at line 1:
ora-00054:resource busy and acquire with nowait specified
因为DDL语句需要表上的排他锁,而这与DML语句已在表上放置了共享锁相冲突,所以试图在表中插入一个列的这条DDL语句会失败.需要注意的是:在类似情况下,DML语句会等待并不断进行尝试,直至获得其所需的锁(换句话说就是挂起);而DDL语句则会由于错误立即终止.
6.在第一个会话中,提交当前事务
>commit;
7.在第二个会话中,重新执行步骤5.此时,因为不纯在与DDL排他锁相冲突的DML共享锁,因此DDL语句将成功的执行.
8.在第一个会话中 ,锁定整个表.
>lock table t1 in exclusive mode;
9.在第二个会话中,插入一条记录.此时,这个会话将被挂起.
>insert into t1 values (1,sysdate);
10.在第一个会话中,通过执行COMMIT命令解除整个表上的锁定.需要注意的是,ROLLBACK命令也可以实现相同的目的.
>commit;
11.第二个会话会释放并且现在会完成插入操作.随后,执行COMMIT命令,终止当前事务斌且解除该记录上的排他锁.
关于如何解决死锁的问题.
1.查哪个过程被锁
查V$DB_OBJECT_CACHE视图:
SELECT * FROM V$DB_OBJECT_CACHE WHERE OWNER=''过程的所属用户'' AND LOCKS!=''0'';
2. 查是哪一个SID,通过SID可知道是哪个SESSION.
查V$ACCESS视图:
SELECT * FROM V$ACCESS WHERE OWNER=''过程的所属用户'' AND NAME=''刚才查到的过程名