http://www.oraclefans.cn/forum/showtopic.jsp?rootid=5467&CPages=1

http://blog.csdn.net/tianlesoftware/article/details/5544307

使用绑定变量可以减少SQL PARSE,但是使用绑定变量有一个不好的地方,就是对于访问具有倾斜的列,可能使用错误的执行计划。在Oracle 9i之前,如果WHERE 条件里面全部使用绑定变量,那么只能使用固定的选择性参数来确定执行计划。

=操作和>=操作的选择性为5%,范围扫描的选择性为25%。缺省值的方式可能生成不好的执行计划。所以Oracle 9i就出现了一个新的技术,bind peeking。什么是bind peeking呢?当SQL第一次执行的时候,优化器会根据绑定变量来确定执行计划(如果存在柱状图)。BIND PEEKING只有当该SQL第一次执行的时候,进行HARD PARSE的时候才进行,第二次调用该SQL,就不会再次进行BIND PEEKING。这种情况下,就存在另外一个风险,如果某个列的倾斜性很厉害,那么使用BIND PEEKING就是不安全的,因为不同的参数代入,只能走第一次执行时的执行计划,那么执行计划就像掷色子一样,要靠运气了。碰到这种情况,应用就不应该使用绑定变量,而应该改为直接值了。

下面是一个实验:

1、创建一个表create table tt2 as select *  from dba_objects; 也可以再执行几次,把记录数搞大一些

2、update tt2 set status='INVALID' where rownum<100;

3、update tt2 set status='IND' where status='VALID' and rownum<3;

4、经过上述操作,就会产生几万valid,89条 invalid和2条ind的记录。下面进行分析

5、create index ind_tt2_status on tt2 (status);

SQL> EXEC DBMS_STATS.GATHER_TABLE_STATS('SCOTT','TT2',method_opt => 'for all indexed columns  size skewonly');

PL/SQL 过程已成功完成。

SQL> select table_name,COLUMN_NAME,endpoint_number,ENDPOINT_VALUE,ENDPOINT_ACTUAL_VALUE from user_tab_histograms where table_name='T
T2' ORDER BY COLUMN_nAME;

TABLE_NAME                     COLUMN_NAME                    ENDPOINT_NUMBER ENDPOINT_VALUE ENDPOINT_ACTUAL_VALU
------------------------------ ------------------------------ --------------- -------------- --------------------
TT2                            STATUS                                      21     3.8063E+35
TT2                            STATUS                                   29819     4.4786E+35
TT2                            STATUS                                       2     3.8063E+35

6、执行:

var a varchar2(10);

exec :a:='VALID';

select object_id,status from tt2  where status=:a;

7、查看执行计划(这个时候最好不要用AUTOTRACE,由于BUG,AUTOTRACE显示的执行计划是错误的),可以通过v$sql_plan,或者干脆用sql_trace

查看sql_trace的时候,如果看裸文件,那么parse #1 。。。mis=1说明是硬解析,否则就是软解析

我们使用v$_sql_plan来看:

Plan Table
--------------------------------------------------------------------------------------------------------------------------------
--------
| Operation | Name | Starts | E-Rows | A-Rows | Buffers | Reads | Writes | E-Time |
------------------------------------------------------------------------------------------------------------------------
|  TABLE ACCESS FULL              |TT2                 |      0 |    123K|    123K|       0 |      0 |      0 |      0 |
| SELECT STATEMENT                |                    |      0 |        |    123K|       0 |      0 |      0 |      0 |
------------------------------------------------------------------------------------------------------------------------

8、然后在执行:

exec :a:='INVALID';

select object_id,status from tt2  where status=:a;

查看执行计划和上面的相同

Plan Table
--------------------------------------------------------------------------------------------------------------------------------
--------
| Operation | Name | Starts | E-Rows | A-Rows | Buffers | Reads | Writes | E-Time |
------------------------------------------------------------------------------------------------------------------------
|  TABLE ACCESS FULL              |TT2                 |      0 |    123K|    123K|       0 |      0 |      0 |      0 |
| SELECT STATEMENT                |                    |      0 |        |    123K|       0 |      0 |      0 |      0 |
------------------------------------------------------------------------------------------------------------------------
如果生成了TRACE文件,可以看到:

