Oracle - 共享游标、父子游标、硬软解析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u013379717/article/details/79586648

一、共享游标的相关概念
    1、Oracle Cursor 的说明
        Oracle 里的 Cursor 分为 Shared Cursor 和 Session Cursor 两种
        Shared Cursor 即共享游标,缓存在 Library Cache 里的 SQL 和 匿名PL/SQL 等可执行对象
        Session Cursor 即通过系统为用户分配的用于存放 SQL语句 的执行结果的缓冲区
    2、Shared Cursor 的说明
         Shared Cursor 即 共享游标,是指缓存在 Library Cache(SGA下的Shared Pool中)里的 SQL语句 和 PL/SQL块,是缓存在 Library Cache 中的几十种 Library Cache Object(LCO)之一,它所属的 Namespace 是 CRSR(即Cursor)
    3、Shared Pool 的说明
        Shared Pool 主要组成由 Library Cache 和 Data Dictionary cache 组成
        Library Cache 主要用于存储 SQL语句、语句解析树、执行计划、PL/SQL程序块(包括匿名程序块、存储过程、包、函数等)、以及它们转换后能够被 Oracle 执行的代码等,这部分信息可以通过 v$librarycache 视图查询

        Data Dictionary Cache 主要用于存放数据字典信息,包括表、视图等对象的结构信息,用户以及对象权限信息;这部分信息相对稳定,在 Shared Pool 中通过字典缓存单独存放,字典缓存的内容是按行(Row)存储的(其他数据通常按 Buffer 存储),所以又被称为 Row Cache,其信息可以通过 v$rowcache 查询

    4、Library Cache 的说明

        Library Cache 中的对象被称为 Library Cache Object。Shared Cursor 是 Lco 表示的几十种对象中的一种。Library Cache 是通过 Hash Table 的形式存储的,Hash Table 经由一系列的 Hash Bucket 组成,单个 Hash Bucket 指向由 哈希值 相同的所有 Lco Handle 组成的Object Handles链表。Lco 就是以 Lco handle 的结构经由 Hash Bucket 存储在以 Hash Table 构成的 Library Cache 中。
        Lco handle也就是库缓存对象句柄,类似c语言的结构体,其中嵌套了一些子结构,保存了Name、Namespace、Lock、Pin、Heap 0 Pointer等信息
            Name属性表示的是 Lco handle 所对应的 Lco 的名称(如果是SQL语句对应的库缓存对象句柄,则属性 Name就是该SQL语句的SQL文本;如果是表对应的库缓存对象句柄,则属性Name就是该表的表明);
            Namespace 表示的是Lco Handle对应的Lco的命名空间,比如SQL语句和PL/SQL块所对应的库缓存对象句柄的namespace值都是CRSR即Cursor)
            Heap 0 Pointer:表示的是指向子结构heap 0即Lco的指针;
        Heap 0 即 Lco也是一种复杂的结构,保存了Object Types、Object Name、Flags、Tables、Datablocks等信息
            Tables:记录的是该Lco依赖的Lco的Handle的地址的集合;Table又细分为很多类,其中Child Table记录的就是从属于该Lco的子Lco的Handle的地址的集合
            Data Block Pointer:该Lco中存储的指向Data Heap的指针;Heap0 也仅仅保存是一个结构,它不保存实际的Data,实际的Data是存储在Data Heap中的
        Data Heap可以简单理解为库缓存中的一块连续的内存区域,而这些内存区域存储着Cursor的动态运行时的数据,比如特别常见的执行计划、SQL所涉及的对象定义、绑定变量类型和长度等
            Heap 6:SQL Context:SQL的执行计划就是存放Heap 6中

    5、Parent/Child Cursor 的说明
        Shared Cursor又细分为Parent Cursor(父游标)和Child Cursor(子游标)两种类型;
        Parent Cursor只包含一些为了管理文本相同的游标的管理性数据,如目标SQL的SQL文本及其相关的Hash值等; 视图 v$sqlarea 中的每一行代表了一个 Parent Cursor;Parent Cursor 通过 SQL_ID 标识,Address 表示了其内存地址,VERSION_COUNT 表示子游标的数量,即当前是第几个子游标
        Child Cursor包含了这个游标所有的相关信息,如OBJECT和权限,优化器设置、解析树、执行计划等。视图v$sql中中 的每一行表示了一个child cursor,通过SQL_ID和CHILD_NUMBER标识,第一个child cursor总是使用CHILD_NUMBER=0来表示其创建顺序;child cursor有自己的内存地址即CHILD_ADDRESS,可以根据hash value和address与parent cursor 关联。
        V$SQL_SHARED_CURSOR 标识了产生子游标的原因。
    6、Hard/Soft Parse 的说明        
        硬解析(Hard Parse)是指 Oracle 在执行 目标SQL 时,在 Library Cache 中找不到可以重用的解析树和执行计划,而不得不从头开始解析 目标SQL 并生成相应的 Parent Cursor 和 Child Cursor 的过程。当 SQL 语句第一次执行时,会进行硬解析。

        软解析(Soft Parse)是指 Oracle 在执行 目标SQL 时,在 Library Cache 中找到了匹配的 Parent Cursor 和 Child Cursor,并将存储在 Child Cursor 中的解析树和执行计划直接拿过来重用,无须从头开始解析的过程。

