查看硬解析次数

TOM大叔的调优书中说过一句话,具体英文就忘了,大概意思就是:如果有人让我写本怎样让ORACLE性能最慢的书的话我会将取消绑定变量(bind variable)做为书的第一章和最后一章(他的意思是他很有幽默~~!),可见绑定变量的重要性。
绑定变量大多用在OLTP(online transaction process)中,在OLAP(online analizy process)中就没必要用BIND VARIABLE了。
要了解绑定变量的作用首先得了解下SHARED_POOL library cache中的SQL解析。
SQL 解析分为硬解析(hard parse)跟软解析(soft parse)。所谓硬解析就是真正的将产生的SQL用自己内部的算法完全解析一遍并将解析后的结果存入库缓存(library cache),而软解析就是发现库缓存中已经存在解析过的该SQL语句,从而直接利用,跳过重新解析的步骤。
所以SQL解析的步骤就是:先判断库缓存中是否存在该sql的解析,存在就不用再解析(软解析),没有就解析一遍(硬解析),并存入库缓存。
下面做个实验:

SQL> select * from v$sysstat where statistic#=331;

STATISTIC# NAME                      CLASS      VALUE    STAT_ID
---------- -------------------- ---------- ---------- ----------
       331 parse count (hard)           64        276 143509059

SQL> select * from v$sysstat where statistic#=331;

STATISTIC# NAME                      CLASS      VALUE    STAT_ID
---------- -------------------- ---------- ---------- ----------
       331 parse count (hard)           64        276 143509059
--这里value记录的就是硬解析次数,上面这个select语句同样第一次会被解析,之后就软解析了。

SQL> select * from v$sysstat where statistic#=331;

STATISTIC# NAME                      CLASS      VALUE    STAT_ID
---------- -------------------- ---------- ---------- ----------
       331 parse count (hard)           64        281 143509059
-- 过会再执行此句,可以看到这里VALUE变成281了,有人可能会有疑问,不是276的吗,怎么变了?原因是库内部平时也在做各种操作,比如状态收集等, 可能会产生硬解析,而且执行一段时间之后,ORACLE会根据最近最少使用原则将此SQL解析踢出共享池,再执行就要重新解析一遍了。

--为了尽量不影响我们的分析,将库以只读启动:
SQL> shutdown immediate
数据库已经关闭。
已经卸载数据库。
ORACLE 例程已经关闭。
SQL> startup mount
ORACLE 例程已经启动。

Total System Global Area 167772160 bytes
Fixed Size                  1247900 bytes
Variable Size              62915940 bytes
Database Buffers          100663296 bytes
Redo Buffers                2945024 bytes
数据库装载完毕。
SQL> alter database open read only
2 ;

数据库已更改。

SQL> col name for a20
SQL> select * from v$sysstat where statistic#=331;

STATISTIC# NAME                      CLASS      VALUE    STAT_ID
---------- -------------------- ---------- ---------- ----------
       331 parse count (hard)           64        188 143509059

SQL> select * from v$sysstat where statistic#=331;

STATISTIC# NAME                      CLASS      VALUE    STAT_ID
---------- -------------------- ---------- ---------- ----------
       331 parse count (hard)           64        188 143509059

SQL> select * from t where id=2009;

        ID
----------
      2009

SQL> select * from v$sysstat where statistic#=331;

STATISTIC# NAME                      CLASS      VALUE    STAT_ID
---------- -------------------- ---------- ---------- ----------
       331 parse count (hard)           64        190 143509059
--解析id=2009的查询
SQL> select * from t where id=2009;

        ID
----------
      2009

SQL> select * from v$sysstat where statistic#=331;

STATISTIC# NAME                      CLASS      VALUE    STAT_ID
---------- -------------------- ---------- ---------- ----------
       331 parse count (hard)           64        190 143509059
--此前解析过,所以再此查询2009时VALUE不变,软解析。
SQL> select * from t where id= 2009;

        ID
----------
      2009

SQL> select * from v$sysstat where statistic#=331;

STATISTIC# NAME                      CLASS      VALUE    STAT_ID
---------- -------------------- ---------- ---------- ----------
       331 parse count (hard)           64        191 143509059
