`
hys1982
  • 浏览: 12073 次
  • 性别: Icon_minigender_1
  • 来自: 重庆
社区版块
存档分类
最新评论

Oracle之悲观锁和乐观锁

阅读更多

为了得到最大的性能,一般数据库都有并发机制,不过带来的问题就是数据访问的冲突。为了解决这个问题,大多数数据库用的方法就是数据的锁定。

数据的锁定分为两种方法,第一种叫做悲观锁,第二种叫做乐观锁。什么叫悲观锁呢,悲观锁顾名思义,就是对数据的冲突采取一种悲观的态度,也就是说假设数据肯定会冲突,所以在数据开始读取的时候就把数据锁定住。而乐观锁就是认为数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则让用户返回错误的信息,让用户决定如何去做。

先从悲观锁开始说。在SqlServer等其余很多数据库中,数据的锁定通常采用页级锁的方式,也就是说对一张表内的数据是一种串行化的更新插入机制,在任何时间同一张表只会插1条数据,别的想插入的数据要等到这一条数据插完以后才能依次插入。带来的后果就是性能的降低,在多用户并发访问的时候,当对一张表进行频繁操作时,会发现响应效率很低,数据库经常处于一种假死状态。而Oracle用的是行级锁,只是对想锁定的数据才进行锁定,其余的数据不相干,所以在对Oracle表中并发插数据的时候,基本上不会有任何影响。

注:对于悲观锁是针对并发的可能性比较大,而一般在我们的应用中用乐观锁足以。


Oracle的悲观锁需要利用一条现有的连接,分成两种方式,从SQL语句的区别来看,就是一种是for update,一种是for update nowait的形式。比如我们看一个例子。首先建立测试用的数据库表。

CREATE TABLE TEST(ID,NAME,LOCATION,VALUE,CONSTRAINT test_pk PRIMARY KEY(ID))AS SELECT deptno, dname, loc, 1 FROM scott.dept


这里我们利用了Oracle的Sample的scott用户的表,把数据copy到我们的test表中。首先我们看一下for update锁定方式。首先我们执行如下的select for update语句。

select * from test where id = 10 for update

通过这条检索语句锁定以后,再开另外一个sql*plus窗口进行操作,再把上面这条sql语句执行一便,你会发现sqlplus好像死在那里了,好像检索不到数据的样子,但是也不返回任何结果,就属于卡在那里的感觉。这个时候是什么原因呢,就是一开始的第一个Session中的select for update语句把数据锁定住了。由于这里锁定的机制是wait的状态(只要不表示nowait那就是wait),所以第二个Session(也就是卡住的那个sql*plus)中当前这个检索就处于等待状态。当第一个session最后commit或者rollback之后,第二个session中的检索结果就是自动跳出来,并且也把数据锁定住。不过如果你第二个session中你的检索语句如下所示。

select * from test where id = 10


也就是没有for update这种锁定数据的语句的话,就不会造成阻塞了。另外一种情况,就是当数据库数据被锁定的时候,也就是执行刚才for update那条sql以后,我们在另外一个session中执行for update nowait后又是什么样呢。比如如下的sql语句。由于这条语句中是制定采用nowait方式来进行检索,所以当发现数据被别的session锁定中的时候,就会迅速返回ORA-00054错误,内容是资源正忙, 但指定以 NOWAIT 方式获取资源。所以在程序中我们可以采用nowait方式迅速判断当前数据是否被锁定中,如果锁定中的话,就要采取相应的业务措施进行处理。

select * from test where id = 10 for update nowait


那这里另外一个问题,就是当我们锁定住数据的时候,我们对数据进行更新和删除的话会是什么样呢。比如同样,我们让第一个Session锁定住id=10的那条数据,我们在第二个session中执行如下语句。

update test set value=2 where id = 10


这个时候我们发现update语句就好像select for update语句一样也停住卡在这里,当你第一个session放开锁定以后update才能正常运行。当你update运行后,数据又被你update 语句锁定住了,这个时候只要你update后还没有commit,别的session照样不能对数据进行锁定更新等等。

总之,Oracle中的悲观锁就是利用Oracle的Connection对数据进行锁定。在Oracle中,用这种行级锁带来的性能损失是很小的,只是要注意程序逻辑,不要给你一不小心搞成死锁了就好。而且由于数据的及时锁定,在数据提交时候就不呼出现冲突,可以省去很多恼人的数据冲突处理。缺点就是你必须要始终有一条数据库连接,就是说在整个锁定到最后放开锁的过程中,你的数据库联接要始终保持住。与悲观锁相对的,我们有了乐观锁。乐观锁一开始也说了,就是一开始假设不会造成数据冲突,在最后提交的时候再进行数据冲突检测。在乐观锁中,我们有3种

常用的做法来实现。

[1]第一种就是在数据取得的时候把整个数据都copy到应用中,在进行提交的时候比对当前数据库中的数据和开始的时候更新前取得的数据。当发现两个数据一模一样以后,就表示没有冲突可以提交,否则则是并发冲突,需要去用业务逻辑进行解决。

[2]第二种乐观锁的做法就是采用版本戳,这个在Hibernate中得到了使用。采用版本戳的话,首先需要在你有乐观锁的数据库table上建立一个新的column,比如为number型,当你数据每更新一次的时候,版本数就会往上增加1。比如同样有2个session同样对某条数据进行操作。两者都取到当前的数据的版本号为1,当第一个session进行数据更新后,在提交的时候查看到当前数据的版本还为1,和自己一开始取到的版本相同。就正式提交,然后把版本号增加1,这个时候当前数据的版本为2。当第二个session也更新了数据提交的时候,发现数据库中版本为2,和一开始这个 session取到的版本号不一致,就知道别人更新过此条数据,这个

时候再进行业务处理,比如整个Transaction都Rollback等等操作。在用版本戳的时候,可以在应用程序侧使用版本戳的验证,也可以在数据库侧采用Trigger(触发器)来进行验证。不过数据库的Trigger的性能开销还是比较的大,所以能在应用侧进行验证的话还是推荐不用 Trigger。

[3]第三种做法和第二种做法有点类似,就是也新增一个Table的Column,不过这次这个column是采用timestamp型,存储数据最后更新的时间。在Oracle9i以后可以采用新的数据类型,也就是timestamp with time zone类型来做时间戳。这种Timestamp的数据精度在Oracle的时间类型中是最高的,精确到微秒(还没与到纳秒的级别),一般来说,加上数据库处理时间和人的思考动作时间,微秒级别是非常非常够了,其实只要精确到毫秒甚至秒都应该没有什么问题。和刚才的版本戳类似,也是在更新提交的时候检查当前数据库中数据的时间戳和自己更新前取到的时间戳进行对比,如果一致则OK,否则就是版本冲突。如果不想把代码写在程序中或者由于别的原因无法把代码写在现有的程序中,也可以把这个时间戳乐观锁逻辑写在Trigger或者存储过程中。

 

 

转自:http://www.blogjava.net/cheneyfree/archive/2008/01/25/177773.html

 

 

 

一、           Oracle锁机制

1、什么是锁

锁是控制“共享资源”并发存取的一种机制。注意这里说“共享资源”而不仅指“数据行”,数据库的却在行一级对表的数据加锁,但是数据库也在其它地方对各种资源的并发存取使用锁。比如说,如果一个存储过程在执行过程中,它会被加上某种模式的锁只允许某些用户执行它而不允许其他用户修改它。锁在数据库中被用来实现允许对共享资源的并发存取,同时保证数据的完整性和一致性。

2、锁的类型

在数据库中有两种基本的锁类型:排它锁(Exclusive Locks,即X锁)和共享锁(Share Locks,即S锁)。当数据对象被加上排它锁时,其他的事务不能对它读取和修改。加了共享锁的数据对象可以被其他事务读取,但不能修改。数据库利用这两种基本的锁类型来对数据库的事务进行并发控制。

根据保护的对象不同,Oracle数据库锁可以分为以下几大类:DML锁(data locks,数据锁),用于实现并发存取并保护数据的完整性;DDL锁(dictionary locks,字典锁),用于保护数据库对象的结构,如表、索引等的结构定义;内部锁和闩(internal locks and latches),保护数据库的内部结构,比如数据库解析了一条查询语句并生成了最优化的执行计划,它将把这个执行计划“latche”在library cache中然后供其它session使用。

DML锁的目的在于保证并发情况下的数据完整性,它也是我们最常见和常用的锁,本文我们主要讨论DML锁。在Oracle数据库中,DML锁主要包括TM锁和TX锁,其中TM锁称为表级锁(用来保证表的结构不被用户修改),TX锁称为事务锁或行级锁。当Oracle执行DML语句时,系统自动在所要操作的表上申请TM类型的锁。当TM锁获得后,系统再自动申请TX类型的锁,并将实际锁定的数据行的锁标志位进行置位。这样在事务加锁前检查TX锁相容性时就不用再逐行检查锁标志,而只需检查TM锁模式的相容性即可,大大提高了系统的效率。TM锁包括了SS、SX、S、X等多种模式,在数据库中用0-6来表示。不同的SQL操作产生不同类型的TM锁。如表1所示。

 

表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 and so on

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

在数据行上只有X锁(排他锁)。在 Oracle数据库中,当一个事务首次发起一个DML语句时就获得一个TX锁,该锁保持到事务被提交或回滚。当两个或多个会话在表的同一条记录上执行 DML语句时,第一个会话在该条记录上加锁,其他的会话处于等待状态。当第一个会话提交后,TX锁被释放,其他会话才可以加锁。

当Oracle数据库发生TX锁等待时,如果不及时处理常常会引起Oracle数据库挂起,或导致死锁的发生,产生ORA-60的错误。这些现象都会对实际应用产生极大的危害,如长时间未响应,大量事务失败等。

3、监控锁的相关视图

 

表2:数据字典视图说明

视图名

描述

主要字段说明

v$session

查询会话的信息和锁的信息。

sid,serial#:表示会话信息。

program:表示会话的应用程序信息。

row_wait_obj#:表示等待的对象。

和dba_objects中的object_id相对应。

v$session_wait

查询等待的会话信息。

sid:表示持有锁的会话信息。

Seconds_in_wait:表示等待持续的时间信息。

Event:表示会话等待的事件。

v$lock

列出系统中的所有的锁。

Sid:表示持有锁的会话信息。

Type:表示锁的类型。值包括TM和TX等。

ID1:表示锁的对象标识。

lmode,request:表示会话等待的锁模式的信息。用数字0-6表示,和表1相对应。

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、更新丢失

“更新丢失”是一个典型的数据库问题,在所有的多用户环境都可能遇到。简单的描述下“更新丢失”的产生:

1)session1的一个事务查询一行数据展现给user1。

2)另一个session2的一个事务也查询同一行数据展现给user2。

3)然后user1通过应用程序更新并提交这行数据,他完成了整个事务。

4)User2也同样通过应用程序更新并提交这行数据,他也完成了整个事务。

上面的过程就会造成“更新丢失”,因为所有在第三步修改的数据全部都会丢失。一个典型的例子就是售票系统,比如一个用户(user1)在网上预定查询到1号位的票还没售出,同时另一用户(user2)在现场售票点查询也查到1号位票没售出。然后user1预订了这张票(即售票系统更新了数据库表中1号位的信息“已预订”),而这时user2又将这张票卖给了现场购票的人(即user2也成功更新1号位的信息“已售”,覆盖更新了user1的更新),等到user1去拿票的时候他预定的票却已经被卖出去了,这就是应用系统出现的一个严重的问题。

2、悲观锁

“悲观锁”实际上是一种使用锁的方式,即user1主观的认为会发生“更新丢失”,所以在他查询的时候就对查询结果的数据“立刻”加锁来防止发生“更新丢失”。这是一种“悲观”的想法,所以叫做“悲观锁”。

“悲观锁”一般用于独占连接的数据库环境,至少是一个用户在一个事务的生存周期中独占这个连接,比如C/S这种结构的系统中。下面模拟下应用中如何使用“悲观锁”:

Session1:

//session1应用程序先查询信息(不加锁)

SQL> select * from test1;

 

        ID NAME                           SEX

---------- ------------------------------ --------------------

       100 iceberg3521                    male

       101 singlelove                     male

       102 myself                         male

       103 fengzhu                        male

       104 test                            female

//session1的用户想修改id=102的这条记录,取出这条记录的值绑定到变量

SQL> variable id number

SQL> variable name varchar2(30)

SQL> variable sex varchar2(20)

SQL> exec :id :=102; :name :='myself'; :sex :='male';

 

PL/SQL 过程已成功完成。

//再简单查询看要修改的行是否已被其它session修改,并对要修改的行加锁,这里使用select for update nowait来对需要修改的行进行加锁。

SQL> select * from test1 where id=:id

2 and name=:name

3 and sex=:sex

4 for update nowait;

 

        ID NAME                           SEX

---------- ------------------------------ --------------------

       102 myself                          male

这里session1重复查询并对准备修改的行加锁来防止其它session来修改这行,这种方法就叫做“悲观锁”,因为我们悲观的人为从我们查询到修改这段时间会有其他人来修改我们打算修改的记录。

这一步实际会有三种结果:

1)102这条记录没有被其他人修改,我们就重新查询出来这条记录并成功对其加锁。

2)102这条记录正在被人修改,我们加得到下面这个结果:

SQL> select * from test1 where id=:id

2 and name=:name

3 and sex=:sex

4 for update nowait;

select * from test1 where id=:id

              *

第 1 行出现错误:

ORA-00054: 资源正忙, 但指定以 NOWAIT 方式获取资源

 

3)如果102这条记录已经被人修改,我们的查询将返回0;这样我们的应用程序就需要重新查询来确定需要修改的记录,这样我们也不会重新修改别人修改的记录。

//一但我们成功加锁,我们就可以放心的修改这条记录了

SQL> update test1 set name='fz' where id=102;

 

已更新 1 行。

 

SQL> commit;

 

提交完成。

3、乐观锁

第二中方式就是“乐观锁”,这种方式在修改前才对要修改的数据加锁。即是说我们乐观的人为从我们查询到修改数据这段时间不会有其他人修改这条数据,我们直到修改前最后一刻才判断数据是否已被其他人修改过。

“乐观锁”适用于任何系统环境,但是这种方式出现“更新丢失”的几率比“悲观锁”大。

一种常用的实现“乐观锁”的方式就是应用程序保留查询出的旧值一直到更新的时候,然后象这样做:

Update table

Set column1 = :new_column1, column2 = :new_column2, ....

Where primary_key = :primary_key

And column1 = :old_column1

And column2 = :old_column2

...

上面这样做如果更新返回的结果行数为0则说明其他人已经修改了这行记录,然后我们需要告诉应用程序下一步该如何做(是重新查询重复要做的事务还是执行其它)。还有一种情况是如果其他人正在修改同样的记录,我们的update将被hang住直到别人提交或是回滚。

还有很多实现“乐观锁”的方法,这里我们介绍使用一种由触发器或者应用程序管理的特殊字段来帮我们判断要修改记录的“版本”的方法。

            这是一种简单的实现方式,通过在表中增加一个number类型的字段或者date和timestamp类型的字段来防止“更新丢失”的发生。这些字段通常由触发器来维护负责增加number字段的值或者更新date/timestamp字段的日期时间。

           只要应用程序保存这个特殊字段的值,然后在更新前一刻比较表中这行数据的这个个字段的值是否与前面读取保存的这个值相等来判断记录是否被更改过。下面简单模拟下实现过程:

           先给test1增加时间类型字段:

SQL> alter table test1 add last_mod timestamp

2 with time zone default systimestamp

3 not null;

 

表已更改。

注:timestamp with time zone这个数据类型只有9i以上才支持

//保存查询值到变量

SQL> variable id number

SQL> variable name varchar2(30)

SQL> variable sex varchar2(20)

SQL> variable last_mod varchar2(50)

SQL> begin

2 select name,sex,last_mod into :name,:sex,:last_mod

3 from test1 where id=102;

4 end;

5 /

 

PL/SQL 过程已成功完成。

//查询变量是否赋值

SQL> select :name,:sex,:last_mod from dual;

 

:NAME                            :SEX

-------------------------------- --------------------------------

:LAST_MOD

-----------------------------------------------------------------------

 

fz                               male

23-3月 -08 02.25.11.687000 下午 +08:00

//开始更新记录,使用last_mod来判断要更新的记录是否改变(这里要用到一个oracle内置函数TO_TIMESTAMP_TZ来转换:last_mod这个变量的值)。

SQL> update test1

2 set name='myself',last_mod=systimestamp

3 where id=102 and last_mod=to_timestamp_tz(:last_mod);

 

已更新 1 行。

//我们模拟在上面执行update之前如果这条记录已经被更改过,我们看到下面的结果(重复执行上面的update语句):

SQL> update test1

2 set name='myself',last_mod=systimestamp

3 where id=102 and last_mod=to_timestamp_tz(:last_mod);

 

已更新0行。

这里我们可以看到更新的记录行数为0,即我们没有更新到想要更新的这条记录,这样应用程序就可以判断出这条记录已经被别人更新过并知道接下来该如何做。

注:一般建议以上整个更新过程放在一个存储过程中来实现,不要在应用程序中直接实现此类逻辑,因为这样做将增加程序的代码量以及不便于后期维护。

4、用悲观锁还是乐观锁?

根据经验来看在oracle中使用“悲观锁”比使用“乐观锁”要好,但是“悲观锁”需要应用程序和oracle数据库是完整的独连的(因为不能跨连接加锁),通常用于C/S结构的系统。对现在很多B/S系统一般都使用“乐观锁”,但是完成这个事务的需要更大的开销。

5、阻塞

一个session1在一个资源上加了锁,而另一个session2同也需要占用该资源,这种情况下就会发生阻塞。Session2将会被hang住直到session1解锁释放资源。通常阻塞是可以避免的,如果你发现在你的交互式系统中发生了阻塞,那么很有可能你的系统也会发生“更新丢失”这种情况。这说明你的应用系统逻辑上有缺陷才导致阻塞的发生。

五种常用的DML语句都会发生阻塞(insert、update、delete、merge、select for update)。对于select for update来说只要加上nowait就可以避免阻塞,这点前面我们实验过,下面我们看看其它DML语句发生阻塞的情况:

1)阻塞insert

通常很少会在 insert的时候发生阻塞,一种情况会发生在两个session同时对有主键或唯一索引的表插入相同记录时,这时只有当第一个session提交或者回滚后第二个session才解除阻塞;另一种情况会发生在有参照完整性约束的表中,当对子表insert的时候,如果与之想关联的父表的记录正在创建或删除,那么此时对子表insert的session将被阻塞。

对于第一种发生insert阻塞的情况,可以使用序列来生成主键或有唯一索引列的值,从而避免阻塞(序列就是在多用户和高并发操作系统环境中用来生成唯一值的)。

2)阻塞merge、update、delete

当两个session同时更新同一个表的同一条(或一组)记录会就会发生阻塞,使用我们前面讨论的“悲观锁”和“乐观锁”就可以避免update的阻塞。Merge实际上就是 insert或update所以情况和处理方式跟insert和update一样。当一个session正update的时候另一个session delete同一条记录也会发生阻塞,效果同两个update的情况一样。

6、死锁

1)死锁发生在两个session同时锁住了对方正请求的资源的情况下,演示情况如下:

//2个表test2和test3,session1更新test2

SQL> update test2 set name='test2' where id=7;

 

已更新 1 行。

//session2更新test3

SQL> update test3 set name='test3' where id=100;

 

已更新 1 行。

//session2又更新test2中session1正在更新的行

SQL> update test2 set name='test3' where id=7;

此时session2阻塞

//session1又更新test3中session2正在更新的行

SQL> update test3 set name='test2' where id=100;

此时session1也被阻塞且session2报错:

SQL> update test2 set name='test3' where id=7;

update test2 set name='test3' where id=7

       *

第 1 行出现错误:

ORA-00060: 等待资源时检测到死锁

如果此时session1不commit或rollback,session2将一直被阻塞。当session1提交或回滚后,session2将更新成功:

SQL> update test3 set name='test2' where id=100;

 

已更新 1 行。

Oracle人为死锁的出现是非常罕见的,所以在系统出现死锁后oracle会自动创建一个trace文件记录死锁信息,部分内容如下:

*** 2008-03-24 19:59:09.265

*** ACTION NAME:() 2008-03-24 19:59:09.218

*** MODULE NAME:(SQL*Plus) 2008-03-24 19:59:09.218

*** SERVICE NAME:(SYS$USERS) 2008-03-24 19:59:09.218

*** SESSION ID:(207.11) 2008-03-24 19:59:09.218

DEADLOCK DETECTED

[Transaction Deadlock]

Current SQL statement for this session:

update test2 set name='test3' where id=7

The following deadlock is not an ORACLE error. It is a

deadlock due to user error in the design of an application

or from issuing incorrect ad-hoc SQL. The following

information may aid in determining the deadlock:

Deadlock graph:

                       ---------Blocker(s)-------- ---------Waiter(s)---------

Resource Name          process session holds waits process session holds waits

TX-0006001d-00000372        30     207     X             22     190           X

TX-00040000-0000036c        22     190     X             30     207           X

session 207: DID 0001-001E-00000006    session 190: DID 0001-0016-0000000C

session 190: DID 0001-0016-0000000C    session 207: DID 0001-001E-00000006

Rows waited on:

Session 190: obj - rowid = 0000CC86 - AAAMyGAAFAAAAEcAAA

(dictionary objn - 52358, file - 5, block - 284, slot - 0)

Session 207: obj - rowid = 0000C912 - AAAMkSAAFAAAABUAAA

(dictionary objn - 51474, file - 5, block - 84, slot - 0)

Information on the OTHER waiting sessions:

Session 190:

pid=22 serial=12 audsid=2465 user: 54/ICEBERG

O/S info: user: IceBerg, term: FENGZHU, ospid: 2300:2436, machine: FDJXINXI\FENGZHU

            program: sqlplus.exe

application name: SQL*Plus, hash value=3669949024

Current SQL Statement:

update test3 set name='test2' where id=100

End of information on OTHER waiting sessions.

2)还有一种情况发生死锁是由没有索引的外键引起的,oracle会在下面两种情况给整个子表加锁:

l         当更新父表主键的时候,如果子表的外键没有索引则会给整个子表加锁。

l         还有就是删除父表记录的时候,同样也会给子表加锁。

比如这样做:

//session1

SQL> create table c (x references p);

 

表已创建。

 

SQL> insert into p values(1);

 

已创建 1 行。

 

SQL> insert into p values(2);

 

已创建 1 行。

 

SQL> commit;

 

提交完成。

 

SQL> insert into c values(2);

 

已创建 1 行。

//session2

SQL> delete from p where x=1;

此时session2立刻被阻塞,此时其它session(如session3)都不能够再对c表做insert、delete及update操作,如果执行这样的操作则会立刻被阻塞并当提交或回滚session1时session2就会提示发现死锁:

//session3

SQL> insert into c values(1);

被阻塞

//session1

SQL> rollback;

 

回退已完成。

//session2

SQL> delete from p where x=1;

delete from p where x=1

            *

第 1 行出现错误:

ORA-00060: 等待资源时检测到死锁

           #实际上此时并没有死锁,但是当其它sesion(如session3)在更新c表之前占有了其它资源,而此时session1又去请求这个资源时就会造成死循环从而导致死锁。

           如果给c表的外键加了索引则可以避免死锁:

           //session1

           SQL> create index idx_c on c (x);

 

索引已创建。

 

SQL> insert into c values(1);

 

已创建 1 行。

//session2

SQL> delete from p where x=1;

此时还是会被阻塞

//session3

SQL> insert into c values(1);

同样被阻塞

//session1

SQL> rollback;

 

回退已完成。

//session2提示违反完整性约束而不会产生死锁

SQL> delete from p where x=1;

delete from p where x=1

*

第 1 行出现错误:

ORA-02292: 违反完整约束条件 (ICEBERG.SYS_C005335) - 已找到子记录

       建议:能够在程序里实现数据完整性约束就尽量不要使用主键和外键关联。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics