首页 数据库 mysql教程 Oracle11g新特性之动态变量窥视

Oracle11g新特性之动态变量窥视

Jun 07, 2016 pm 03:12 PM
动态 变量 新特性 特性

欢迎进入Oracle社区论坛,与200万技术人员互动交流 >>进入 1. 11g之前的绑定变量窥视 我们都知道,为了能够让SQL语句共享执行计划,oracle始终都是强调在进行应用系统的设计时,必须使用绑定变量,也就是用一个变量来代替原来出现在SQL语句里的字面值。比如

欢迎进入Oracle社区论坛,与200万技术人员互动交流 >>进入

        1. 11g之前的绑定变量窥视

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

         select col1 from t where col2 = 1;
        select col1 from t where col2 = 2;
        select col1 from t where col2 = 3;

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

         select col1 from t where col2 = :v1;

        然后,分别将1、2、3传递给v1,这样的话,只需要第一次传入1时进行解析即可。而后面执行2、3时,由于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又有数据仓库,这时就很难完美的解决该问题了。

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

        我们先创建一个表,使得其含有的数据分布不均匀,并在该表上创建一个索引。

         hr@ora10g > create table t1 as select object_id as id,object_name from dba_objects;
        hr@ora10g > update t1 set id=1 where rownum        hr@ora10g > commit;
        hr@ora10g > create index idx_t1 on t1(id);

        这样,该表里id为的1记录有一万条,而id为其他值的记录都只有一条。从而,我们构建出一个分布不均匀的测试用表。然后,我们收集一下统计信息。注意,这里要收集直方图,为的是要让CBO知道id列上的数据分布不均匀。

         hr@ora10g> begin
        2 dbms_stats.gather_table_stats(
        3 user,
        4 't1',
        5 cascade => true,
        6 method_opt => 'for columns id size 254'
        7 );
        8 end;
        9 /

        我们找到表t1里最大的id,然后以该id作为第一个绑定变量传入,可以想象,该绑定变量将导致走索引。注意,我们这里设定的优化器目标为all_rows。

         hr@ora11g > select max(id) from t1;
        MAX(ID)
        ----------
        13871
        hr@ora10g> alter system flush shared_pool;
        hr@ora10g> var v_id number;
        hr@ora10g> var v_sql_id varchar2(20);
        hr@ora10g> exec :v_id := 13871;
        hr@ora10g> select * from t1 where id=:v_id;
        此处省略查询结果
        hr@ora10g > begin
        2 select sql_id into :v_sql_id from v$sql
        3 where sql_text like 'select * from t1 where id=:v_id%';
        4 end;
        5 /
        hr@ora10g > select * from table(dbms_xplan.display_cursor(:v_sql_id));
        PLAN_TABLE_OUTPUT
        --------------------------------------------------------------------------------
        SQL_ID djwq30cpbcz7k, child number 0
        -------------------------------------
        select * from t1 where id=:v_id
        Plan hash value: 50753647
        --------------------------------------------------------------------------------
        | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time
        --------------------------------------------------------------------------------
        | 0 | SELECT STATEMENT | | | | 11 (100) |
        | 1 | TABLE ACCESS BY INDEX ROWID| T1 | 1365 | 28665| 11 (0) | 00:00:01
        |* 2 | INDEX RANGE SCAN | IDX_T1 | 1365 | | 3 (0) | 00:00:01
        --------------------------------------------------------------------------------
        ......
        hr@ora10g> exec :v_id := 1;
        hr@ora10g> select * from t1 where id=:v_id;
        此处省略查询结果
        hr@ora10g > begin
        2 select sql_id into :v_sql_id from v$sql
        3 where sql_text like 'select * from t1 where id=:v_id%';
        4 end;
        5 /
        hr@ora10g > select * from table(dbms_xplan.display_cursor(:v_sql_id));
        PLAN_TABLE_OUTPUT
        --------------------------------------------------------------------------------
        SQL_ID djwq30cpbcz7k, child number 0
        -------------------------------------
        select * from t1 where id=:v_id
        Plan hash value: 50753647
        --------------------------------------------------------------------------------
        | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time
        --------------------------------------------------------------------------------
        | 0 | SELECT STATEMENT | | | | 11 (100) |
        | 1 | TABLE ACCESS BY INDEX ROWID| T1 | 1365 | 28665| 11 (0) | 00:00:01
        |* 2 | INDEX RANGE SCAN | IDX_T1 | 1365 | | 3 (0) | 00:00:01
        --------------------------------------------------------------------------------

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

        但是,如果先传入1的绑定变量值,然后再传入13871的绑定变量值时,会怎样?我们继续测试。

         hr@ora10g> alter system flush shared_pool;
        hr@ora10g> set autotrace traceonly exp stat;
        hr@ora10g> exec :v_id := 1;
        hr@ora10g> select * from t1 where id=:v_id;
        hr@ora10g > begin
        2 select sql_id into :v_sql_id from v$sql
        3 where sql_text like 'select * from t1 where id=:v_id%';
        4 end;
        5 /
        hr@ora10g > select * from table(dbms_xplan.display_cursor(:v_sql_id));
        PLAN_TABLE_OUTPUT
        --------------------------------------------------------------------------------
        SQL_ID djwq30cpbcz7k, child number 0
        -------------------------------------
        select * from t1 where id=:v_id
        Plan hash value: 3617692013
        --------------------------------------------------------------------------
        | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
        --------------------------------------------------------------------------
        | 0 | SELECT STATEMENT | | | | 13 (100)| |
        |* 1 | TABLE ACCESS FULL | T1 | 8738 | 179K | 13 (0) | 00:00:01 |
        --------------------------------------------------------------------------
        ......
        hr@ora10g > exec :v_id := 13871;
        hr@ora10g > select * from t1 where id=:v_id;
        hr@ora10g > begin
        2 select sql_id into :v_sql_id from v$sql
        3 where sql_text like 'select * from t1 where id=:v_id%';
        4 end;
        5 /
        hr@ora10g > select * from table(dbms_xplan.display_cursor(:v_sql_id));
        PLAN_TABLE_OUTPUT
        --------------------------------------------------------------------------------
        SQL_ID djwq30cpbcz7k, child number 0
        -------------------------------------
        select * from t1 where id=:v_id
        Plan hash value: 3617692013
        --------------------------------------------------------------------------
        | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
        --------------------------------------------------------------------------
        | 0 | SELECT STATEMENT | | | | 13 (100)| |
        |* 1 | TABLE ACCESS FULL | T1 | 8738 | 179K | 13 (0) | 00:00:01 |
        --------------------------------------------------------------------------

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

