Oracle中数据隔离模式

我们通常在读取数据,存在以下3个问题。

1、幻想读:事务T1读取一条指定where条件的语句,返回结果集。此时事务T2插入一行新记录,恰好满足T1的where条件。然后T1使用相同的条件再次查询,结果集中可以看到T2插入的记录,这条新纪录就是幻想。
2、不可重复读取:事务T1读取一行记录,紧接着事务T2修改了T1刚刚读取的记录,然后T1再次查询,发现与第一次读取的记录不同,这称为不可重复读。
3、脏读:事务T1更新了一行记录,还未提交所做的修改,这个T2读取了更新后的数据,然后T1执行回滚操作,取消刚才的修改,所以T2所读取的行就无效,也就是脏数据。

为了处理这些问题,SQL标准定义了以下几种事务隔离级别
  READ UNCOMMITTED    幻想读、不可重复读和脏读都允许。
  READ COMMITTED      允许幻想读、不可重复读,不允许脏读
  REPEATABLE READ     允许幻想读,不允许不可重复读和脏读
  SERIALIZABLE         幻想读、不可重复读和脏读都不允许

在Oralce中,支持READ COMMITTED和SERIALIZABLE,设置方法:

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;

Oracle默认隔离模式为READ COMMITTED,示例说明:

在PLSQL中,直接打开两个空白SQL窗口,这两个窗口就有了两个session(两个事务)。

A窗口SQL:

update t_dept set memo = memo+1 where sid = '00';

B窗口SQL:

update t_dept set memo = memo+1 where sid = '00';

两个都一样。

当前t_dept.memo值为0,执行A窗口SQL,不提交,再执行B窗口SQL,由于A未提交,B需要待A提交后才会执行,此时B处理等待中,然后提交A,切到窗口B时,发现B已自动执行了,提交B,此时t_dept.memo的值为2。

这很好理解,A执行未提交时,Oracle锁定A即将更新的记录,B试图去更改记录时,被告之你得等一会,等A处理完。A提交后,Oracle告诉B,数据准备就绪,可以了操作了,然后B将此记录进行修改。

接下来说一下SERIALIZABLE模式,先看A、B两个窗口中的SQL:

A窗口SQL(不变):

update t_dept set memo = memo+1 where sid = '00';

B窗口SQL(加入SERIALIZABLE):

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

update t_dept set memo = memo+1 where sid = '00';

先执行A,不提交,再执行B,B处理等待中,然后提交A,此时会弹出来错误信息:

此错误是B窗口弹出来的。

为什么会出现这种错误,我们先了解一下SERIALIZABLE的含义,从字面理解,即串行化。

执行A时,在A窗口的事务中,memo的值+1了,由于没有提交,在B事务执行时,B事务中得到的memo的值仍然是原来的值。

提交A后,memo的值正正真真发生了变化。

然后,B开始执行update语句,发现在进行等待时memo的值和现在的值不一样了,报ora-08177。

这里为了方便说明,将memo进行了修改,实际上,即使A中不对memo进行+1,只是set  memo=memo,B一样会报这个错,这说明只要进行了修改的操作,不论改没改,B都会报错。

比上面的试验可以得知,SERIALIZABLE的事务隔离更加严格,在READ COMMITTED模式中,B窗口等待恢复后,不管memo中有没有变化,直接在其上加1,不考虑进入等待时和等待结束后,memo的值有没有发生变化。

 

posted @ 2018-06-15 17:07  rockmmm  阅读(730)  评论(0编辑  收藏  举报