知识大全 Oracle11g新特性之动态变量窥视

Posted 变量

篇首语:人凭志气,虎凭威势。本文由小常识网(cha138.com)小编为大家整理,主要介绍了知识大全 Oracle11g新特性之动态变量窥视相关的知识,希望对你有一定的参考价值。

Oracle11g新特性之动态变量窥视  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!

   g之前的绑定变量窥视

  我们都知道 为了能够让SQL语句共享执行计划 oracle始终都是强调在进行应用系统的设计时 必须使用绑定变量 也就是用一个变量来代替原来出现在SQL语句里的字面值 比如 对于下面三条SQL语句来说          select col from t where col = ;        select col from t where col = ;        select col from t where col = ;

  我们可以看到 这三条SQL语句几乎一样 只有最后where条件里的字面值(分别是 )不同而已 但是如果写成这个样子 则oracle是不知道这三条SQL语句是一样的 仍然把它们当作三条完全不同的SQL语句 从而在shared pool里进行硬解析 并生成最终的执行计划 但是我们会发现 这三个执行计划可能都是一样的 因此后面两次生成执行计划的工作可能是完全不必要的 这在典型的OLTP环境中更是如此 由于解析本身属于CPU密集型操作 因此为了降低对CPU的消耗 oracle建议将这样的SQL写成          select col from t where col = :v ;

  然后 分别将 传递给v 这样的话 只需要第一次传入 时进行解析即可 而后面执行 时 由于SQL文本本身没有变化 因此直接把执行计划拿来使用即可 不需要再次生成执行计划

  但是 生成执行计划本身是基于概率的理论 在不访问具体表里的数据的前提下 根据你的where条件 来猜测返回的记录数大概是多少 从而判断应该采用怎样的访问路径 很明显 这是一定要参照具体的where条件里的值才能进行猜测的 这样就与节省CPU的初衷产生了矛盾 因为节省CPU的关键是使用绑定变量 你一旦使用了绑定变量 则oracle岂不是不知道你具体的字面值了吗?

  为了解决这一问题 oracle引入了绑定变量窥视 所谓绑定变量窥视 就是指oracle在第一次解析SQL语句的时候(也就是说该SQL第一次传入shared pool) 会将你输入的绑定变量的值带入SQL语句里 从而参考你的字面值来猜测该SQL大概会返回多少条记录 从而得到优化的执行计划 然后 以后再次执行相同的SQL语句时 不再考虑你所输入的绑定变量的值 直接取出第一次生成的绑定变量

  但是 很可惜的是 使用绑定变量从而共享游标与SQL优化是两个矛盾的目标 Oracle使用绑定变量的前提 是oracle认为大部分的列的数据都是分布比较均匀的 从而 使用第一次的绑定变量的值所得到的执行计划 大多数情况下都能适用于该绑定变量的其他的值 很明显 如果第一次传入的绑定变量的值恰好占整个数据量的百分比较高 从而导致全表扫描的执行计划 而后来传入的绑定变量的值都占整个数据量的百分比都很低 则应该走索引扫描会更好的 但是由于使用了绑定变量 从而oracle并不会再去看你的绑定变量的值 而是直接拿全表扫描的执行计划来用 这时 由于使用了绑定变量 虽然我们达到了游标共享 从而节省CPU的目的 但是SQL的执行计划却不够优化了

  那么我们如何在绑定变量和SQL优化之间进行取舍呢?在OLTP应用中 由于并发性较高 CPU上的争用会比较严重 同时SQL本身执行时间较短 涉及到的数据量较少 解析所占的时间在整个SQL执行时间中占的比例较高 而花在I/O上的时间占的比例较低 因此尽管绑定变量会有SQL不够优化的问题 还是建议使用绑定变量 但是在DSS应用和数据仓库应用中 由于并发性较低 CPU上的争用较轻 同时SQL语句的执行时间都很长 而且主要时间花在等待I/O上 而解析占的比重较低 这时优化SQL执行计划的重要性就体现出来了 因此 建议不要使用绑定变量 而直接使用字面值 但是大多数的情况都是混合应用 既有OLTP又有数据仓库 这时就很难完美的解决该问题了

  我们先来看一下 g之前的绑定变量窥视是如何工作的 以 g为例

  我们先创建一个表 使得其含有的数据分布不均匀 并在该表上创建一个索引          hr@ora g > create table t as select object_id as id object_name from dba_objects;        hr@ora g > update t set id= where rownum<= ;        hr@ora g > mit;        hr@ora g > create index idx_t on t (id);

  这样 该表里id为的 记录有一万条 而id为其他值的记录都只有一条 从而 我们构建出一个分布不均匀的测试用表 然后 我们收集一下统计信息 注意 这里要收集直方图 为的是要让CBO知道id列上的数据分布不均匀          hr@ora g> begin        dbms_stats gather_table_stats(        user         t         cascade => true         method_opt => for columns id size         );        end;        /

  我们找到表t 里最大的id 然后以该id作为第一个绑定变量传入 可以想象 该绑定变量将导致走索引 注意 我们这里设定的优化器目标为all_rows          hr@ora g > select max(id) from t ;        MAX(ID)                        hr@ora g> alter system flush shared_pool;        hr@ora g> var v_id number;        hr@ora g> var v_sql_id varchar ( );        hr@ora g> exec :v_id := ;        hr@ora g> select * from t where id=:v_id;        此处省略查询结果        hr@ora g > begin        select sql_id into :v_sql_id from v$sql        where sql_text like select * from t where id=:v_id% ;        end;        /        hr@ora g > select * from table(dbms_xplan display_cursor(:v_sql_id));        PLAN_TABLE_OUTPUT                SQL_ID djwq cpbcz k child number                 select * from t where id=:v_id        Plan hash value:                 | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time                | | SELECT STATEMENT | | | | ( ) |        | | TABLE ACCESS BY INDEX ROWID| T | | | ( ) | : :         |* | INDEX RANGE SCAN | IDX_T | | | ( ) | : :                         hr@ora g> exec :v_id := ;        hr@ora g> select * from t where id=:v_id;        此处省略查询结果        hr@ora g > begin        select sql_id into :v_sql_id from v$sql        where sql_text like select * from t where id=:v_id% ;        end;        /        hr@ora g > select * from table(dbms_xplan display_cursor(:v_sql_id));        PLAN_TABLE_OUTPUT                SQL_ID djwq cpbcz k child number                 select * from t where id=:v_id        Plan hash value:                 | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time                | | SELECT STATEMENT | | | | ( ) |        | | TABLE ACCESS BY INDEX ROWID| T | | | ( ) | : :         |* | INDEX RANGE SCAN | IDX_T | | | ( ) | : :        

  从上面结果可以看出 在为绑定变量传入第一个值为 时 由于返回的记录条数较少 导致走索引扫描 当我们第二次传入绑定变量值 时 oracle不再生成新的执行计划 而直接拿索引扫描的执行路径来用

  但是 如果先传入 的绑定变量值 然后再传入 的绑定变量值时 会怎样?我们继续测试          hr@ora g> alter system flush shared_pool;        hr@ora g> set autotrace traceonly exp stat;        hr@ora g> exec :v_id := ;        hr@ora g> select * from t where id=:v_id;        hr@ora g > begin        select sql_id into :v_sql_id from v$sql        where sql_text like select * from t where id=:v_id% ;        end;        /        hr@ora g > select * from table(dbms_xplan display_cursor(:v_sql_id));        PLAN_TABLE_OUTPUT                SQL_ID djwq cpbcz k child number                 select * from t where id=:v_id        Plan hash value:                 | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |                | | SELECT STATEMENT | | | | ( )| |        |* | TABLE ACCESS FULL | T | | K | ( ) | : : |                        hr@ora g > exec :v_id := ;        hr@ora g > select * from t where id=:v_id;        hr@ora g > begin        select sql_id into :v_sql_id from v$sql        where sql_text like select * from t where id=:v_id% ;        end;        /        hr@ora g > select * from table(dbms_xplan display_cursor(:v_sql_id));        PLAN_TABLE_OUTPUT                SQL_ID djwq cpbcz k child number                 select * from t where id=:v_id        Plan hash value:                 | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |                | | SELECT STATEMENT | | | | ( )| |        |* | TABLE ACCESS FULL | T | | K | ( ) | : : |       

  很明显 先传入 的绑定变量时将导致生成的执行计划走全表扫描 后面传入的 的绑定变量的最佳执行路径应该是索引扫描 但是由于CBO并不知道这一点 而是直接拿第一次生成的执行计划来用了 于是也走全表扫描了

   g之后的动态绑定变量窥视

  而从 g开始 这个尴尬的问题开始得到了改善 因此从 g开始 引入了所谓的自适应游标共享(Adaptive Cursor Sharing) 该特性是一个非常复杂的技术 用来平衡游标共享和SQL优化这两个矛盾的目标 g里不会盲目的共享游标 而是会去查看每个绑定变量 并为不同的绑定变量来产生不同的执行计划 而oracle这么做的前提是 使用多个执行计划的所带来的收益 要比产生多个执行计划所引起的CPU开销要更大

  使用自适应游标共享时 会遵循下面的步骤         ) 一条新的SQL语句第一次传入shared pool时 还是和以前一样 进行硬解析 而且进行绑定变量窥视 计算where条件各个列的selectivity 同时如果绑定变量所在的列上存在直方图的话 也会去参考该直方图来计算selectivity 该游标会被标记为是一个绑定敏感的游标(bind sensitive cursor) 同时 oracle还会保留包含绑定变量的where条件的其他信息 比如selectivity等 Oracle会为该谓词的selectivity维持一个范围 oracle叫做立方体(cube) 只要传入的绑定变量所产生的selectivity落在该范围里面 也就是落在该cube里面 就不产生新的执行计划 而直接拿该cube所对应的执行计划来用

   ) 下次再次执行相同的SQL时 传入了新的绑定变量 假设使用新的绑定变量的谓词的selectivity落在已经存在的cube范围里 于是这次SQL的执行会使用该cube所对应的执行计划

   ) 相同的查询再次执行时 假设所使用的新的绑定变量导致这时候的selectivity不再落在已经存在的cube里了 于是也就找不到对应的执行计划 于是系统会进行一个硬解析 这将产生第二个新的执行计划 而且新的selectivity以及对应的cube也会保存下来 也就是说 这时 我们分别有两个cube以及两个执行计划

   ) 相同的查询再次执行时 假设所使用的新的绑定变量导致这时候的selectivity不落在现存的两个cube中的任何一个 所以系统又会进行硬解析 假设这时硬解析所产生的执行计划与第一次产生执行计划一样 也就是说 在第一次评估selectivity的cube时过于保守 导致cube过小 进而导致了这一次的不必要的硬解析 于是 oracle会将第一次产生的cube与这次产生的cube合并成一个新的更大的cube 那么 下次再次进行软解析的时候 如果selectivity落在新的cube里 则会使用第一次所产生的执行计划

  我们从这里可以看到 g对这个问题的处理非常精彩 这样做的结果是 系统开始运行时 CPU消耗可能会比较严重 但是随着系统不断运行 cube的不断合并从而不断扩大 于是系统的CPU消耗会不断下降 同时执行计划也会更加的合理

  我们来做个试验进行验证 我们采用 g新引入的执行计划管理特性来验证该特性

  与 g中的测试一样 创建一个数据分布不均匀的表 在数据分布不均匀的列上创建索引 并收集统计信息 收集时注意要收集直方图 从而让CBO知道该列上的数据分布不均匀          hr@ora g > create table t as select object_id as id object_name from dba_objects;        hr@ora g > select count(*) from t ;        COUNT(*)                        hr@ora g > update t set id= where rownum<= ;        hr@ora g > mit;        hr@ora g > create index idx_t on t (id);        hr@ora g > begin        dbms_stats gather_table_stats(        user         t         cascade => true         method_opt => for columns id size         );        end;        /

  我们找到表t 里最大的id 然后以该id作为第一个绑定变量传入 可以想象 该绑定变量将导致走索引          hr@ora g > select max(id) from t ;        MAX(ID)                        我们将optimizer_capture_plan_baselines设置为true 从而让oracle自动获取plan baseline         hr@ora g > alter system set OPTIMIZER_CAPTURE_PLAN_BASELINES=true;        hr@ora g > alter system flush shared_pool;        hr@ora g > var v_id number;        hr@ora g > exec :v_id := ;        hr@ora g > select * from t where id=:v_id;        hr@ora g > select * from t where id=:v_id;

  我们运行两遍select * from t where id=:v_id 从而让oracle捕获plan baseline 我们知道id为 的记录只有一条 因此该SQL应该使用索引扫描 然后我们再为绑定变量传入 我们知道id为 的记录有一万条 所以较好的执行计划不应该走已经生成的执行计划 而应该走全表扫描          hr@ora g > exec :v_id := ;        hr@ora g > set autotrace traceonly stat;        之所以设置stat是为了让该sql实际执行 但不要返回所有记录         hr@ora g > select * from t where id=:v_id;        hr@ora g > select sql_handle plan_name origin enabled accepted        from dba_sql_plan_baselines where sql_text like select * from t % ;        SQL_HANDLE PLAN_NAME ORIGIN ENA ACC                SYS_SQL_ea bbed f f c SYS_SQL_PLAN_ f f c cb a AUTO CAPTURE YES YES        SYS_SQL_ea bbed f f c SYS_SQL_PLAN_ f f cdbd e e AUTO CAPTURE YES NO

  我们可以发现 现在该SQL语句存在两个执行计划了 其中第一个执行计划 也就是accepted为YES的执行计划为v_id等于 得到的 而第二个执行计划 也就是accepted为NO的是由v_id等于 得到的 第二个执行计划还没有被加入plan baseline 所以优化器不会使用该执行计划 我们将第二个执行计划的accepted改为YES 从而让oracle考虑使用该计划          hr@ora g > var cnt number;        hr@ora g > begin        :cnt := dbms_spm alter_sql_plan_baseline(        sql_handle => SYS_SQL_ea bbed f f c         plan_name => SYS_SQL_PLAN_ f f cdbd e e         attribute_name => ACCEPTED attribute_value => YES );        end;        /

  我们来看一下这两个执行计划分别是怎样的

  注意 在这里我们要验证oracle会为不同绑定变量生成不同的执行计划时 不能使用set autotrace traceonly exp stat等其他方式 因为set autotrace得出的执行计划始终都是第一次生成的执行计划 我们通过plan baseline从侧面来验证它 当然 我们也可以通过设置sql_trace=true从而将执行计划转储出来进行验证          SQL> select * from table(dbms_xplan display_sql_plan_baseline        ( SYS_SQL_ea bbed f f c SYS_SQL_PLAN_ f f c cb a ));                        Plan hash value:                 | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |                | | SELECT STATEMENT | | | | ( )| : : |        | | TABLE ACCESS BY INDEX ROWID| T | | | ( )| : : |        |* | INDEX RANGE SCAN | IDX_T | | | ( )| : : |                        SQL> select * from table(dbms_xplan display_sql_plan_baseline        ( SYS_SQL_ea bbed f f c SYS_SQL_PLAN_ f f cdbd e e ));                        Plan hash value:                 | Id | Operation | Name | Rows | Bytes | Cost (%CPU) | Time |                | | SELECT STATEMENT | | | | ( ) | : : |        |* | TABLE ACCESS FULL | T | | | ( ) | : : |               

  很明显 第一个是索引扫描 第二个是全表扫描 同样 我们来看一下v$sql里该sql语句有几条记录          hr@ora g > select sql_text sql_id child_number plan_hash_value        from v$sql where sql_text like select * from t where% ;        SQL_TEXT SQL_ID CHILD_NUMBER PLAN_HASH_VALUE                select * from t where id=:v_id y tt xyhas g

  可以看到 该SQL语句目前在内存里只存在一个执行计划 其plan hash value就等于我们在前面plan baseline里看到的第一个走索引的执行计划的hash value 我们把该执行计划显示出来进行确认          hr@ora g > select * from table(dbms_xplan display_cursor( y tt xyhas g ));        PLAN_TABLE_OUTPUT                SQL_ID y tt xyhas g child number                 select * from t where id=:v_id        Plan hash value:                 | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |        | | SELECT STATEMENT | | | | ( ) | |        | | TABLE ACCESS BY INDEX ROWID| T | | | ( ) | : : |        |* | INDEX RANGE SCAN | IDX_T | | | ( ) | : : |       

  结果很明显 正是走索引的执行计划 然后我们继续为帮定变量传入 多执行几次          hr@ora g > exec :v_id := ;        hr@ora g > set autotrace traceonly stat;        hr@ora g > select * from t where id=:v_id;        hr@ora g > select * from t where id=:v_id;        hr@ora g > select * from t where id=:v_id;

  注意 这里 我们之所以要多执行几次 主要是因为如果只是执行一次或两次 oracle能够认识到你传入的绑定变量落在了第一次的绑定变量( )所在的cube之外 但是oracle认为你可能只是偶尔执行该绑定变量 所以并不一定会使用另外那个全表扫描的执行计划 多执行几次以后 你会发现consistent gets突然从 直线下降到了 这时就说明oracle开始使用新的全表扫描的执行计划了

  然后 这时我们再去查看v$sql里该sql语句有几条记录          hr@ora g > select sql_text sql_id child_number plan_hash_value        from v$sql where sql_text like select * from t where% ;        SQL_TEXT SQL_ID CHILD_NUMBER PLAN_HASH_VALUE                select * from t where id=:v_id y tt xyhas g         select * from t where id=:v_id y tt xyhas g

  我们发现 该SQL语句在内存里存在两条记录了 也就是存在两个子游标了 分别对应了不同的执行计划 同样 我们来看一下新产生的子游标 也就是child_number为 的执行计划是怎样的          SQL> select * from table(dbms_xplan display_cursor( y tt xyhas g ));        PLAN_TABLE_OUTPUT                SQL_ID y tt xyhas g child number                 select * from t where id=:v_id        Plan hash value:                 | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |                | | SELECT STATEMENT | | | | ( ) | |        |* | TABLE ACCESS FULL| T | | K | ( ) | : : |       

  我们还可以从另外的角度来验证 g里的动态绑定变量窥视 也就是设置sql_trace的方式 这个方式比较简单 只要先发出 alter session set sql_trace=true以后 传入两个不同的绑定变量 然后分别就不同的绑定变量多执行几次 最后调用tkprof对跟踪文件进行分析 这里注意两个地方 第一是跟踪文件位于ADR中 不再位于user_dump_dest参数所指定的目录里了 就这里的跟踪文件而言 其所在位置缺省为 $ORACLE_HOME/diag/rdbms/<DB name>/<SID>/trace目录下 第二个要注意的是使用tkprof时 添加aggregate=no选项 缺省会将相同SQL语句合并 这样你就发现不到对于相同SQL语句的不同的执行计划了

  这里节选部分使用tkprof得到的文件内容 如下所示                  SQL ID : y tt xyhas g        select *        from        t where id=:v_id                Rows Row Source Operation                TABLE ACCESS BY INDEX ROWID T (cr= pr= pw= time= us cost= size= card= )        INDEX RANGE SCAN IDX_T (cr= pr= pw= time= us cost= size= card= )(object id )                SQL ID : y tt xyhas g        select *        from        t where id=:v_id                Rows Row Source Operation                TABLE ACCESS FULL T (cr= pr= pw= time= us cost= size= card= )       

