代码改变世界

Oracle:在Oracle中使用锁进行并发控制

2011-09-20 14:04  Tracy.  阅读(653)  评论(0编辑  收藏  举报

 现在主流的数据库管理系统中,都支持多个事务同时执行,这样提高了数据库管理系统的运行效率。试想如果只允许一个事务运行,而这个事务又需要很长的时间,那么其他的用户必须一直等待该事务结束,效率何其低下。执行不同的事务虽然可以提高性能,但是有可能会破坏数据的完整性,所以我们必须在性能和数据完整性之间进行权衡。那么什么是并发控制呢?并发控制是数据库管理系统协调多个运行事务的行为。首先了解一下,并发控制中经常遇到的三个问题。

  脏读

   我们通过一个products表来解释一下什么是脏读,该products表有一列quantity(数量),现在的值为20。假如现在有两个事务T1和T2,它们都是要更新quantity列,T1将该列值加100,T2将此列减10,但是T1执行失败进行了回滚。我们很容易计算出正确的结果20-10=10,但是如果事务按照以下方式运行,能够得出什么结果?

  时间   事务   步骤                             存储值

  1      T1     读出quantity的值                20

  2      T1      quantity=20+100             120

  3      T1      写入quantity值                  120

  4      T2      读出quantity(T1未提交)     120

  5      T2      quantity=120-10             110

  6      T1      回滚(rollback)                  20

  7      T2      写入quantity值                 110

   以上得出了110的结果,显然是不正确的,问题就在于T2读取到了T1没有提交的数据,我们把这种情况就称之为脏读。

  不可重复读

   还是事务T1和T2,它们都是要更新quantity列,T1将该列值加100,T2将此列减10,而且两个事务都成功。我们很容易计算出正确的结果20+100-10=110,但是如果事务按照以下方式运行,能够得出什么结果?

  时间   事务   步骤                             存储值

  1      T1     读出quantity的值                20

  2      T2     读出quantity的值               20

  3      T1     quantity=20+100                  120

  4      T2     quantity=20-10                  10

  5      T1     写入quantity值(更新丢失)          110

  6      T2     写入quantity值                  10

   得出了10的结果,仍然是不正确的。问题就在于T2的值覆盖了T1的值,我们把这种情况称之为不可重复读。

  幻觉读

   例如T1对一个表中的所有行修改,同时T2向该表中插入一行记录。这时在T1中就会发生还有没有被修改的数据行,就好象发生了幻觉一样。

   SQL92标准定义了四种隔离级别,以解决以上问题,四种隔离级别如下图所示:

  

   Oracle提供了三种隔离级别,分别是Read Committed Serializable和Read Only。Read Committed是Oracle的默认隔离级别,可以看出这种隔离级别消除了脏读,但是避免不了不可重复读和幻觉读的发生,不过一般的应用还是可以使用该隔离级别的。Serializable这种隔离级别可以消除以上三个问题,但是效率会降低很多。Read Only隔离级别不是SQL92标准中定义的,它不允许事务进行更新操作,这种隔离级别也可以消除脏读。

   Oracle中在事务开始之前可以通过以下方式设置隔离级别:

set transation isolation level read committed;
set transation isolation level serialezable;
set transation isolation level read only;

   也可以在单独的会话中设置隔离级别:

alter session set isolation_level read committed;
alter session set isolation_level serialezable;
alter session set isolation_level read only;

   通过刚才的介绍,我们知道了可以设置隔离级别来降低或者消除数据的不完整性,那数据库管理系统是怎么做到这一点的呢?答案是,采用锁。锁可以保护数据,当一个事务修改数据时,锁会将该数据锁定,防止这些数据在同一时刻被其它事务修改。

   大多数情况下,我们可以不必自己管理锁,Oracle会自动创建并管理,但是了解锁是如何工作的,对我们来说也是非常有必要的。下列这些情况Oracle会创建锁:

   当我们运行了create truncate alter语句时,Oracle会创建锁,称之为DDL锁。

   当我们运行了insert update delete语句,Oracle会创建锁,称之为DML锁。

   还有一种是内部锁,由Oracle在内部使用,比如管理数据文件。在这里我们不做介绍。

   从级别上讲,锁还可以分为数据库级别锁、表级别锁、行级别锁和列级别锁(Oracle不支持)。

  1、数据库级别锁:它会锁定数据库以禁止任何新会话和新事务。锁定数据库的最主要目的是在没有用户干扰的情况下完成维护。在Oracle中使用以下语句锁定数据库为限制模式:

alter system enable restricted session;

  通过下列语句取消数据库限制模式:

alter system disable restricted session;

  以下语句将锁定数据库为只读模式:

startup mount;
alter database open read only;

  2、表级别锁:它会锁定整张表,以防止其它事务对表结构进行修改。比如当我们运行一个insert语句,如果表没有没锁定,其它的事务修改了一列(将列名修改),这时我们运行update语句就会出错,所以当我们运行DML语句时,Oracle会自动将表锁定,DML语句执行完,会释放表级别锁。我们也可以通过lock语句锁定表。

  3、行级别锁:当我们运行DML语句时,当前行上就会有行级别锁,防止其它事务对该行进行修改。

  当我们运行DML语句时,Oracle会自动加锁,请看以下示例:

  update dept set loc='北京' where deptno=40;

  然后在Oracle的企业管理器中观察Oracle自动管理的锁,如下图:

  从上图中我们可以看到,我们运行了一个update语句,产生了两个锁,都是scott用户,SID表示会话是10,锁类型TM表示表级别锁,而TX表示行级别锁,占用的模式我们后面给大家介绍。

  

  锁类型:TM 锁模式:share    某个事务锁定了一张表,允许其它事务再锁定这张表,但不允许这个事务对这张表进行更新。

  锁类型:TM 锁模式:row share 某个事务锁定了一张表,允许其它事务再锁定这张表中的其它行。

  锁类型:TM 锁模式:row eclusive 某个事务锁定了一张表,允许其它事务以相同的锁模式锁定这张表。

  锁类型:TM 锁模式:share row eclusive 在share模式基础上,不允许其它事务锁定这张表。

  锁类型:TM 锁模式:eclusive  不允许其它事务再锁定该表,也不允许其它事务更新。

  锁类型:TX 锁模式:eclusive  该行已经被锁定,不允许其它事务锁定。

魔兽就是毒瘤,大家千万不要玩。