[1] [2] 

Oracle11g新特性之动态变量窥视

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
2 周前 By 尊渡假赌尊渡假赌尊渡假赌
仓库:如何复兴队友
4 周前 By 尊渡假赌尊渡假赌尊渡假赌
Hello Kitty Island冒险:如何获得巨型种子
4 周前 By 尊渡假赌尊渡假赌尊渡假赌

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

将VirtualBox固定磁盘转换为动态磁盘,反之亦然 将VirtualBox固定磁盘转换为动态磁盘,反之亦然 Mar 25, 2024 am 09:36 AM

在创建虚拟机时,系统会要求您选择磁盘类型,您可以选择固定磁盘或动态磁盘。如果您选择了固定磁盘,后来意识到需要动态磁盘,或者相反,该怎么办?好!你可以把一种转换成另一种。在这篇文章中,我们将看到如何将VirtualBox固定磁盘转换为动态磁盘,反之亦然。动态磁盘是一种虚拟硬盘,它最初具有较小的大小,随着您在虚拟机中存储数据,其大小会相应增长。动态磁盘在节省存储空间方面非常高效,因为它们只占用所需的主机存储空间。然而,随着磁盘容量的扩展,可能会稍微影响计算机的性能。固定磁盘和动态磁盘是虚拟机中常用的

