为什么Oracle中包含绑定变量的SQL会有多个游标?(译自Oracle Blog)

12 篇文章 2 订阅

在这里插入图片描述
原文网址:
https://blogs.oracle.com/optimizer/post/why-are-there-many-cursors-for-queries-containing-bind-variables
摘要:为同一个SQL的不同绑定变量值生成不同的执行计划是Oracle数据库的独门绝技,这个特性让Oracle的优化器变得更加智能。这篇Oracle原厂大师的文章深入浅出地阐述了这个特性等适用场景和效果,值的Oracle DBA仔细阅读。
在这里插入图片描述
作者:Maria Colgan,Oracle公司的Distinguished Product Manager,自1996年7.3版发布以来一直在Oracle公司工作。Maria的核心职责是编写关于Oracle数据库的资料和课件,以及这些资料在客户环境中的最佳实践。她还负责将客户和合作伙伴的反馈纳入产品的未来版本中。在此角色之前,她是Oracle数据库内存和Oracle查询优化器的产品经理。

译者,姚远

为了改进包含绑定变量的SQL的执行计划,Oracle在11g版本中引入了一项名为自适应游标共享(ACS,Adaptive Cursor Sharing)的新功能。此功能可以为包含绑定变量的SQL生成多个游标,我们将在本文中解释原因。在我们讨论细节之前,让我们先回顾一下历史。

Oracle在 9i版本中引入了绑定偷窥(Bind Peeking)功能。通过绑定偷窥,优化器在第一次调用游标时会查看用户定义的绑定变量的值。这允许优化器确定WHERE子句条件的选择性,就好像使用了字面量而不是绑定变量一样,从而提高了为包含绑定变量的SQL生成的执行计划的质量。

然而,当WHERE子句中使用的列中的数据分布扭曲时,这种方法存在问题。如果该列中的数据分布扭曲,则在统计信息收集期间,该列上可能创建了直方图。当优化器查看用户定义的绑定变量的值并选择执行计划时,不能保证这个计划对绑定变量的所有可能值都是合适的。换句话说,该计划是针对绑定变量的偷窥值进行了优化,而不是针对所有可能的值进行了优化。

在Oracle 11g中,对于使用绑定变量的单个语句,优化器可以生成多个不同执行计划。这确保了根据绑定值使用最佳执行计划。让我们根据一个例子看看它是如何工作的。

假设我有简单的表EMP,它有10万行,在DEPTNO列上有一个名为EMP_I1的索引。

SQL> desc emp

Name                   Null?    Type
---------------------- -------- ----------------------------------
ENAME                           VARCHAR2(20)
EMPNO                           NUMBER
PHONE                           VARCHAR2(20)
DEPTNO                          NUMBER

DEPTNO列上的数据分布扭曲,因此当我在EMP表上收集统计信息时,Oracle会自动在DEPTNO列上创建直方图。

SQL> desc emp

Name                   Null?    Type
---------------------- -------- ----------------------------------
ENAME                           VARCHAR2(20)
EMPNO                           NUMBER
PHONE                           VARCHAR2(20)
DEPTNO                          NUMBER

现在我们在EMP表上执行一个简单的select语句,这个语句的WHERE子句谓词会查询DEPTNO列。谓词中包含一个绑定变量。我们首先将这个绑定变量的值设置为9,9这个值在表中出现了10次,即0.0001%的行。

SQL> exec :deptno := 9
SQL> select /*ACS_1*/ count(*), max(empno)
     from emp
     where deptno = :deptno;

COUNT(*)   MAX(EMPNO)
---------- ----------
        10         99

鉴于9这个值的选择性,我们预计这个查询会进行索引范围扫描。让我们检查一下执行计划。

SQL> select * from table(dbms_xplan.display_cursor);

PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------
SQL_ID  272gr4hapc9w1, child number 0
------------------------------------------------------------------------
select /*ACS_1*/ count(*), max(empno) from emp where deptno = :deptno

