Oracle-绑定变量binding variable解读
Oracle-Soft Parse/Hard Parse/Soft Soft Parse解读
绑定变量是OLTP系统中一个非常值得关注的技术点。良好的变量绑定会使OLTP系统数据库中的SQL执行的飞快,内存效率极高。 不绑定变量有可能会使OLTP数据库不堪负重,资源被SQL解析严重消耗,系统显得缓慢。
本博文的案例基于Oracle Database 11g Enterprise Edition Release 11.2.0.4.0
在介绍绑定变量之前,我们需要知道SQL究竟是如何被执行的?
当一个用户与数据库建立连接后,向数据库发送SQL语句,Oracle在接到这条SQL后,首先会将这个SQL做一个Hash函数运算,得到一个Hash值,然后到共享池中寻找是否有和这个hash值匹配的SQL存在。
如果找到了,Oracle会直接使用已经存在的SQL的执行计划去执行当前的SQL,然后将结果返回给用户。
如果没有找到,Oracle会认为这是一条新的SQL, 将会按照下面的顺序来执行:
1 .语法分析
SQL 是否符Oracle规定的语法,如果有语法错误,则向用户抛出错误信息
2. 语义分析
语法分析通过之后,Oracle会对这条SQL做一些对象、权限方面的检查,查看SQL中操作的表是否存在,表中的列是否正确,用户是否有权限操作这个对象的权限等
3 .生成执行计划
这个过程Oracle在经过一些列的操作之后,来做出SQL最后的执行计划,比如查看操作对象的统计信息,动态采样等等。
如何生成执行计划的详细信息,可以参考10053事件
4.执行SQL
Oracle按照上一步生成的执行计划,执行SQL,并将结果返回给用户。
以上的这些工作,我们通常称为硬分析(hard parse),其实是十分消耗系统资源的。 而对于相同Hash值的SQL已经存在于共享池中则称为软分析(soft parse)。
绑定变量就起本质而言就是说把本来需要Oracle做硬分析的SQL变成了软分析,以减少Oracle花费在SQL解析上的时间和资源。
下面我们来对下同一条SQL被执行10000次,绑定变量和非绑定变量在资源消耗上的情况
Connected to Oracle Database 11g Enterprise Edition Release 11.2.0.4.0
Connected as xx@xgj
##打开时间统计
SQL> set timing on;
##为防止干扰信息,先手动的清空共享池中的信息(若生产环境慎重)
SQL> alter system flush shared_pool;
System altered
Executed in 0.078 seconds
##建表
SQL> create table t as select * from dba_objects;
Table created
Executed in 0.281 seconds
##设置trace文件标识
SQL> alter session set tracefile_identifier='xgj_var_bind';
Session altered
Executed in 0 seconds
##打开SQL_TRACE
SQL> alter session set sql_trace=true;
Session altered
Executed in 0.015 seconds
##执行SQL块
SQL> begin
2 for i in 1 .. 10000 loop
3 execute immediate 'select * from t where t.object_id = :i' using i ;
4 end loop;
5 end;
6 /
PL/SQL procedure successfully completed
Executed in 0.578 seconds
##关闭SQL_TRACE
SQL> alter session set sql_trace=false ;
Session altered
Executed in 0 seconds
SQL>
在Oracle服务器端获取到trace文件后,使用tkprof进行分析汇总,查看
oracle@entel1:[/oracle/diag/rdbms/cc/cc/trace]$ls *xgj_var_bind*
cc_ora_32363_xgj_var_bind.trc cc_ora_32363_xgj_var_bind.trm
oracle@entel1:[/oracle/diag/rdbms/cc/cc/trace]$tkprof cc_ora_32363_xgj_var_bind.trc xgj_var_bind.txt sys=no
TKPROF: Release 11.2.0.4.0 - Development on Sat Dec 17 21:13:11 2016
Copyright (c) 1982, 2011, Oracle and/or its affiliates. All rights reserved.
我们截取分析汇总后的关键部分来看
整个过程加上产生的递归SQL,我们可以看到整个语句的:
ALL NON-RECURSIVE STATEMENTS + ALL RECURSIVE STATEMENTS
SQL> set timing on ;
SQL> alter session set tracefile_identifier='xgj_var_unbind';
Session altered
Executed in 0.016 seconds
SQL> alter session set sql_trace=true;
Session altered
Executed in 0.016 seconds
SQL> begin
2 for x in 1 .. 10000 loop
3 execute immediate 'select * from t where t.object_id ='||x;
4 end loop;
5 end;
6 /
PL/SQL procedure successfully completed
Executed in 16.672 seconds -----耗时很长
SQL>
执行tkprof分析汇总
查看xgj_var_unbind.txt关键部分
………… 中间省略一万万字
可以看到 每一条都是hard parse,非常消耗系统资源,耗时很长。
同样的我们统计下执行信息:
ALL NON-RECURSIVE STATEMENTS + ALL RECURSIVE STATEMENTS
通过对比我们可以发现,在OLTP系统中,使用绑定变量的SQL资源消耗要与那远少于未绑定变量SQL的资源消耗,SQL执行的次数越多,差距越明显。
未绑定变量SQL的资源主要消耗在产生的递归SQL上,这些SQL主要是对SQL语句做hard parse时使用的。
试想,当一个数据库有成千上万甚至更多的用户同时执行这样的SQL,而ORACLE只做一次硬分析,后面相同的SQL只执行SQL的执行操作,势必将大大减轻数据库的资源开销。
这就是绑定变量的由来,它并不神秘,不过是拿一个变量来代替谓词常量,让ORACLE每次对用户发送的SQL做hash运算,运算出相同的hash值,于是Oracle便将这些SQL看做同一个SQL对待而已。
如果你使用Oracle的图形化工具DBCA创建数据库,应该有印象,其中有一步是要求你选择数据库的类型是OLTP还是OLAP。 其实这就说明了OLTP和OLAP数据库是有很大的差异的,Oracle需要知道你选择的系统架构,以便于按照系统的架构对相应的参数值做设定,比如初始化参数。
OLTP和OLAP的请参考之前梳理的文章 Oracle-OLAP和OLTP解读
http://blog.csdn.net/yangshangwei/article/details/52949378
SQL> drop table t;
Table dropped
SQL> create table t as select a.OBJECT_ID ,a.OBJECT_NAME from dba_objects a ;
Table created
SQL> select count(1) from t;
COUNT(1)
----------
35234
SQL> 循环300次,写入更多的数据 每50次提交一次数据
SQL> begin
2 for i in 1 .. 300 loop
3 execute immediate 'insert into t select a.OBJECT_ID ,a.OBJECT_NAME from dba_objects a';
4 if mod(i,50)=0 then
5 commit;
6 end if;
7 end loop;
8 end;
9 /
PL/SQL procedure successfully completed
SQL> select count(1) from t;
COUNT(1)
----------
10605434
SQL> 在object_id字段上创建索引
SQL> create index idx_t on t(object_id);
Index created
SQL> alter session set tracefile_identifier='xgj_oltp';
Session altered
SQL> alter session set sql_trace =true;
Session altered
执行两遍SQL
##强制走全表扫描执行计划
SQL> select /*+ full(t)*/ * from t where object_id = 188;
OBJECT_ID OBJECT_NAME
---------- ---------------------
188 SQLLOG$_PKEY
...........
301 rows selected
##让Oracle自己选择执行计划
SQL> select * from t where t.object_id = 188;
OBJECT_ID OBJECT_NAME
---------- ---------------------
188 SQLLOG$_PKEY
...........
301 rows selected
SQL> alter session set sql_trace=false;
Session altered
可以看到 全表扫描执行计划的SQL扫描过的数据块明显大于使用索引执行的SQL计划。
从trace文件中可以看到,在fetch阶段,全表扫描读取了42093多个数据块,而走索引的,在fetch阶段,仅仅读取了308个数据块。
OLAP系统在SQL的操作中就复杂的多,OLAP数据库上大多数的时候运行是一些报表SQL,这些SQL经常会用到聚合查询(比如group by),而且结果集也是非常庞大,在这种情况下,索引并不是必然的选择,甚至有些时候全表扫描的性能会由于索引,即使相同的SQL,如果谓词条件不同,执行计划都可能不同
SQL> create table t2(object_id, object_name) partition by range (object_id)
2 (partition p1 values less than (5000),
3 partition p2 values less than (15000),
4 partition p3 values less than (25000),
5 partition p4 values less than (maxvalue))
6 as select object_id, object_name from dba_objects;
Table created
SQL>
SQL> begin
2 for x in 1 .. 300 loop
3 insert into t2 select object_id ,object_name from dba_objects ;
4 if mod(x,50)=0 then
5 commit;
6 end if;
7 end loop;
8 end;
9 /
PL/SQL procedure successfully completed
SQL> select count(1) from t2;
COUNT(1)
----------
10609046
SQL> select count(1) from t2 partition(p1);
COUNT(1)
----------
1504097
SQL> select count(1) from t2 partition(p2);
COUNT(1)
----------
2691241
SQL> select count(1) from t2 partition(p3);
COUNT(1)
----------
0
SQL> select count(1) from t2 partition(p4);
COUNT(1)
----------
6413708
SQL> 在分区表上建立本地索引
SQL> create index idx_t_id on t2(object_id) local;
Index created
##对表做一次分析(cascade => true ,索引也会被一同分析)
SQL> exec dbms_stats.gather_table_stats(user,'t2',cascade => true);
PL/SQL procedure successfully completed
我们使用如下命令
注释:以上的命令,是在plsql客户端执行的,可以支持, 但是autotrace命令,plsql并没有很好的支持,所以我登录到了服务器所在的主机执行的,当然也可以通过sqlplus客户端操作。
SQL> set autotrace traceonly explain ;
从结果中我们可以看到,虽然只是谓词的不同,但是oracle却选择了不同的执行计划,因为Oracle认为那样的计划代价最小。
谈到变量绑定,我们不得不提一下从Oracle9i开始引入的一个新的特性,叫做bind peaking ,顾名思义,就是在SQL语句硬解析的时候,Oracle会看一下当前SQL谓词的值,以便于生成最佳的执行计划。
需要强调的是,bind peaking 只发生在hard parse的时候,即SQL被第一次执行的时候,之后的变量将不会再做peeking.
bind peeking 并不能最终解决不同谓词导致不同执行计划的问题,它只能让SQL第一次执行的时候,执行计划更加准确,并不能帮助OLAP系统解决绑定变量导致执行计划选择错误的问题,所以,OLAP依然不应该使用绑定变量。
对于OLTP系统来讲,相同的SQL重复频率非常搞,如果优化器范湖解析SQL,势必极大地消耗资源。 另外OLTP系统用户请求的结果通常都是非常小,说基本上都会考虑使用索引。 bind peeking在第一次hard parse的时候获得了一个正确的执行计划,后面的SQL都按照这个计划来执行,可以极大地改善系统的性能,这是由OLTP系统的特性决定的。