--此处注意,2009前面有个空格,也会被判断为跟原来的SQL不匹配。
SQL> select * from t where Id=2009;

        ID
----------
      2009

SQL> select * from v$sysstat where statistic#=331;

STATISTIC# NAME                      CLASS      VALUE    STAT_ID
---------- -------------------- ---------- ---------- ----------
       331 parse count (hard)           64        192 143509059
--此处的Id的I是大写,也会跟原来的查询不匹配,所以硬解析。
SQL> select * from t where id=2010;

        ID
----------
      2010

SQL> select * from v$sysstat where statistic#=331;

STATISTIC# NAME                      CLASS      VALUE    STAT_ID
---------- -------------------- ---------- ---------- ----------
       331 parse count (hard)           64        194 143509059
--2010跟2009的查询SQL解析不共享,所以也要硬解析。
SQL> show parameter cursor_sharing

NAME                                 TYPE                   VALUE
------------------------------------ ---------------------- ---------
cursor_sharing                       string                 EXACT
此外,cursor_sharing参数也会影响ORACLE判断SQL是否共享,此处用的是EXACT,也是10G默认的,11G中已不是。以后我会单独讨论下这个参数。

可以想象,如果对10W个不同的ID每个都查询一次,硬解析的次数是很大的,会导致性能的下降。而用绑定变量来代替ID的值,10W条数据的查询会被共享为一条SQL,只硬解析一次,带来的性能提升也是很明显的。
下面也对绑定变量做个实验,向T表插入10W条数据。可以看出用绑定变量跟不用的巨大差异。

SQL> set timing on
SQL> truncate table t;

Table truncated.

Elapsed: 00:00:00.04
--p1不用绑定变量
SQL> create or replace procedure p1
2 as
3 begin
4 for i in 1..100000 loop
5 execute immediate 'insert into t values('||i||')';
6 end loop;
7 end;
8 /

Procedure created.

Elapsed: 00:00:00.06
--p2用绑定变量
SQL> create or replace procedure p2
2 as
3 begin
4 for i in 1..100000 loop
5 execute immediate 'insert into t values(:a)' using i;
6 end loop;
7 end;
8 /

Procedure created.

Elapsed: 00:00:00.04
SQL> select * from v$sysstat where statistic#=331;

STATISTIC# NAME                      CLASS      VALUE    STAT_ID
---------- -------------------- ---------- ---------- ----------
       331 parse count (hard)           64     143800 143509059

1 row selected.

Elapsed: 00:00:00.01
SQL> exec p1;

PL/SQL procedure successfully completed.

Elapsed: 00:01:10.01
SQL> select * from v$sysstat where statistic#=331;

STATISTIC# NAME                      CLASS      VALUE    STAT_ID
---------- -------------------- ---------- ---------- ----------
       331 parse count (hard)           64     243800 143509059

1 row selected.

Elapsed: 00:00:00.01
SQL> truncate table t;

Table truncated.

Elapsed: 00:00:00.12
SQL> select * from v$sysstat where statistic#=331;

STATISTIC# NAME                      CLASS      VALUE    STAT_ID
---------- -------------------- ---------- ---------- ----------
       331 parse count (hard)           64     243801 143509059

1 row selected.

Elapsed: 00:00:00.03
SQL> exec p2;

PL/SQL procedure successfully completed.

Elapsed: 00:00:14.67
SQL> select * from v$sysstat where statistic#=331;

STATISTIC# NAME                      CLASS      VALUE    STAT_ID
---------- -------------------- ---------- ---------- ----------
       331 parse count (hard)           64     243803 143509059

1 row selected.

Elapsed: 00:00:00.00

可见差异之大。。
做 实验的时候发现过程p2中用insert into t values(i)代替execute immediate 'insert into t values(:a)' using i的话,消耗的时间是相近的,也就是说前者也会被替换成使用绑定变量的方式。感兴趣的朋友可以试一下。不足的地方欢迎指正~

转自:http://hi.baidu.com/kywinder/item/8dc7972656a6019db7326341

猜你喜欢

转载自fred-han.iteye.com/blog/1949370