Plan hash value: 3184478295
------------------------------------------------------------------------
| Id | Operation                    | Name  | Rows | Bytes |Cost (%CPU)|
------------------------------------------------------------------------
|  0 | SELECT STATEMENT             |       |      |       |    2 (100)|
|  1 |  SORT AGGREGATE              |       |     1|    16 |           |
|  2 |   TABLE ACCESS BY INDEX ROWID| EMP   |     1|    16 |    2   (0)|
|  3 |    INDEX RANGE SCAN          | EMP_I1|     1|       |    1   (0)|
------------------------------------------------------------------------

果然,我们看到了预期的索引范围扫描。现在让我们看看这个语句的执行统计信息。

SQL> select child_number, executions, buffer_gets,
     is_bind_sensitive, is_bind_aware
     from v$sql
     where sql_text like 'select /*ACS_1%';

CHILD_NUMBER EXECUTIONS BUFFER_GETS IS_BIND_SENSITIVE IS_BIND_AWARE 
------------ ---------- ----------- ----------------- ------------- 
          0          1          53  Y                 N

这里可以看到,这个SQL有一个子游标,已经执行过一次,并且BUFFER_GETS的值很少,只有53。我们还看到游标已被标记为绑定敏感(IS_BIND_SENSITIVE)。如果优化器认为最优计划可能取决于绑定变量的值,则游标会被标记为绑定敏感。当游标被标记为绑定敏感时,Oracle会使用不同的绑定值监控游标的行为,以确定是否需要针对不同的绑定值生成不同的执行计划。此游标被标记为绑定敏感,因为DEPTNO列上的直方图用于计算谓词“where deptno = :deptno”的选择性。直方图的存在表明这个列的数据分布是扭曲的,因此绑定变量的不同值可能会要求不同的执行计划。现在,让我们将绑定变量的值更改为10,这是DEPTNO列上出现最多的值,它出现了99900次,即99.9%的行。

SQL> exec :deptno := 10
SQL> select /*ACS_1*/ count(*), max(empno) 
     from emp
     where deptno = :deptno;

COUNT(*) MAX(EMPNO) 
---------- ----------  
     99900     100000

我们预计这次执行计划与以前相同,因为Oracle最初认为这个游标可以共享。让我们检查一下:

SQL> select * from table(dbms_xplan.display_cursor);

PLAN_TABLE_OUTPUT 
------------------------------------------------------------------------
SQL_ID  272gr4hapc9w1, child number 0 
------------------------------------------------------------------------ 
select /*ACS_1*/ count(*), max(empno) from emp where deptno = :deptno 
Plan hash value: 3184478295 
------------------------------------------------------------------------ 
| Id | Operation                    | Name  | Rows | Bytes |Cost (%CPU)| ------------------------------------------------------------------------
|  0 | SELECT STATEMENT             |       |      |       |    2 (100)|
|  1 |  SORT AGGREGATE              |       |     1|    16 |           | 
|  2 |   TABLE ACCESS BY INDEX ROWID| EMP   |     1|    16 |    2   (0)|
|  3 |    INDEX RANGE SCAN          | EMP_I1|     1|       |    1   (0)| ------------------------------------------------------------------------

该计划和以前一样仍然是索引范围扫描,但如果我们查看执行统计信息,我们应该会看到有两次执行,BUFFER_GET的数量从之前的53大幅跃升到1007。

SQL> select child_number, executions, buffer_gets, 
     is_bind_sensitive, is_bind_aware 
     from v$sql 
     where sql_text like 'select /*ACS_1%';

CHILD_NUMBER EXECUTIONS BUFFER_GETS IS_BIND_SENSITIVE IS_BIND_AWARE
------------ ---------- ----------- ----------------- -------------
0            2          1007        Y                 N

我们还应该注意到,游标仍然只标记为绑定敏感(IS_BIND_SENSITIVE),此时还是非绑定感知(IS_BIND_AWARE)。因此,让我们使用相同的值10重新执行该语句。

SQL> exec :deptno := 10
SQL> select /*ACS_1*/ count(*), max(empno)
     from emp
     where deptno = :deptno;

COUNT(*)    MAX(EMPNO)
---------- -----------
     99900      100000