SELECT OBJECT_NAME,STATUS 
FROM
 TT2 WHERE STATUS=:A

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        2      0.00       0.01          0          0          0           0
Execute      2      0.00       0.00          0          0          0           0
Fetch     8213      0.56       0.59          0      18352          0      123154
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total     8217      0.56       0.60          0      18352          0      123154

Misses in library cache during parse: 1
Optimizer goal: CHOOSE
Parsing user id: 59

Rows     Row Source Operation
-------  ---------------------------------------------------
  4      TABLE ACCESS FULL TT2

从这里可以看出执行了2次parse,其中misses in library cache during parse:1,说明其中一次是硬分析(从裸文件也可以看出mis=1的有一次)

9、刷新一下共享池alter system flush shared_pool;

10、然后再执行

exec :a:='INVALID';

select object_id,status from tt2  where status=:a;

看到的执行计划如下:

Plan Table
-------------------------------------------------------------------------------------------------------------------------------
--------
| Operation | Name | Starts | E-Rows | A-Rows | Buffers | Reads | Writes | E-Time |
------------------------------------------------------------------------------------------------------------------------
|   INDEX RANGE SCAN              |IDX_TT2_STATUS      |      0 |    178 |    123K|       0 |      0 |      0 |      0 |
|  TABLE ACCESS BY INDEX ROWID    |TT2                 |      0 |    178 |    123K|       0 |      0 |      0 |      0 |
| SELECT STATEMENT                |                    |      0 |        |    123K|       0 |      0 |      0 |      0 |
------------------------------------------------------------------------------------------------------------------------

11、再执行:

exec :a:='VALID';

select object_id,status from tt2  where status=:a;

看到的执行计划还是和上面的相同,走索引范围扫描。

12、这就是bind peeking的含义,使用绑定变量的时候,能够共享SQL,其执行计划是在第一次parse的时候做peeking生成的。根据当前的绑定变量的值。今后再次执行,就认为SQL是安全的,不再进行peeking,也不会生成新的执行计划

13、如果删除柱状图,那么还是否会产生PEEKING呢?

SQL> exec dbms_stats.delete_column_stats(ownname=>'SCOTT',TABNAME=>'TT2',COLNAME=>'STATUS');

PL/SQL 过程已成功完成。

SQL> exec :a:='VALID';

PL/SQL 过程已成功完成。

SQL> select object_id,status from tt2 where status=:a;

已选择123150行。

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=1233 Bytes=22
          194)

1    0   TABLE ACCESS (BY INDEX ROWID) OF 'TT2' (Cost=2 Card=1233 B
          ytes=22194)

2    1     INDEX (RANGE SCAN) OF 'IDX_TT2_STATUS' (NON-UNIQUE) (Cos
          t=1 Card=493)

Statistics
----------------------------------------------------------
        303  recursive calls
          0  db block gets
      18388  consistent gets
       1955  physical reads
          0  redo size
    2185342  bytes sent via SQL*Net to client
      90802  bytes received via SQL*Net from client
       8211  SQL*Net roundtrips to/from client
          6  sorts (memory)
          0  sorts (disk)
     123150  rows processed

Plan Table
---------------------------------------------------------------------------------------------------------------------------
--------
| Operation | Name | Starts | E-Rows | A-Rows | Buffers | Reads | Writes | E-Time |
------------------------------------------------------------------------------------------------------------------------
|   INDEX RANGE SCAN              |IDX_TT2_STATUS      |        |    493 |        |         |        |        |        |
|  TABLE ACCESS BY INDEX ROWID    |TT2                 |        |      1K|        |         |        |        |        |
| SELECT STATEMENT                |                    |        |        |        |         |        |        |        |
------------------------------------------------------------------------------------------------------------------------

在没有柱状图的情况下,优化器采用了缺省值,认为选择性是5%,毫不犹豫就走了索引。

