现在的位置: 首页 > 数据库 > 正文

Oracle共享游标总结(关于游标、父游标、子游标和共享游标)

2015年01月20日 数据库 ⁄ 共 4017字 评论数 1 ⁄ 阅读 4,479 次
文章目录

首先,明确一个概念,这里讨论的游标主要是共享游标(shared cursor),跟SQL语句中定义的游标(session cursor)不是一个概念。

共享游标是用户提交SQL或PL/SQL程序块到Oracle的share pool之后,在library cache中生成的一个可执行对象,这个对象我们称之为游标(cursor)。而SQL定义游标则是SELECT语句产生的多行结果集,需要声明、打开、提取、关闭。

游标定义与分类

游标包括shared cursor和session cursor:

shared cursor即是共享游标,是SQL语句在游标解析阶段生成获得的,是位于library cache中的sql或匿名的pl/sql等。其元数据被在视图V$sqlarea与v$sql中具体化。如果library cache中的父游标与子游标能够被共享,此时则为共享游标。父游标能够共享即为共享的父游标,子游标能够共享即为共享的子游标。

session cursor即系统为用户分配缓存区,用于存放SQL语句的执行结果。用户可以通过这个中间缓冲区逐条取出游标中的记录并对其处理,直到所有的游标记录被逐一处理完毕。session cursor指的跟这个session相对应的server process的PGA里(准确的说是UGA)的一块内存区域(或者说内存结构)即其主要特性表现在记录的逐条定位,逐条处理。session cursor的元数据通过v$open_cursor视图来具体化,每一个打开或解析的SQL都将位于该视图。

游标的生命周期

shared cursor生命周期

1)包含vpd的约束条件:SQL语句如果使用的表使用了行级安全控制,安全策略生成的约束条件添加到where子句中。

2)语法、语义、访问权限检查:检查SQL语句书写的正确性,对象存在性,用户的访问权限。

3)父游标缓存:将该游标(SQL语句)的文本进行哈希得到哈希值并在library cache寻找相同的哈希值,如不存在则生存父游标且保存在library cache中,按顺序完成后续步骤。如果此时存在父游标,则进一步判断是否存在子游标。若存在相同的子游标,则直接调用其子游标的执行计划执行该SQL语句,否则转到下一步进行逻辑优化。

4)逻辑优化:使用不同的转换技巧,生成语义上等同的新的SQL语句(SQL语句的改写),一旦该操作完成,则执行计划数量、搜索空间将会相应增长。其主要目的未进行转换的情况下是寻找无法被考虑到的执行计划。

5)物理优化:为逻辑优化阶段的SQL语句产生执行计划,读取数据字典中的统计信息以及动态采样的统计信息,计算开销,开销最低的执行计划将被选中。

6)子游标缓存:分配内存,生成子游标(即最佳执行计划),与父游标关联。可以在v$sqlarea, v$sql得到具体游标信息,父子游标通过sql_id关联。

对于仅仅完成步骤1与2的SQL语句即为软解析,否则即为硬解析。SQL语句在Oracle中的执行机理大概也类似这个,具体可见“Oracle SQL语句执行流程与顺序原理解析”。

共享游标包括父游标和子游标。

父游标是在进行硬解析时产生的,父游标里主要包含两种信息:SQL文本以及优化目标(optimizer goal),首次打开父游标被锁定,直到其他所有的session都关闭该游标后才被解锁。当父游标被锁定的时候是不能被LRU算法置换出library cache,只有在解锁以后才能置换出library cache,此时该父游标对应的所有子游标也同样被置换出library cache。v$sqlarea中的每一行代表了一个parent cursor,address表示其内存地址。

子游标在发生硬解析时,在产生父游标的同时,则跟随父游标会产生相应的子游标,此时V$SQL.CHILD_NUMBER的值为0。如果存在父游标,由于不同的运行环境,此时同样会产生新的子游标,新子游标的CHILD_NUMBER在已有子游标基础上以1为单位累计。子游标包括游标所有相关信息,如具体的执行计划、绑定变、OBJECT、权限、优化器设置等。子游标随时可以被LRU算法置换出library cache,当子游标被置换出library cache时,oracle可以利用父游标的信息重新构建出一个子游标来,这个过程叫reload。v$sql中的每一行表示了一个child cursor,根据hash value和address与parent cursor关联。child cursor有自己的address,即v$sql.child_address。

确定一个游标的三个主要字段:address、hash_value和child_number。sql_id可以唯一确定一个父游标,sql_id、child_number唯一确定一个子游标。

session cursor生命周期