cha138/Article/program/Oracle/201311/16590

相关参考

知识大全 Oracle11g新特性点评之RMAN

Oracle11g新特性点评之RMAN  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  RMAN除

知识大全 oracle 11g 收集统计信息的新特点介绍

  对组合列创建统计信息  检查表结构  select*fromscottempwhere=;  创建虚拟列会输出虚拟列名  selectdbms_statscreate_extended_stats

知识大全 数据压缩技术

Oracle11g新特性之:数据压缩技术  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  随着数据

知识大全 Oracle 9i新特性研究系列之四 -- 延续性初始化参数文件

Oracle9i新特性研究系列之四--延续性初始化参数文件  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一

知识大全 Oracle复合变量之RECORD

Oracle复合变量之RECORD  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  CREATEO

知识大全 Oracle内存数据库11g和高速缓存11g上市

Oracle内存数据库11g和高速缓存11g上市  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!北京

知识大全 Oracle11g默认目录结构

Oracle11g默认目录结构  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  Oracleg主流

知识大全 Oracle 11g安装图文攻略

Oracle11g安装图文攻略  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  呵呵花了一个多小时

知识大全 Oracle11g备份恢复功能增强

Oracle11g备份恢复功能增强  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  Oracleg

知识大全 oracle11G空表默认不导出解决

oracle11G空表默认不导出解决  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  ORACLE