14、以上测试是在CURSOR_SHARING=EXACT情况的,再来看看在CURSOR_SHARING=SIMILAR的情况下是什么样的?

SQL> exec :a:='VALID';

PL/SQL 过程已成功完成。

SQL> select object_id,status from tt2 where status=:a;

已选择123150行。

查看执行计划,发现是走全表扫描,再执行:

SQL> exec :a:='INVALID';

PL/SQL 过程已成功完成。

SQL> select object_id,status from tt2 where status=:a;

已选择123150行。

设置:a='INVALID';

执行还是全表扫描,说明第二次执行的时候BIND PEEKING没有出现。而第一次执行出现了bind peeking,再次确认:

Plan Table
------------------------------------------------------------------------------------------------------------------------------
--------
| Operation | Name | Starts | E-Rows | A-Rows | Buffers | Reads | Writes | E-Time |
------------------------------------------------------------------------------------------------------------------------
|   INDEX RANGE SCAN              |IDX_TT2_STATUS      |        |    178 |        |         |        |        |        |
|  TABLE ACCESS BY INDEX ROWID    |TT2                 |        |    178 |        |         |        |        |        |
| SELECT STATEMENT                |                    |        |        |        |         |        |        |        |
------------------------------------------------------------------------------------------------------------------------
走了索引,确认了第一次执行是进行了bind peeking的。

15、下面测试cursor_sharing=force的情况

SQL> alter session set cursor_sharing=force;

会话已更改。

SQL> alter system flush shared_pool;

系统已更改。

SQL> exec :a:='INVALID';

PL/SQL 过程已成功完成。

SQL> select object_id,status from tt2 where status=:a;

已选择178行。

Plan Table
-----------------------------------------------------------------------------------------------------------------------------
--------
| Operation | Name | Starts | E-Rows | A-Rows | Buffers | Reads | Writes | E-Time |
------------------------------------------------------------------------------------------------------------------------
|   INDEX RANGE SCAN              |IDX_TT2_STATUS      |        |    178 |        |         |        |        |        |
|  TABLE ACCESS BY INDEX ROWID    |TT2                 |        |    178 |        |         |        |        |        |
| SELECT STATEMENT                |                    |        |        |        |         |        |        |        |
------------------------------------------------------------------------------------------------------------------------
第一次执行也是做了BIND PEEKING的,

16、结论:

  • BIND PEEKING对于使用绑定变量的情况下,选择较优的执行计划有一定的作用
  • BIND PEEKING只有在存在柱状图的情况下才能工作
  • BIND PEEKING只在做HARD PARSE的时候才产生,随后的执行如果不需要HARD PARSE就不会进行BIND PEEKING,这种情况和CURSOR_SHARING无关
  • 由于以上原因,使用绑定变量的时候可以有效的减少PARSE
  • 对于使用不同绑定变量执行计划应该不同的情况,建议不要使用绑定变量,否则可能会产生随机的执行计划(硬分析后的所有执行都使用第一个执行计划,执行计划和第一次执行的参数有关)

关于非绑定变量情况在使用cursor_sharing=force,similar情况下的bind peeking,请参考另外一篇文章   绑定变量的一旦心得

