http://space.itpub.net/17203031/viewspace-756336
 
在各种事务级别中,Oracle实现的是“Read Committed”,也就是读取的数据都是已经提交过的数据内容。在Oracle中,select不会阻塞任何操作,同样也不会被任何其他操作阻塞。
 
Oracle的select动作是不会加锁的,也不会受到数据表已经有锁的影响。其他操作,如insert、update和delete,通常会有两个锁定动作,一个是对数据表的共享锁,保护数据表结构不被DDL操作修改。另一个锁定动作是独占锁,独占修改删除的数据记录和对应的Undo段地址。
 
如果Oracle需要保证在其他会话在修改数据块,尚未提交的时候,一个会话select数据,还满足“Read Committed”,就必须要在一个地方保存住数据块的“前镜像”,也就是rollback segment/undo segment的作用。
 
当一个数据块进行修改的时候,Oracle会自动将前镜像信息保存在一个rollback/undo空间里面,数据块中包含一个寻址到rollback/undo位置,这个位置就是ITL(活动事务槽)。当事务没有结束,select操作到这个数据块的时候,会通过ITL找到前镜像信息,并且拼凑出数据块的前镜像信息。
 
另一个方面问题,如果一个select开始后,由于数据量的原因,持续很长时间。在这个时间段内发生了DML操作,比如删除了几条数据并且提交,那么select操作检索的范围中,会不会包括这几条数据呢?
 
下面我们通过实验来证明。
 
1、环境准备
 
我们选择11gR2环境进行实验。
 
 
SQL> select * from v$version;
 
BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - Production
PL/SQL Release 11.2.0.1.0 - Production
CORE       11.2.0.1.0        Production
TNS for 32-bit Windows: Version 11.2.0.1.0 - Production
NLSRTL Version 11.2.0.1.0 – Production
 
--创建实验数据表
 
13:35:25 SQL> create table t as select * from dba_objects where rownum<100;
Table created
 
Executed in 0.124 seconds
 
 
实验有一个重点,就是如何让一个数据查询持续很长时间,由于环境的限制,笔者不能拿到一个长时间操作的数据范围。所以,采用了一些变通方法。
 
 
set time on;
set serveroutput on;
declare
 coun number;
 cursor tes is select * from t;
 t_info t%rowtype;
begin
 coun := -1;
 
 open tes;
 loop
   fetch tes into t_info;
   coun := coun + 1;   
   dbms_lock.sleep(0.1);
   
   exit when tes%notfound;
 end loop; 
 dbms_output.put_line('Total Count: '||to_char(coun)); 
end;
/
 
 
代码中的部分就是使用游标逐条的fetch数据表t记录。关键部分在于dbms_lock方法sleep,它可以实现让代码终止一段时间,模拟长时间操作处理。这样,我们就可以在另一个会话中更从容的进行操作了。
 
2、一致读DML操作实验
 
下面我们进行DML操作实验,如果一个select很长时间,并且中间有已经提交的DML修改操作,如插入、删除,select的结果会有什么影响?
 
首先,我们在会话1里面执行select过程脚本。
 
 
会话1:
 
14:16:22 SQL> set time on;
14:16:22 SQL> set serveroutput on;
14:16:22SQL> declare
          2   coun number;
          3   cursor tes is select * from t;
          4   t_info t%rowtype;
          5 begin
          6   coun := -1;
          7 
          8   open tes;
          9   loop
         10     fetch tes into t_info;
         11     coun := coun + 1;
         12     dbms_lock.sleep(1);
         13 
         14     exit when tes%notfound;
         15   end loop;
         16   dbms_output.put_line('Total Count: '||to_char(coun));
         17 end;
         18 /
 
Total Count: 99
 
PL/SQL procedure successfully completed
 
Executed in100.044seconds
 
 
同时,第二个会话再执行脚本,注意相应的时间。
 
 
会话2:
 
14:15:54 SQL> set time on;
14:16:25SQL> delete t;
99 rows deleted
 
Executed in 0.016 seconds
 
14:16:29SQL> commit;
Commit complete
 
Executed in 0 seconds
 
14:16:31 SQL>
 
 
在第二个会话中,我们实现了14:16:29的时候提交了事务。此时任何select动作,都应该是不能访问到数据。
 
但是,第一个会话的启动时间是14:16:22,此时数据是存在的。执行了超过100秒之后,才运行结束。返回结果是99行,明显是有结果的。
 
之后,第一个会话再执行的时候,我们就再也看不到数据了。
 
 
14:18:03 SQL> select count(*) from t;
 
 COUNT(*)
----------
        0
Executed in 0.031 seconds
 
 
这就是典型的Oracle一致读过程。无论select持续多长时间,Oracle都会保证返回的数据SCN版本都是在发出select语句时的SCN。比如,当发出select命令的时候,SCN编号是1000,那么在检索数据表段的时候,只会去检索那些SCN小于等于1000的数据行记录。如果当前数据行正有事务信息,就会根据ITL查找Undo/Rollback中的前镜像。如果数据行的SCN大于1000,那么会去Undo/Rollback找那些Expired的记录。
 