session cursor需要从UGA中分配内存,因此有其生命周期。其生命周期主要包括:

  • 打开游标(根据游标声明的名称在UGA中分配内存区域);
  • 解析游标(将SQL语句与游标关联,并将其执行计划加载到Library Cache);
  • 定义输出变量(仅当游标返回数据时);
  • 绑定输入变量(如果与游标关联的SQL语句使用了绑定变量);
  • 执行游标(即执行SQL语句);
  • 获取游标(即获取SQL语句记录结果,根据需要对记录作相应操作。游标将逐条取出查询的记录,直到取完所有记录);
  • 关闭游标(释放UGA中该游标占有的相关资源,但Library Cache中的游标的执行计划按LRU原则清除,为其游标共享提供可能性);

对于session cursor而言,可以将游标理解为任意的DML,DQL语句(个人理解,有待核实)。即一条SQL语句实际上就是一个游标,只不过session cursor分为显示游标和隐式游标,以及游标指针。由上面游标的生命周期可知,任何的游标(SQL语句)都必须经历内存分配,解析,执行与关闭的过程。故对隐式游标而言,生命周期的所有过程由系统来自动完成。对所有的DML和单行查询(select ... into ...)而言,系统自动使用隐式游标。多行结果集的DQL则通常使用显示游标。

一个session cursor只能对应一个shared cursor,而一个shared cursor却可能同时对应多个session cursor。

共享游标举例

假设有用户SCOTT和KING,两者均有表EMP。先以SCOTT为例,执行如下语句:

以上4条语句,第1条和第4条完全相同,第1条、第2条、第3条在大小写上有不同,查询v$sqlarea:

执行结果见下图,有3条记录,说明产生了3个父游标,其中一个父游标执行了2次。这说明,SQL语句必须完全一致(大小写、空格回车等)才能共享,进而避免硬解析。

shared-cursor1

这3个父游标对应的子游标可以在v$sql中获得:

执行结果见下图,可见生成父游标时同时也生成一个以0为child_number的子游标,其sql_id和hash_value都和父游标相同。

shared-cursor2

然后使用KING用户,执行相同的语句。然后查询v$sqlarea父游标,结果仍然是3条记录,不过执行次数发生了变化:

shared-cursor3

再查询子游标,结果是6条记录,说明由于语句执行的环境不同而造成生成不同的子游标:

shared-cursor4

产生子游标的原因很多,比如上边的用户(SCHEMA)改变的例子,当然还有很多其他原因也可以导致子游标的产生,比如优化器模式的改变,或者绑定变量的窥视等,如果你想确定是由那种原因造成的,需要查看v$sql_shared_cursor。

共享游标其他知识点

  • 查看语句共享可以借助两个数据字典:V$SQLAREA和V$SQL。V$SQLAREA保留SQL语句的父游标信息,可以通过SQL_ID标识,其中的VERSION_COUNT列表示子游标的数量。V$SQL保留SQL语句的子游标信息,可以通过SQL_ID和CHILD_NUMBER标识。V$SQL_SHARED_CURSOR可以查看语句产生子游标的原因。关于v$sql和v$sqlarea视图字段及其详解见“Oracle高资源消耗SQL语句定位”。
  • 父游标的关键信息是sql文本,子游标的关键信息是执行计划和执行环境。
  • 硬解析通常是由于不可共享的父游标造成的,如经常变动的SQL语句,或动态SQL或未使用绑定变量等。
  • 解决硬解析的办法则通常是使用绑定变量来解决。
  • 与父游标SQL文本完全一致的情形下,多个相同的SQL语句可以共享一个父游标。
  • SQL文本、执行环境完全一致的情形下,子游标能够被共享,否则如果执行环境不一致则生成新的子游标。
  • 游标是可以被所有进程共享的,也就是说如果100个进程都执行相同的SQL语句,那么这100个进程都可以同时使用该SQL语句所产生的游标,从而节省了内存。每个游标都是由library cache中的两个或多个对象所体现的,至少两个对象:一个对象叫做父游标(parent cursor),包含游标的名称以及其他独立于提交用户的信息,从v$sqlarea视图里看到的都是有关父游标的信息;另外一个或多个对象叫做子游标(child cursors),如果SQL文本相同,但是可能提交SQL语句的用户不同,或者用户提交的SQL语句所涉及到的对象为同名词等,都有可能生成不同的子游标。因为这些SQL语句的文本虽然完全一样,但是上下文环境却不一样,因此这样的SQL语句不是一个可执行的对象,必须细化为多个子游标后才能够执行。子游标含有执行计划或者PL/SQL对象的程序代码块等。

注:本文内容转载汇总自互联网,参考网站主要有ITPUB、程序员部落、sina、163等。

» 声明:本站文章源于个人经验总结或书籍、互联网转载,内容仅用于个人学习,请勿转载,否则后果自负!

目前有 1 条留言 其中:访客:1 条, 博主:0 条

  1. 会发光的chen : 2015年03月21日21:35:29  -49楼 @回复 回复

    对这种一窍不通

给我留言

留言无头像?