Oracle一直在幕后监控这两次SQL执行的统计信息,发现不同的绑定值导致执行时查询的数据量显著不同。基于这种差异,Oracle会自动调整其行为,因此不总是为这个SQL共享相同的计划。因此,根据当前的绑定值10会生成一个新计划。让我们看看新计划:

SQL> select * from table(dbms_xplan.display_cursor);

PLAN_TABLE_OUTPUT 
--------------------------------------------------------------------
SQL_ID  272gr4hapc9w1, child number 1
--------------------------------------------------------------------
select /*ACS_1*/ count(*), max(empno) from emp where deptno = :deptno
Plan hash value: 2083865914
--------------------------------------------------------------------
| Id  | Operation            | Name  | Rows   | Bytes | Cost (%CPU)| -------------------------------------------------------------------- 
|   0 | SELECT STATEMENT     |       |        |       |   240 (100)|
|   1 |  SORT AGGREGATE      |       |     1  |   16  |            | 
|*  2 |   TABLE ACCESS FULL  | EMP   | 95000  | 1484K |   240   (1)| --------------------------------------------------------------------

鉴于表中的值10的选择性很差,新计划采用的是全表扫描。现在,如果我们显示执行统计信息,我们看到已经创建了一个新的子游标(#1)。游标#1显示BUFFER_GET低于游标#0,并标记为绑定敏感(IS_BIND_SENSITIVE)和绑定感知(IS_BIND_AWARE)。绑定感知游标可能会对不同的绑定值使用不同的执行计划,具体取决于包含绑定变量的谓词的选择度。查看执行统计信息:

SQL> select child_number, executions, buffer_gets, 
     is_bind_sensitive, is_bind_aware 
     from v$sql 
     where sql_text like 'select /*ACS_1%';

CHILD_NUMBER EXECUTIONS BUFFER_GETS IS_BIND_SENSITIVE IS_BIND_AWARE
------------ ---------- ----------- ----------------- -------------
           0          2        1007 Y                 N
           1          1         821 Y                 Y  

我们看到一个新的游标,它代表使用全表扫描的计划。但是,如果我们再次使用选择性高的绑定值执行SQL,执行计划应该访问索引:

SQL> exec :deptno := 9
SQL> select /*ACS_1*/ count(*), max(empno)
from emp
where deptno = :deptno;

COUNT(*)   MAX(EMPNO) 
---------- ---------- 
   10         99

SQL> select * from table(dbms_xplan.display_cursor);
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------
SQL_ID  272gr4hapc9w1, child number 2
------------------------------------------------------------------------
select /*ACS_1*/ count(*), max(empno) from emp where deptno = :deptno
Plan hash value: 3184478295 
------------------------------------------------------------------------
| Id | Operation                    | Name  | Rows | Bytes |Cost (%CPU)| 
------------------------------------------------------------------------
|  0 | SELECT STATEMENT             |       |      |       |    2 (100)| 
|  1 |  SORT AGGREGATE              |       |     1|    16 |           |
|  2 |   TABLE ACCESS BY INDEX ROWID| EMP   |     1|    16 |    2   (0)|
|  3 |    INDEX RANGE SCAN          | EMP_I1|     1|       |    1   (0)|
------------------------------------------------------------------------

优化器根据当前绑定值的选择性,选择了适当的计划。关于这一点,还有最后一件有趣的事情需要注意。如果我们再看一遍执行统计信息,现在有三个游标:

SQL> select child_number, executions, buffer_gets, 
     is_bind_sensitive, is_bind_aware 
     from v$sql 
     where sql_text like 'select /*ACS_1%';
CHILD_NUMBER EXECUTIONS BUFFER_GETS IS_B_SENS IS_B_AWAR IS_SHAR
------------ ---------- ----------- --------- --------- ---------- 
0            2          957         Y         N         N           
1            1          765         Y         Y         Y           
2            2          6           Y         Y         Y 

当游标切换到绑定感知模式时,原来的游标被丢弃,它被标记为不可共享(is_shareable是“N”),这意味着该游标将会从游标缓存中淘汰,它将不再被使用。换句话说,它只是在等待垃圾收集。还有一个原因会造成在11g中产生额外游标,当使用新的绑定值时,优化器会根据绑定值选择度的相似性,试图找到它认为合适的游标。如果它找不到这样的游标,它将创建一个新的游标(就像前面的例子中,当为选择性低的“10”创建一个(#1),为选择性高的“9”创建另外一个(#2))。如果新游标的计划与现有游标之一相同,则两个游标将被合并,以节省游标缓存中的空间。这将导致一个处于不可共享状态的游标被抛弃,如果游标缓存空间紧张,此游标将会首先从缓存中删除,并且不会用于未来的执行。

我将在这里汇总回答大家的问题,而不是逐一回答大家评论中的问题。

问:这种行为是否由11g优化器自动管理,我们不再需要cursor_sharing了?
答:出于向后兼容性的目的,我们尚未更改cursor_sharing参数的行为。因此,如果您将这个参数设置为 similar,自适应游标共享只会在字面量被替换为绑定变量的SQL中作用。我们希望将来来,此功能将说服人们将cursor_sharing设置为force。

问:搜索合适的子游标会有性能的影响吗,比如长时间拿着库缓存栓。
答:匹配游标的任何其他开销总会引发大家对性能的担心,我们努力将影响降至最低。当然,代码路径会增加一些,以匹配绑定感知游标,因为它需要更智能的检查。但这个功能不应影响尚未标记为绑定感知的游标。

问:是什么触发游标被标记为“绑定感知”?
答:我们的目标是考虑许多类型的谓词,当绑定值发生变化时,选择性可能会发生变化。

问:听起来优化器正在根据返回的行数来决定是否生成一个新计划…
答:我不会说明如何决定标记游标绑定感知的细节,处理的行数只是其中一个输入。

托业890的Oracle ACE为您翻译国外大佬的雄文

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
oracle+proc文.pdf 1 前言 .................................................................................................................................................1 1.1 读者范围 .................................................................................................................................1 1.2 内容组织 .................................................................................................................................1 1.3 约定 .........................................................................................................................................1 2 新特性介绍......................................................................................................................................2 2.1 ORACIE9I RELEASE 2 (9.2) 新特性..........................................................................................2 2.2 ORACLE9I RELEASE 1 (9.0.1) 新特性......................................................................................2 2.3 ORACLE 8I RELEASEE 8.1.5 新特性.........................................................................................3 2.4 ORACLE 8I RELEASE 8.1.4 新特性...........................................................................................3 2.5 ORACLE 8I RELEASE 8.1.3 新特性...........................................................................................3 3 概述 .................................................................................................................................................3 3.1 什么是ORACLE预编译程序? ................................................................................................3 3.2 为什么使用ORACLE预编译程序? ........................................................................................4 3.3 为什么使用SQL?..................................................................................................................4 3.4 为什么使用PL/SQL? ............................................................................................................5 3.5 PRO*C预编译的优点..............................................................................................................5 3.6 常见的问题.............................................................................................................................6 3.6.1 我怎么编译链接应用程序? .........................................................................................6 3.6.2 什么是varchar?...............................................................................................................8 3.6.3 在什么情况下不使用Pro*C/C++和SQLLIB库函数?...............................................8 3.6.4 能在Pro*C/C++程序调用存储过程吗?..................................................................8 3.6.5 我能在 SQL 语句的任意位置使用绑定变量(也可理解为用户自定义变量或输入 宿主变量)吗?..............................................................................................................................8 3.6.6 对Pro*C/C++字符类型变量的困惑?..........................................................................9 3.6.7 关于字符串指针变量的应用有特殊需要注意的么? .................................................9 3.6.8 为什么SPOOL不能用在Pro*C程序? ........................................................................9 3.6.9 Pro*C/C++支持结构作为宿主变量么?..........................................................................9 3.6.10 可以在递归函数嵌入SQL么? ..................................................................................10 3.6.11 我可以在任意版本的Oracle使用任意版本的预编译器么?.................................10 3.6.12 1405 错误(Fetch column values is null)可避免么?...............................................10 4 预编译介绍....................................................................................................................................10 4.1 嵌入式SQL编程概念介绍....................................................................................................10 4.1.1 可嵌入Pro*C/C++的SQL语句....................................................................................10 4.1.2 嵌入SQL语句的语法格式............................................................................................ 11 4.1.3 静态和动态SQL语句....................................................................................................12 4.1.4 嵌入的PL/SQL语句块..................................................................................................12 4.1.5 宿主变量和指示变量...................................................................................................12 4.1.6 Oracle数据类型................................................................................................................13 4.1.7 数组 ...............................................................................................................................13 4.1.8 数据类型转换...............................................................................................................13 4.1.9 私有SQL工作区、游标和记录集................................................................................13
关键字 ORACEL SQL Performance tuning 出处 http://www.dbasupport.com 1. 选用适合的ORACLE优化器 ORACLE的优化器共有3种: a. RULE (基于规则) b. COST (基于成本) c. CHOOSE (选择性) 设置缺省的优化器,可以通过对init.ora文件OPTIMIZER_MODE参数的各种声明,如RULE,COST,CHOOSE,ALL_ROWS,FIRST_ROWS . 你当然也在SQL句级或是话(session)级对其进行覆盖. 为了使用基于成本的优化器(CBO, Cost-Based Optimizer) , 你必须经常运行analyze 命令,以增加数据库的对象统计信息(object statistics)的准确性. 如果数据库的优化器模式设置为选择性(CHOOSE),那么实际的优化器模式将和是否运行过analyze命令有关. 如果table已经被analyze过, 优化器模式将自动成为CBO , 反之,数据库将采用RULE形式的优化器. 在缺省情况下,ORACLE采用CHOOSE优化器, 为了避免那些不必要的全表扫描(full table scan) , 你必须尽量避免使用CHOOSE优化器,而直接采用基于规则或者基于成本的优化器. 2. 访问Table的方式 ORACLE 采用两种访问表记录的方式: a. 全表扫描 全表扫描就是顺序地访问表每条记录. ORACLE采用一次读入多个数据块(database block)的方式优化全表扫描. b. 通过ROWID访问表 你可以采用基于ROWID的访问方式情况,提高访问表的效率, , ROWID包含了表记录的物理位置信息..ORACLE采用索引(INDEX)实现了数据和存放数据的物理位置(ROWID)之间的联系. 通常索引提供了快速访问ROWID的方法,因此那些基于索引列的查询就可以得到性能上的提高. 3. 共享SQL语句 为了不重复解析相同的SQL语句,在第一次解析之后, ORACLESQL语句存放在内存.这块位于系统全局区域SGA(system global area)的共享池(shared buffer pool)的内存可以被所有的数据库用户共享. 因此,当你执行一个SQL语句(有时被称为一个游标)时,如果它 和之前的执行过的语句完全相同, ORACLE就能很快获得已经被解析的语句以及最好的 执行路径. ORACLE的这个功能大大地提高了SQL的执行性能并节省了内存的使用. 可惜的是ORACLE只对简单的表提供高速缓冲(cache buffering) ,这个功能并不适用于多表连接查询. 数据库管理员必须在init.ora为这个区域设置合适的参数,当这个内存区域越大,就可以保留更多的语句,当然被共享的可能性也就越大了. 当你向ORACLE 提交一个SQL语句,ORACLE首先在这块内存查找相同的语句. 这里需要注明的是,ORACLE对两者采取的是一种严格匹配,要达成共享,SQL语句必须 完全相同(包括空格,换行等). 共享的语句必须满足三个条件: A. 字符级的比较: 当前被执行的语句和共享池的语句必须完全相同. 例如: SELECT * FROM EMP; 和下列每一个都不同 SELECT * from EMP; Select * From Emp; SELECT * FROM EMP; B. 两个语句所指的对象必须完全相同: 例如: 用户 对象名 如何访问 Jack sal_limit private synonym Work_city public synonym Plant_detail public synonym Jill sal_limit private synonym Work_city public synonym Plant_detail table owner 考虑一下下列SQL语句能否在这两个用户之间共享. SQL 能否共享 原因 select max(sal_cap) from sal_limit; 不能 每个用户都有一个private synonym - sal_limit , 它们是不同的对象 select count(*0 from work_city where sdesc like 'NEW%'; 能 两个用户访问相同的对象public synonym - work_city select a.sdesc,b.location from work_city a , plant_detail b where a.city_id = b.city_id 不能 用户jack 通过private synonym访问plant_detail 而jill 是表的所有者,对象不同. C. 两个SQL语句必须使用相同的名字的绑定变量(bind variables) 例如: 第一组的两个SQL语句是相同的(可以共享),而第二组的两个语句是不同的(即使在运行时,赋于不同的绑定变量相同的值) a. select pin , name from people where pin = :blk1.pin; select pin , name from people where pin = :blk1.pin; b. select pin , name from people where pin = :blk1.ot_ind; select pin , name from people where pin = :blk1.ov_ind;
Oracle 从入门到精通》的视频教程和PPT资料。 Oracle 11G从入门到精通视频的PPT 第1章-Oracle 11g数据库简介 认识Oracle 11g 回忆Oracle的产品版本 学习Oracle 11g的新特性 第2章-Oracle 11g的安装与测试 能够使用Oracle 11g的基本条件 在Windows 2003上安装Oracle 11g 移除Oracle 11g 第3章-熟悉数据库 什么是数据库 范式,设计关系型数据库的准则 绘制E-R图设计数据库 第4章-SQL基础 SQL-数据库沟通的语言标准 Oracle 11g支持的数据类型 数据库定义语言(DDL) 约束的使用 数据操纵语言(DML) 第5章-利用SELECT检索数据 SQL-数据库沟通的语言标准 Oracle 11g支持的数据类型 数据库定义语言(DDL) 约束的使用 数据操纵语言(DML) 第6章-Oracle内置函数 数值型函数 字符型函数 日期型函数 转换函数 NULL函数 集合函数 其他函数 第7章-PLSQL基础 PL/SQL是什么 PL/SQL 变量的使用 表达式 PL/SQL结构控制 PL/SQL使用DML和DDL语言 PL/SQL的异常 PL/SQL函数编写 第8章-游标,数据的缓存区 什么是游标 显示游标 隐式游标 第9章-视图,数据库虚拟的表 什么是视图 视图的创建 操作视图数据的限制 视图的修改 视图的删除 第10章-存储过程,提高程序执行的效率 什么是存储过程 在SQL*Plus创建存储过程 使用PL/SQL工具创建存储过程 修改删除存储过程 第11章-触发器,保证数据的正确性 什么是触发器 SQL*PLUS操作触发器 使用PL/SQL工具操作触发器 删除修改触发器 第12章-事务和锁,确保数据安全 什么是事务 什么是锁 第13章-使用Oracle 11g Enterprise Manager 什么是Oracle 11g Enterprise Manager 使用OEM管理Oracle 第14章-常用工具介绍 什么是SQL*Plus 使用SQL*Plus 使用PL/SQL Developer 第15章-控制文件和日志文件 控制文件与日志文件 初识控制文件 控制文件的多路复用 创建控制文件 日志文件的管理 第16章-表空间的管理 与表空间有关的概念 表空间的管理 临时表空间的管理 数据文件管理 第17章-与数据库安全性有关的对象 用户管理 权限管理 角色管理 概要文件PROFILE 第18章-备份与恢复 数据库备份与恢复 物理备份和恢复数据库 逻辑备份和恢复数据库 本章小结 第19章-使用RMAN工具 RMAN的概述 使用恢复目录 通道分配 备份集 使用RMAN恢复 本章小结 http://down.51cto.com/data/376692 第20章-在线考试系统数据库设计 在线考试系统需求 模块设计 本章小结 第21章-在.NET连接Oracle 什么是ADO.NET 使用绑定的方式连接Oracle 使用写代码的方式连接Oracle 第22章-在Java连接Oracle JDBC与ODBC简介 Thin方式连接Oracle JDBC-ODBC桥连接Oracle

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值