绑定变量和BIND PEEKING相关推荐

  1. Oracle 绑定变量 详解 .

    之前整理过一篇有关绑定变量的文章,不太详细,重新补充一下. Oracle 绑定变量 http://blog.csdn.net/tianlesoftware/archive/2009/10/17/467 ...

  2. sql like 绑定变量_码硬解析的改进方法之二使用绑定变量

    绑定变量要求变量名称,数据类型以及长度是一致,否则无法使用软解析 绑定变量(bind variable)是指在DML语句中使用一个占位符,即使用冒号后面紧跟变量名的形式,如下 select * fro ...

  3. 绑定变量窥测(Bind Variable Peeking)

    绑定变量窥测是oracle在进行硬解析生成执行计划的时候会窥探绑定变量的真实值,去评估绑定变量的谓词条件的选择率,影响执行计划是选择访问路径是先走索引扫描进而去访问表还是直接走全表扫描. 没有绑定变量 ...

  4. PLSQL_性能优化系列07_Oracle Parse Bind Variables解析绑定变量

    2014-09-25 Created By BaoXinjian 一.绑定变量用法和使用场合 使用绑定变量的重要性:如果不使用绑定变量而使用常量,会导致大量硬解析.由于硬解析的种种危害,不使用绑定变量 ...

  5. Oracle绑定变量分级(Bind Graduation)

    Oracle绑定变量分级(Bind Graduation) 绑定变量分级(Bind Graduation)是指Oracle在PL/SQL代码中会根据文本型绑定变量的定义长度而将这些文本型绑定变量分为四 ...

  6. linux获取bind返回值信息,v$sql_bind_capture 获取绑定变量信息

    截取自 v$sql_bind_capture       对于游标中定义的每一个绑定变量都会有视图中的一行对应.主要包含三个部分:指向父游标(hash_value, address)和子游标(hash ...

  7. Oracle面对“数据倾斜列使用绑定变量”场景的解决方案

    1. 背景知识介绍 我们知道,Oracle在传统的OLTP(在线事务处理)类系统中,强烈推荐使用绑定变量,这样可以有效的减少硬解析从而增加系统的并发处理能力.甚至在有些老旧系统,由于在开始开发阶段缺乏 ...

  8. 【实践】对于绑定变量看执行计划

    基础: 从Oracle9i,开始的Bind Peeking (http://www.oraclefans.cn/forum/showtopic.jsp?rootid=5467&CPages=1 ...

  9. Oracle --获取绑定变量的值.

    SELECT * FROM DBA_HIST_SQLBIND WHERE SNAP_ID>67073 AND SNAP_ID<=67079 AND SQL_ID='3DR3410F086P ...

最新文章

  1. Datawhale来杭电啦!
  2. QT的QMessageBox类的使用
  3. mybatis的拦截器及分页机制
  4. bash:yum:command not found 解决办法
  5. PowerDesigner概念模型与物理模型相互转换及导出数据字典
  6. 【最短路】Walls
  7. python常用代码入门-入门十大Python机器学习算法(附代码)
  8. FPGA厂家谁家强?
  9. eclipse中追加html模板,Eclipse创建自定义HTML5,JSP模板
  10. pyqt5在图元上画图_PyQt5的PyQtGraph实践系列1:添加图形到PyQt5布局
  11. 信息学奥赛一本通(C++版)在线评测系统 - 题解目录
  12. Java8中Map新方法:compute使用详解
  13. 【移动安全高级篇】————2、浅谈Android软件安全自动化审计
  14. runtime error解决方法
  15. matlab内置vmd吗,matlab中使用VMD(变分模态分解)
  16. 周杰伦理片,吴奇隆胸店,邓紫棋牌室,黄家驹留所,金秀贤鸭蛋,郭富城管员,苍井空调店,齐达内衣店,乔布斯袜店……能接下去么?...
  17. 几张图片演示PD(PowerDesigner)生成数据库脚本sql文件
  18. 造车 8 年,苹果“认输”了:放弃 L 5 级全自动驾驶,售价低于 70 万,将于 2026 年推出...
  19. VC++ 文件读写总结
  20. iOS 组件化-发布组件到远程仓库

热门文章

  1. 美国公司信息查询网站
  2. Scratch教程——按键的技巧
  3. 怎么从初、中级Java程序员过渡到高级Java程序员?
  4. Python标准库:内置函数property(fget=None, fset=None, fdel=None, doc=None)
  5. 动易SiteFactory CMS自动采集器 V2.5.1
  6. 令人智熄的智(zhi)能(zhang)蛇
  7. [c/c++] size_t是什么
  8. 前端跳转失败,例如返回按钮,js跳转失败的踩坑原因之一
  9. NFC源码分析之初始化流程
  10. 跨境电商一站式数字化管理ERP系统,帮你解决烦恼