二、SQL解析的相关说明        
    1、Oracle 对 SQL 的处理过程      
        1)语法检查(Syntax Check),检查此 SQL 的拼写是否符合语法
        2)语义检查(Semantic Check), 检查诸如 SQL 语句中的访问对象是否存在及该用户是否具备相应的权限
        3)对 SQL 语句进行解析(Prase),利用内部算法对 SQL 进行解析,生成解析树(Parse Tree)及执行计划(Execution Plan)
        4)执行SQL,并返回结果(Execute and Return)
    2、Oracle 在解析 目标SQL 时在 Library Cache 中匹配的过程
        1)首先基于对应 LCO Handle(库缓存对象句柄)的属性 Name 和 Namespace 的值做 Hash 运算,即对于 SQL 来说是基于属性 Name 对应的该 SQL 的 SQL文本 和属性 Namespace 对应的常量 CRSR 做 Hash 运算
        2)根据得到的 Hash 值在Library Cache 的 Hash Table 中匹配是否有对应的 Hash Bucket
        3)在匹配到 Hash Bucket 之后,在这个 Hash Bucket 对应的 LCO handles(库缓存对象句柄链表)中寻找匹配的 LCO Handle(库缓存对象句柄),在SQL的过程中 LCO Handle 即 Parent Cursor 的句柄;这里需要检验 SQL文本 是否一致,因为不同的 SQL文本 计算出来的哈希值可能相同
        4)如果没有匹配到 LCO Handle,就意味着没找到对应的Parent Cursor,更没有对应的Child Cursor,也就意味着没有可以共享的解析树和执行计划,需要从头开始解析上述 目标SQL,新生成一个 Parent Cursor 和一个 Child Cursor,并挂在对应的 Hash Bucket 中,这就是硬解析
        5)如果有匹配到 LCO Handle,也就找到了这个 Handle 所指向的 LOC,即找到了匹配的 Parent Cursor,遍历这个 Parent Cursor 的 Child Tables 以查找满足重用的执行计划和解析树对应的 Child Cursor,这里需要对比涉及的对象定义、绑定变量类型和长度、优化器参数等
        6)如果有查找到满足条件的 Child Cursor,则直接重用该 Child Cursor 的执行计划和解析树等,不用再从头开始解析 目标SQL,这就是软解析
        7)如果没有没找到满足条件的 Child Cursor,就意味着没有可以共享的解析树和执行计划,需要从头开始解析上述 目标SQL,新生成一个 Child Cursor,并把这个 Child Cursor 挂在对应的 Parent Cursor 下,这也是硬解析

