分类: Java
2011-04-28 14:03:01
脏读
我们通过一个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,锁类型TX表示表级别锁,而TX表示行级别锁,占用的模式我们后面给大家介绍。
锁类型:TM 锁模式:share 某个事务锁定了一张表,允许其它事务再锁定这张表,但不允许这个事务对这张表进行更新。
锁类型:TM 锁模式:row share 某个事务锁定了一张表,允许其它事务再锁定这张表中的其它行。
锁类型:TM 锁模式:row eclusive 某个事务锁定了一张表,允许其它事务以相同的锁模式锁定这张表。
锁类型:TM 锁模式:share row eclusive 在share模式基础上,不允许其它事务锁定这张表。
锁类型:TM 锁模式:eclusive 不允许其它事务再锁定该表,也不允许其它事务更新。
锁类型:TX 锁模式:eclusive 该行已经被锁定,不允许其它事务锁定。