什么是Java中的实例变量 什么是Java中的实例变量 Feb 19, 2024 pm 07:55 PM

Java中的实例变量是指定义在类中,而不是方法或构造函数中的变量。实例变量也称为成员变量,每个类的实例都有自己的一份实例变量副本。实例变量在创建对象的过程中被初始化,以及在对象的生命周期中保存并保持其状态。实例变量的定义通常放在类的顶部,可以用任何访问修饰符来声明,可以是public、private、protected或默认访问修饰符。这取决于我们希望这个变

如何使用Ajax从PHP方法中获取变量? 如何使用Ajax从PHP方法中获取变量? Mar 09, 2024 pm 05:36 PM

使用Ajax从PHP方法中获取变量是Web开发中常见的场景,通过Ajax可以实现页面无需刷新即可动态获取数据。在本文中,将介绍如何使用Ajax从PHP方法中获取变量,并提供具体的代码示例。首先,我们需要编写一个PHP文件来处理Ajax请求,并返回所需的变量。下面是一个简单的PHP文件getData.php的示例代码:

Python 语法的思维导图:深入理解代码结构 Python 语法的思维导图:深入理解代码结构 Feb 21, 2024 am 09:00 AM

python凭借其简单易读的语法,广泛应用于广泛的领域中。掌握Python语法的基础结构至关重要,既可以提高编程效率,又能深入理解代码的运作方式。为此,本文提供了一个全面的思维导图,详细阐述了Python语法的各个方面。变量和数据类型变量是Python中用于存储数据的容器。思维导图展示了常见的Python数据类型,包括整数、浮点数、字符串、布尔值和列表。每个数据类型都有其自身的特性和操作方法。运算符运算符用于对数据类型执行各种操作。思维导图涵盖了Python中的不同运算符类型,例如算术运算符、比

jQuery使用实践:判断变量是否为空的几种方式 jQuery使用实践:判断变量是否为空的几种方式 Feb 27, 2024 pm 04:12 PM

jQuery是一个广泛应用于Web开发中的JavaScript库,它提供了许多简洁方便的方法来操作网页元素和处理事件。在实际开发中,经常会遇到需要判断变量是否为空的情况。本文将介绍使用jQuery判断变量是否为空的几种常用方法,并附上具体的代码示例。方法一:使用if语句判断varstr="";if(str){co

解析MyBatis中的动态SQL标签:select标签 解析MyBatis中的动态SQL标签:select标签 Feb 24, 2024 pm 12:15 PM

动态SQL是MyBatis框架中非常重要的功能之一,它可以根据不同的条件在SQL语句中进行动态的拼接和处理,从而实现灵活的SQL操作。其中,选择标签是动态SQL中的一种关键标签,它主要用于实现条件选择的逻辑。本文将探讨MyBatis中选择标签的使用方法,并提供具体的代码示例进行演示。1.选择标签的基本语法选择标签在MyBatis中主要有两种形式:

深入理解C语言中的const 深入理解C语言中的const Feb 18, 2024 pm 12:56 PM

C中const的详解及代码示例在C语言中,const关键字用于定义常量,表示该变量的值在程序执行过程中不能被修改。const关键字可以用于修饰变量、函数参数以及函数返回值。本文将对C语言中const关键字的使用进行详细解析,并提供具体的代码示例。const修饰变量当const用于修饰变量时,表示该变量为只读变量,一旦赋值就不能再修改。例如:constint

Golang中有类似类的面向对象特性吗? Golang中有类似类的面向对象特性吗? Mar 19, 2024 pm 02:51 PM

在Golang(Go语言)中并没有传统意义上的类的概念,但它提供了一种称为结构体的数据类型,通过结构体可以实现类似类的面向对象特性。在本文中,我们将介绍如何使用结构体实现面向对象的特性,并提供具体的代码示例。结构体的定义和使用首先,让我们看一下结构体的定义和使用方式。在Golang中,结构体可以通过type关键字定义,然后在需要的地方使用。结构体中可以包含属

See all articles