意见箱
恒创运营部门将仔细参阅您的意见和建议,必要时将通过预留邮箱与您保持联络。感谢您的支持!
意见/建议
提交建议

Oracle共享游标有哪些

来源:恒创科技 编辑:恒创科技编辑部
2023-12-23 23:07:59

本篇内容介绍了“Oracle共享游标有哪些”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

首先,明确一个概念,这里讨论的游标主要是共享游标(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为例,执行如下语句:
select * from emp where empno = 7788;
SELECT * from emp where empno = 7788;
SELECT * FROM emp WHERE empno = 7788;
select * from emp where empno = 7788;

以上4条语句,第1条和第4条完全相同,第1条、第2条、第3条在大小写上有不同,查询v$sqlarea:
select sql_id, sql_text, executions
  from v$sqlarea
 where sql_text like '%empno = 7788%'
   and sql_text not like '%from v$sqlarea%';
   
执行结果见下图,有3条记录,说明产生了3个父游标,其中一个父游标执行了2次。这说明,SQL语句必须完全一致(大小写、空格回车等)才能共享,进而避免硬解析。



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

select sql_id,
       hash_value,
       child_number,
       plan_hash_value,
       sql_text,
       executions
  from v$sql
 where sql_text like '%empno = 7788%'
   and sql_text not like '%from v$sql%';

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



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



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


产生子游标的原因很多,比如上边的用户(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对象的程序代码块等。

“Oracle共享游标有哪些”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注恒创网站,小编将为大家输出更多高质量的实用文章!


Oracle共享游标有哪些

上一篇: oracle锁表问题怎么处理 下一篇: logminer怎么使用