ITPub博客

首页 > Linux操作系统 > Linux操作系统 > select与update的读一致

select与update的读一致

原创 Linux操作系统 作者:redhouser 时间:2011-05-26 11:05:25 0 删除 编辑

问题:

一般的理解,在read commited隔离级别,Oracle实现了语句级读一致,即语句开始执行时数据库的一致状态,包括对select,update,delete。

问题:如果更新一个大表,需要更新的行在加锁前被其他会话更新并提交,更新操作会如何处理:

报错?

继续更新?如果继续更新,将会读取到更新操作开始后提交的数据,即读到了不一致数据。

测试表明,Oracle选择了继续更新,即update没有保证读一致性。

测试方法:
会话一通过全表扫描查询/执行一个大的更新,将会执行较长时间;
会话二通过索引只更新会话一更新范围中的最后一条,将会很快返回。
这样,就可以通过分析更新结果确定会话一读到的是会话二更新前或更新后的数据。

--1,环境
select * from v$version;
Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 - 64bi
PL/SQL Release 10.2.0.4.0 - Production
CORE  10.2.0.4.0  Production
TNS for IBM/AIX RISC System/6000: Version 10.2.0.4.0 - Productio
NLSRTL Version 10.2.0.4.0 - Production

--2,测试表
drop table t;

create table t nologging
as
select rownum rn,1 x from dual connect by level<=50000000;

create index idx_t_rn on t(rn) nologging parallel 8;
alter index idx_t_rn noparallel;

--3,案例 1--------
--session 1---
select count(*),sum(x) from t;

--session 2,在会话1开始后执行,由于使用索引,很快返回---
update t set x=0 where rn=50000000;
commit;


--session 1,12秒后返回---
select count(*),sum(x) from t;
50000000 50000000

结论:以上测试说明,session 1查询到的是查询开始时的一致数据(使用undo中的before image)。

--4,案例2--------
--变化:更新结果为常量

--恢复数据
update t set x=1 where rn=50000000;
commit;

--session 1---
update t set x=2 where rn<=50000000*0.1;

--session 2,在会话1开始后执行---
update t set x=0 where rn=50000000*0.1;
commit;

--session 1---
75秒后返回,待commit;
select count(*) from t where x<>2 and rn<=50000000*0.1;
0
==>没有x=0的记录,说明会话2结果被更新

结论:以上测试说明,session 1更新了session 2更新过的数据,即session 1
更新了scn晚于update语句开始时的scn的数据,即丢失了session2的更新。

--5,案例3--------
--变化:更新结果是原值+1

--恢复数据
update t set x=1 where rn<=50000000*0.1;
commit;

--session 1---
update t set x=x+1 where rn<=50000000*0.1;

--session 2,在会话1开始后执行,0.1秒后返回---
update t set x=0 where rn=50000000*0.1;
commit;

--session 1---
--62秒后返回,待commit
commit;
select * from t where x<>2 and rn<=50000000*0.1;
RN      X
5000000 1
==>RN=5000000的记录x=1,说明会话2结果被更新,而且更新时读到的x=0.

结论:与案例2相同。

--6,案例4--------
--变化:更新条件中引入待更新列

--恢复数据
update t set x=1 where rn<=50000000*0.1;
commit;

--session 1---
update t set x=x+1 where rn<=50000000*0.1 and x <10;

--session 2,在会话1开始后执行,0.1秒后返回---
update t set x=0 where rn=50000000*0.1;
commit;

--session 1---
--60秒后返回,待commit
commit;
select * from t where x<>2 and rn<=50000000*0.1;
RN      X
5000000 1
==>RN=5000000的记录x=1,说明会话2结果被更新,而且更新时读到的x=0.

结论:与案例2相同。

--7,案例5--------
--变化:更新条件中x=1

--恢复数据
update t set x=1 where rn<=50000000*0.1;
commit;

--session 1---
update t set x=x+1 where rn<=50000000*0.1 and x=1;

--session 2,在会话1开始后执行,0.1秒后返回---
update t set x=0 where rn=50000000*0.1;
commit;

--session 1---
--96秒后返回,待commit
commit;
select * from t where x<>2 and rn<=50000000*0.1;
RN      X
5000000 0
==>RN=5000000的记录x=0,说明会话一读到该行时x<>1.

结论:与案例2相同。

 

 

 

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/18922393/viewspace-696331/,如需转载,请注明出处,否则将追究法律责任。

上一篇: 没有了~
请登录后发表评论 登录
全部评论

注册时间:2011-05-26

  • 博文量
    211
  • 访问量
    809963