三、软解析和硬解析的对比
    当 Oracle 要执行 目标SQL 语句时,首选对该 SQL 的 SQL文本 做 Hash 运算,然后根据得到的 Hash 值去相关的 Hash Bucket 中扫描 LCO handles(库缓存对象句柄链表),而扫描库缓存对象句柄链表这个动作需要持有 Library Cache Latch;
    如果在 Hash Bucket 中找到了匹配的 LCO handle,即找到了 Parent Cursor,且在其中找到了匹配的 Child Cursor,则可以直接使用其执行计划、解析树等对象,最后释放 Library Cache Latch,这就是我们常说的软解析;
    如果没有在 Hash Bucket 中找到对应的 LCO Handle,需要再次持有 Library Cache Latch,在此前提下再持有 Shared Pool Latch,然后从 Shared Pool 中申请分配内存,以便新生成一个 Child Cursor 或者 Parent Cursor 和 Child Cursor,成功申请后就会释放 Shared Pool Latch,然后重新解析 SQL 并把相关的 SQL 执行计划、解析树等对象载入该 Child Cursor 里,再以 LCO Handle 的方式存储到相关的 Hash Bucket 中的 LCO Handles 链表中,最后再释放 Library Cache Latch
    软解析不会导致 Shared Pool Latch 的争用,因为软解析能够在库缓存中找到匹配的 Parent Cursor 和 Child Cursor;软解析也有可能会导致库缓存相关的如 Library Cache Latch 和 Mutex 的争用,但软解析持有库缓存相关 Latch 的次数要少,且部分持有的时间会比硬解析短。在 Oracle 11g 后,Oracle 用 Mutex 替换了相关的 Library Cache Latch

四、父游标和子游标的样例

    1、相同的SQL语句,在执行环境等相同的情况下,第一次被执行时要做硬解析,生成一个父游标和一个子游标,第二次执行时会共享第一次执行时生成的父游标和子游标

[oracle@epay ~]$ sqlplus scott/tiger

SQL> SELECT EMPNO, ENAME, JOB, SAL FROM EMP WHERE EMPNO=7566; 
 -- 第一次执行目标sql
     EMPNO ENAME      JOB              SAL
---------- ---------- --------- ----------
      7566 JONES      MANAGER         2975

SQL> SELECT SQL_ID,HASH_VALUE,ADDRESS,SQL_TEXT,LOADS,EXECUTIONS FROM V$SQLAREA WHERE SQL_TEXT LIKE '%NO=7566%';  
 -- 查看父游标,此时加载过一次,执行过一次
SQL_ID        HASH_VALUE ADDRESS          SQL_TEXT                                                  LOADS EXECUTIONS
------------- ---------- ---------------- ------------------------------------------------------ -------- ----------
fnhr4u8drbu15  460711973 000000015C853DD8 SELECT EMPNO,ENAME,JOB,SAL FROM EMP WHERE EMPNO=7566          1          1

SQL> SELECT SQL_ID,HASH_VALUE,CHILD_ADDRESS,CHILD_NUMBER,LOADS,EXECUTIONS FROM V$SQL WHERE SQL_ID='fnhr4u8drbu15';  
-- 查看子游标,此时只有一个子游标,加载过一次,执行过一次
SQL_ID        HASH_VALUE    CHILD_ADDRESS CHILD_NUMBER      LOADS EXECUTIONS 
------------- ---------- ---------------- ------------ ---------- ----------
fnhr4u8drbu15  460711973 000000015C82C017            0          1          1

SQL> SELECT EMPNO, ENAME, JOB, SAL FROM EMP WHERE EMPNO=7566;  
-- 第二次执行目标sql
     EMPNO ENAME      JOB              SAL
---------- ---------- --------- ----------
      7566 JONES      MANAGER         2975

SQL> SELECT SQL_ID,HASH_VALUE,ADDRESS,SQL_TEXT,LOADS,EXECUTIONS FROM V$SQLAREA WHERE SQL_TEXT LIKE '%EMPNO=7566%'; 
-- 查看父游标,此时加载过一次,执行过两次
SQL_ID        HASH_VALUE ADDRESS          SQL_TEXT                                                          LOADS EXECUTIONS
------------- ---------- ---------------- ------------------------------------------------------------ ---------- ----------
fnhr4u8drbu15  460711973 000000015C853DD8 SELECT EMPNO, ENAME, JOB, SAL FROM EMP WHERE EMPNO=7566               1          2