所以,在过去手工管理Rollback的时候,如果一个select时间很长,同时数据修改频度很高,会报错1555错误,也就是Snapshot Too Old。在现在自动Undo管理的时候,这样的场景已经很少了。
 
3、Cross DDL
 
那么,我们想到一个场景,如果我们在长时间select的时候,发出DDL语句,如Truncate、Drop数据表。因为select操作不会加锁,所以不能组织DDL操作(独占六级锁)。
 
我们还是通过实验来证明。首先,我们恢复一下现场。
 
 
 
14:21:35 SQL> insert into t select * from dba_objects where rownum<100;
99 rows inserted
 
Executed in 0.032 seconds
 
14:23:05 SQL> commit;
Commit complete
 
Executed in 0.016 seconds
 
 
第一个会话,启动匿名块脚本。
 
 
14:23:27 SQL> set time on;
14:23:27 SQL> set serveroutput on;
14:23:27SQL> declare
          2   coun number;
          3   cursor tes is select * from t;
          4   t_info t%rowtype;
          5 begin
          6   coun := -1;
          7 
          8   open tes;
          9   loop
         10     fetch tes into t_info;
         11     coun := coun + 1;
         12     dbms_lock.sleep(1);
         13 
         14     exit when tes%notfound;
         15   end loop;
         16   dbms_output.put_line('Total Count: '||to_char(coun));
         17 end;
         18 /
Total Count: 99
 
PL/SQL procedure successfully completed
Executed in99.997seconds
 
 
第二个会话,进行drop数据表过程。
 
 
14:16:31 SQL> set time on;
14:23:30 SQL> drop table t purge;
 
Table dropped
 
Executed in 0.047 seconds
 
 
执行drop数据表动作。开始时间为14:23:30,持续不到1s。但是在第一个会话中,开始14:23:27,持续了100s。说明,在第一个会话结束之前,数据表就已经不存在了。
 
但是,从第一个会话结束的情况看,数据表还是存在的。第二次执行一次,第一个会话报错。
 
 
14:25:07 SQL>
14:25:19 SQL> set time on;
14:25:19 SQL> set serveroutput on;
14:25:19 SQL> declare
          2   coun number;
          3   cursor tes is select * from t;
          4   t_info t%rowtype;
          5 begin
          6   coun := -1;
          7 
          8   open tes;
          9   loop
         10     fetch tes into t_info;
         11     coun := coun + 1;
         12     dbms_lock.sleep(1);
         13 
         14     exit when tes%notfound;
         15   end loop;
         16   dbms_output.put_line('Total Count: '||to_char(coun));
         17 end;
         18 /
 
declare
 coun number;
 cursor tes is select * from t;
 t_info t%rowtype;
begin
 coun := -1;
 
 open tes;
 loop
   fetch tes into t_info;
   coun := coun + 1;
   dbms_lock.sleep(1);
 
   exit when tes%notfound;
 end loop;
 dbms_output.put_line('Total Count: '||to_char(coun));
end;
 
ORA-06550:第3行,第31列:
PL/SQL: ORA-01775:同义词的循环链
ORA-06550:第3行,第17列:
PL/SQL: SQL Statement ignored
ORA-06550:第4行,第10列:
PLS-00201:必须声明标识符'T'
ORA-06550:第4行,第10列:
PL/SQL: Item ignored
ORA-06550:第10行,第20列:
PLS-00320:此表达式的类型声明不完整或格式不正确
ORA-06550:第10行,第5列:
PL/SQL: SQL Statement ignored
 
 
报错。
 
说明,对于DDL操作(Truncate相同),一致读的现象依然存在。Oracle不会因为同时的DDL操作,影响到原来已经发出的select动作。从原理上,笔者认为还是和Undo/Rollback有关。
 
对于Truncate数据表,Oracle没有修改真正的数据,而是修改了段头信息,直接修改段头的分区extent信息。这部分动作其实也是会计入到Undo/Rollback空间,并且段信息data_object_id被修改。
 
当原有data_object_id访问需求出现的时候,Oracle会找Undo/Rollback上的段头信息,找到原有的Extent分区列表,进而可以范围数据。
 
4、结论
 
本篇演示了一致读现象,不仅对于DML操作有效,对DDL同样有效。
 