SQL> SELECT SQL_ID,HASH_VALUE,CHILD_ADDRESS,CHILD_NUMBER,LOADS,EXECUTIONS FROM V$SQL WHERE SQL_ID='fnhr4u8drbu15';  
-- 查看子游标,此时只有一个子游标,加载过一次,执行过两次
SQL_ID        HASH_VALUE    CHILD_ADDRESS CHILD_NUMBER      LOADS EXECUTIONS
------------- ---------- ---------------- ------------ ---------- ----------
fnhr4u8drbu15  460711973 000000015C82A096            0          1          2fnhr4u8drbu15  460711973 000000015C82A096            0          1          2

    2、不同的sql语句,在第一次执行时要分别做硬解析,分别生成对应的父游标和子游标

SQL> SELECT EMPNO, ENAME, JOB, SAL FROM EMP WHERE EMPNO=7900;
 -- 执行第一条SQL
     EMPNO ENAME      JOB              SAL
---------- ---------- --------- ----------
      7900 JAMES      CLERK            950

SQL> SELECT EMPNO, ENAME, JOB, SAL FROM EMP WHERE EMPNO=7788;
 -- 执行第二天SQL
     EMPNO ENAME      JOB              SAL
---------- ---------- --------- ----------
      7788 SCOTT      ANALYST         3000
      
SQL> SELECT SQL_ID,HASH_VALUE,ADDRESS,SQL_TEXT,LOADS,EXECUTIONS FROM V$SQLAREA WHERE SQL_TEXT LIKE '%EMPNO=7788%' or SQL_TEXT LIKE '%EMPNO=7900%';
 -- 查看父游标,每条SQL各加载了一次,执行了一次
SQL_ID        HASH_VALUE ADDRESS          SQL_TEXT                                                          LOADS EXECUTIONS
------------- ---------- ---------------- ------------------------------------------------------------ ---------- ----------
9vqs2frmta8mx 3885310589 000000015C5AF7D8 SELECT EMPNO, ENAME, JOB, SAL FROM EMP WHERE EMPNO=7788               1          1
6cunv8390bfwc 3523591052 000000015DDB4440 SELECT EMPNO, ENAME, JOB, SAL FROM EMP WHERE EMPNO=7900               1          1

SQL> SELECT SQL_ID,HASH_VALUE,CHILD_ADDRESS,CHILD_NUMBER,LOADS,EXECUTIONS FROM V$SQL WHERE SQL_ID='6cunv8390bfwc' or SQL_ID='9vqs2frmta8mx';
-- 查看子游标,每条SQL对应一个子游标,各执行了一次
SQL_ID        HASH_VALUE    CHILD_ADDRESS CHILD_NUMBER      LOADS EXECUTIONS
------------- ---------- ---------------- ------------ ---------- ----------
9vqs2frmta8mx 3885310589 000000015C8410C0            0          1          1
6cunv8390bfwc 3523591052 000000015C8410B5            0          1          1

    3、相同的sql语句,在不同的用户(Schema)或者执行环境等情况下,共享父游标,但生成新的子游标

[oracle@epay ~]$ sqlplus scott/tiger
 --  第一个用户登录,该用户下有一张emp表
 
SQL> SELECT EMPNO, ENAME, JOB, SAL FROM EMP WHERE EMPNO=7698; -- 使用scott用户第一次执行
 -- 使用scott用户第一次执行SQ
     EMPNO ENAME      JOB              SAL
---------- ---------- --------- ----------
      7698 BLAKE      MANAGER         2850

SQL> SELECT SQL_ID,HASH_VALUE,ADDRESS,SQL_TEXT,LOADS,EXECUTIONS FROM V$SQLAREA WHERE SQL_TEXT LIKE '%EMPNO=7698%';
-- 查看父游标,此时SQL被加载和执行了一次
SQL_ID        HASH_VALUE ADDRESS          SQL_TEXT                                                          LOADS EXECUTIONS
------------- ---------- ---------------- ------------------------------------------------------------ ---------- ----------
da4hr63f5gpzm 3696744435 000000015C394778 SELECT EMPNO, ENAME, JOB, SAL FROM EMP WHERE EMPNO=7698               1          1

SQL> SELECT SQL_ID,HASH_VALUE,CHILD_ADDRESS,CHILD_NUMBER,LOADS,EXECUTIONS FROM V$SQL WHERE SQL_ID='da4hr63f5gpzm';
 -- 查看子游标,此时只有一个子游标
SQL_ID        HASH_VALUE    CHILD_ADDRESS CHILD_NUMBER      LOADS EXECUTIONS
------------- ---------- ---------------- ------------ ---------- ----------
da4hr63f5gpzm 3696744435 000000015C84A016            0          1          1

[oracle@epay ~]$ sqlplus xl/xl
-- 切换用户,该用户下也有一张emp表

SQL> SELECT EMPNO, ENAME, JOB, SAL FROM EMP WHERE EMPNO=7698; 
 -- 使用xl用户第二次执行SQL
     EMPNO ENAME      JOB              SAL
---------- ---------- --------- ----------
      7698 BLAKE      MANAGER         2850
      
SQL> alter session set optimizer_mode=first_rows;  
 -- 仍然使用xl用户,但修改系统环境

SQL> SELECT EMPNO, ENAME, JOB, SAL FROM EMP WHERE EMPNO=7698; 
 -- 使用xl用户第三次执行SQL
     EMPNO ENAME      JOB              SAL
---------- ---------- --------- ----------
      7698 BLAKE      MANAGER         2850

SQL> SELECT SQL_ID,HASH_VALUE,ADDRESS,SQL_TEXT,LOADS,EXECUTIONS FROM V$SQLAREA WHERE SQL_TEXT LIKE '%EMPNO=7698%';
 -- 查看父游标,此时只有一个父游标,但是被加载和执行了三次
SQL_ID        HASH_VALUE ADDRESS          SQL_TEXT                                                          LOADS EXECUTIONS
------------- ---------- ---------------- ------------------------------------------------------------ ---------- ----------
da4hr63f5gpzm 3696744435 000000015C394778 SELECT EMPNO, ENAME, JOB, SAL FROM EMP WHERE EMPNO=7698               3          3

SQL> SELECT SQL_ID,HASH_VALUE,CHILD_ADDRESS,CHILD_NUMBER,LOADS,EXECUTIONS FROM V$SQL WHERE SQL_ID='da4hr63f5gpzm'; 
 -- 查看子游标,生成了三个子游标,分别用CHILD_NUMBER值为0和1和2标识
SQL_ID        HASH_VALUE     CHILD_ADDRES CHILD_NUMBER      LOADS EXECUTIONS
------------- ---------- ---------------- ------------ ---------- ----------
da4hr63f5gpzm 3696744435 000000015C84A0F0            0          1          1
da4hr63f5gpzm 3696744435 000000015C84A930            1          1          1
da4hr63f5gpzm 3696744435 000000015C7B8240            2          1          1

SQL> SELECT SQL_ID,CHILD_NUMBER,AUTH_CHECK_MISMATCH,TRANSLATION_MISMATCH,OPTIMIZER_MODE_MISMATCH FROM V$SQL_SHARED_CURSOR WHERE SQL_ID='da4hr63f5gpzm';  
-- 通过v$sql_shared_cursor查看生成新子游标的原因
SQL_ID        CHILD_NUMBER A T O
------------- ------------ - - -
da4hr63f5gpzm            0 N N N
da4hr63f5gpzm            1 Y Y N
da4hr63f5gpzm            2 N N Y

猜你喜欢

转载自blog.csdn.net/u013379717/article/details/79586648