实验演示Oracle“多版本一致读”和“Cross DDL”的更多相关文章

  1. oracle 多版本

    并发控制 concurrency control 数据库提供的函数集合,允许多个人同时访问和修改数据. 锁(lock)是Oracle管理共享数据库资源并发访问并防止并发数据库事务之间“相互干涉”的核心 ...

  2. 代码演示C#各版本新功能

    代码演示C#各版本新功能 C#各版本新功能其实都能在官网搜到,但很少有人整理在一起,并通过非常简短的代码将每个新特性演示出来. 代码演示C#各版本新功能 C# 2.0版 - 2005 泛型 分部类型 ...

  3. 如何查看Oracle客户端版本

    在实际工作中,总会遇到一些需要查看.验证ORACLE客户端版本的问题,因为一台服务器可能装了多个Oracle客户端版本:也有可能你需要知道安装的版本是32位还是64位的.如何查看Oracle客户端(O ...

  4. Oracle高版本导出dmp导入Oracle低版本报错:"不是有效的导出文件、头部验证失败"解决方法

    从Oracle高版本中导出dmp,然后导入到Oracle低版本时会报错:"不是有效的导出文件.头部验证失败",解决方法: 方法一:下载软件:AlxcTools,打开后选择要修改的文 ...

  5. CentOS6.9 安装Oracle 11G 版本11.2.0.1.0

    安装实例与数据库 CentOS6.9 安装Oracle 11G 版本11.2.0.1.0 一.检查系统类别. 查看 系统的类别,这里是 64位系统:[root@localhost ~]# uname ...

  6. Oracle数据库版本10.2.0.1升级到10.2.0.3(转)

    Oracle数据库版本10.2.0.1升级到10.2.0.3 1.停止OEM/isqlplus/监听/DB实例 $ emctl stop dbconsole $ isqlplusctl stop $ ...

  7. 案例情景--在一次Oracle 数据库导出时 EXP-00008;ORA-00904:EXP-00000: oracle不同版本导入导出规则

    案例情景--在一次Oracle 数据库导出时: C:\Documents and Settings\Administrator>exp lsxy/lsxy@lsxy_db file=E:\lsx ...

  8. EBS报错FRM-92095:Oracle JInitiator版本太旧,请安装版本1.1.8.2或更高版本

    EBS打开报错FRM-92095:Oracle JInitiator版本太旧,请安装版本1.1.8.2或更高版本 打开EBS的form,系统报错,中文提示信息是:FRM-92095:oracle ji ...

  9. ORACLE PATCH 版本的查询 PL/SQL

    --ORACLE PATCH 版本的查询 PL/SQL SELECT DD.PATCH_NAME,        PP.CREATION_DATE,        PP.DRIVER_FILE_NAM ...

随机推荐

  1. 浅谈 Requests包

    浅谈 Requests包 一:Requests包是做什么的? 简单地说,是用python处理HTTP的一个包. 它的标志也非常有气质,是一个双蛇杖,按照官方的说法,一条蛇代表client,一条代表se ...

  2. Java编程练习(四)——集合框架应用

    Java集合框架小应用之扑克牌小游戏 学习了Java集合框架之后,我写了一个扑克牌小游戏来巩固知识.学习之余的练习之作,有不足之处还得多多指教了~(*/ω\*) 扑克牌小游戏背景: 1. 创建一副扑克 ...

  3. springmvc(三) 参数绑定、

    前面两章就介绍了什么是springmvc,springmvc的框架原理,并且会简单的使用springmvc以及ssm的整合,从这一章节来看,就开始讲解springmvc的各种功能实现,慢慢消化 --W ...

  4. React 实践项目 (三)

    React在Github上已经有接近70000的 star 数了,是目前最热门的前端框架.而我学习React也有一段时间了,现在就开始用 React+Redux 进行实战! 上回说到使用Redux进行 ...

  5. Python中的元类(metaclass)

    推荐+收藏:深刻理解Python中的元类(metaclass) 做一些笔记学习学习: 在大多数编程语言中,类就是用来描述如何生成一个对象的代码段,在Python中类也是一个对象,这个(类)对象自身拥有 ...

  6. poj_1679: The Unique MST【次小生成树】

    题目链接 参考博客 希望注释足够清楚..欢迎指出不足~ #include<cstdio> #include<cstring> #include<algorithm> ...

  7. fzu 2257 saya的小熊饼干

    https://vjudge.net/problem/FZU-2257 题意:略 思路: 看题解补的题.正难则反的思想求概率. 首先,由于各维数之间是独立的.所以以x为例.首先,计算可以取到(i,j) ...

  8. Jmeter使用代理服务器录制脚本

    Mark一下Jmeter使用代理服务器录制脚本,以备自己可以翻阅,也可以帮助其他人了解一下Jmeter的这个功能.其实录制脚本只是在我们工作中的一个小插曲而已,只是为了能快速看到应用程序跑的逻辑及实现 ...

  9. LINUX下的远端主机登入 校园网络注册 网络数据包转发和捕获

    第一部分:LINUX 下的远端主机登入和校园网注册 校园网内目的主机远程管理登入程序 本程序为校园网内远程登入,管理功能,该程序分服务器端和客户端两部分:服务器端为remote_server_udp. ...

  10. Android源码博文集锦4

    Android精选源码 一款常见的自定义加载动画 android开源记账项目CoCoin Android自定义view:拖拽选择按钮 Android指纹识别 一个折线图,它提供了几个非常实用的功能 一 ...