从业界普遍接受绑定变量、硬解析和软解析概念以及对Oracle数据库性能的影响之后。开发和运维DBA就都在寻找一种方法,将系统中已经存在和即将存在的硬编码改为绑定变量方式,进而减少硬解析的出现。
Cursor_sharing参数在一个时期似乎是解决一切的万灵药。该参数的“作用域”仅限在那些没有使用过绑定变量的SQL语句。这一点很重要,因为如果在系统中使用了绑定变量,这个问题就不是我们需要考虑的了。
Cursor_sharing从参数语义上看,就是处理没有使用绑定变量时候,Oracle是否将where后面取值替换为绑定变量,便于进行SQL语句shared cursor共享。
参数cursor_sharing具有三个取值:exact、similar和force。EXACT是各Oracle版本参数的默认值,表示不开启这个功能。对没使用绑定变量的SQL语句,完全使用字面值共享机制。FORCE是一个极端,首先会将字面值进行替换,之后所有的执行语句均是采用相同的执行计划。详细解析请参见笔者《浅谈cursor_sharing》(http://space.itpub.net/17203031/viewspace-705196)
Similar是三个取值中较为复杂的一个。首先,同FORCE取值一样,对传入的SQL语句字面值进行改写,将where条件后的条件取值替换为绑定变量。但同时,SIMILAR并不是强令使用一个子游标满足所有的SQL语句,而是准备多条执行计划与之对应。
1、一条SQL需要几个执行计划对应?
当一个SQL的where条件取值被替换为绑定变量,如果按照CBO的原则,不同的绑定变量取值最优的执行计划是不同的。这里我们分为两种情况:
ü “Safe SQL”
如果SQL条件使用的是等号“=”条件,而且数据列分布比较平均,且Oracle存在多种执行计划生成可能的情况下,我们说单执行计划的确可以满足所有SQL等号取值条件。
实际中这种数据列比如主键列、唯一索引列和一些统计量反映的平均化。
ü “Unsafe SQL”
Unsafe SQL的情况要复杂的多。当我们的SQL对应条件列是一个数据偏移度很高的列,或者进行范围查询的时候,多个最优执行计划对应的情况是可能出现的。
对cursor_sharing参数取定SIMILAR的时候,就是可以对“Safe SQL”取定单一执行计划,而对“Unsafe SQL”取定多执行计划。
那么,Oracle怎么知道这个SQL是否是Safe呢?下面我们依据实例进行试验。
2、环境准备
我们选择在Oracle 10g下进行试验。
SQL> select * from v$version;
BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Prod
PL/SQL Release 10.2.0.1.0 - Production
CORE 10.2.0.1.0 Production
TNS for 32-bit Windows: Version 10.2.0.1.0 - Production
NLSRTL Version 10.2.0.1.0 – Production
--默认情况下,cursor_sharing参数取值为EXACT;
SQL> show parameter cursor_sharing
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
cursor_sharing string EXACT
SQL> select name, type , value from v$parameter where name='cursor_sharing';
NAME TYPE VALUE
-------------------- ---------- ----------
cursor_sharing 2 EXACT
此时,我们构建数据表,取值相同,但是具有不同的数据字典描述内容。从而验证是不是数据字典信息是SIMILAR判定Safe SQL的标准。
SQL> create table t as select object_id id1, object_id id2, object_id id3 from dba_objects;
Table created
SQL> alter table t add constraint pk_t_id1 primary key (id1);
Table altered
SQL> create unique index idx_t_id2 on t(id2);
Index created
SQL> exec dbms_stats.gather_table_stats(user,'T',cascade => true,method_opt => 'for all columns size auto');
PL/SQL procedure successfully completed
SQL> select column_name, num_distinct, NUM_BUCKETS, HISTOGRAM from dba_tab_col_statistics where wner='SYS' and table_name='T';
COLUMN_NAME NUM_DISTINCT NUM_BUCKETS HISTOGRAM
------------------------------ ------------ ----------- ---------------
ID1 51697 1 NONE
ID2 51697 1 NONE
ID3 53196 1 NONE
三个数据列取值完全相同,差异只在于数据字典的描述地位不同。同时,三个列均没有收集标识数据偏移度的直方图。
3、无直方图下的相关实验
我们首先进行三个数据列试验,判断SIMILAR取值的行为方式。
--调整cursor_sharing取值
SQL> alter session set cursor_sharing='SIMILAR';
Session altered
SQL> select name, type , value from v$parameter where name='cursor_sharing';
NAME TYPE VALUE
-------------------- ---------- --------------------
cursor_sharing 2 SIMILAR
首先对主键列进行选择使用。
--第一次调用SQL
SQL> select /*+ demo_id1 */ count(*) from t where id1=1000;
COUNT(*)
----------
1
--检查父子游标情况
SQL> select sql_text, sql_id, version_count from v$sqlarea where sql_text like 'select /*+ demo_id1 */%';
SQL_TEXT SQL_ID VERSION_COUNT
-------------------- ------------- -------------
select /*+ demo_id1 bvdkhydtduzb6 1
*/ count(*) from t w
here id1=:"SYS_B_0"
--生成一个父游标和一个子游标;
SQL> select sql_id, child_number from V$sql where sql_id='bvdkhydtduzb6';
SQL_ID CHILD_NUMBER
------------- ------------
bvdkhydtduzb6 0
注意,我们输入的SQL是一个条件硬编码的SQL。当cursor_sharing取值为SIMILAR之后,一个重要特性就是SQL被改写,原有条件列被占用符替换。这样就变化为一个使用绑定变量的SQL语句。
下面分别执行两个SQL,除了where条件中id1取值不同之外,字面值相同。
--取定2000取值;
SQL> select /*+ demo_id1 */ count(*) from t where id1=2000;
COUNT(*)
----------
1
--取定3000取值;
SQL> select /*+ demo_id1 */ count(*) from t where id1=3000;
COUNT(*)
----------
1
SQL> select sql_text, sql_id, version_count, executions from v$sqlarea where sql_text like 'select /*+ demo_id1 */%';
SQL_TEXT SQL_ID VERSION_COUNT EXECUTIONS
-------------------- ------------- ------------- ----------
select /*+ demo_id1 bvdkhydtduzb6 1 3
*/ count(*) from t w
here id1=:"SYS_B_0"
SQL> select sql_id, child_number, executions from V$sql where sql_id='bvdkhydtduzb6';
SQL_ID CHILD_NUMBER EXECUTIONS
------------- ------------ ----------
bvdkhydtduzb6 0 3
当我们对id1使用三个不同的取值时,使用的完全是相同的执行计划。SIMILAR的效果和FORCE取值完全相同。Id1列为主键列,取值绝对均匀,是否是由于主键列唯一的特性,让Oracle SIMILAR取值判定是“Safe SQL”?
我们进行下面的试验,对id2列进行。Id2列为一个唯一索引列,取值和id1列完全相同。看看此时的效果。
--id2
SQL> select /*+ demo_id2 */ count(*) from t where id2=1000;
COUNT(*)
----------
1
SQL> select sql_text, sql_id, version_count, executions from v$sqlarea where sql_text like 'select /*+ demo_id2 */%';
SQL_TEXT SQL_ID VERSION_COUNT EXECUTIONS
-------------------- ------------- ------------- ----------
select /*+ demo_id2 1wnrjz4j2dkqr 1 1
*/ count(*) from t w
here id2=:"SYS_B_0"
SQL> select sql_id, child_number, executions from V$sql where sql_id='1wnrjz4j2dkqr';
SQL_ID CHILD_NUMBER EXECUTIONS
------------- ------------ ----------
1wnrjz4j2dkqr 0 1
再次尝试2000和3000的id2取值。
SQL> select /*+ demo_id2 */ count(*) from t where id2=2000;
COUNT(*)
----------
1
SQL> select /*+ demo_id2 */ count(*) from t where id2=3000;
COUNT(*)
----------
1
SQL> select sql_text, sql_id, version_count, executions from v$sqlarea where sql_text like 'select /*+ demo_id2 */%';
SQL_TEXT SQL_ID VERSION_COUNT EXECUTIONS
-------------------- ------------- ------------- ----------
select /*+ demo_id2 1wnrjz4j2dkqr 1 3
*/ count(*) from t w
here id2=:"SYS_B_0"
SQL> select sql_id, child_number, executions from V$sql where sql_id='1wnrjz4j2dkqr';
SQL_ID CHILD_NUMBER EXECUTIONS
------------- ------------ ----------
1wnrjz4j2dkqr 0 3
注意,此时传入的SQL语句发生where条件取值替换为绑定变量的现象,但是同时都是针对其对应一个执行计划。难道说,Oracle能“看出”唯一索引的效果?
最后进行一般列id3的试验。
SQL> select /*+ demo_id3 */ count(*) from t where id3=1000;
COUNT(*)
----------
1
SQL> select sql_text, sql_id, version_count, executions from v$sqlarea where sql_text like 'select /*+ demo_id3 */%';
SQL_TEXT SQL_ID VERSION_COUNT EXECUTIONS
-------------------- ------------- ------------- ----------
select /*+ demo_id3 c0yvrw873f8gw 1 1
*/ count(*) from t w
here id3=:"SYS_B_0"
SQL> select /*+ demo_id3 */ count(*) from t where id3=2000;
COUNT(*)
----------
1
SQL> select /*+ demo_id3 */ count(*) from t where id3=3000;
COUNT(*)
----------
1
SQL> select sql_text, sql_id, version_count, executions from v$sqlarea where sql_text like 'select /*+ demo_id3 */%';
SQL_TEXT SQL_ID VERSION_COUNT EXECUTIONS
-------------------- ------------- ------------- ----------
select /*+ demo_id3 c0yvrw873f8gw 1 3
*/ count(*) from t w
here id3=:"SYS_B_0"
SQL> select sql_id, child_number, executions from V$sql where sql_id='c0yvrw873f8gw';
SQL_ID CHILD_NUMBER EXECUTIONS
------------- ------------ ----------
c0yvrw873f8gw 0 3
普通数据列id3也只对应一条执行计划。
从上面对id1-id3的试验情况看,cursor_sharing=SIMILAR下Oracle是会对SQL字面值中的条件进行绑定变量替换。但是判定“Safe SQL”的标准一定不是数据列描述信息。
那么,SIMILAR和FORCE在判定“Safe SQL”上的差异,是什么标准呢?我们下篇继续探讨。
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/17203031/viewspace-709321/ ,如需转载,请注明出处,否则将追究法律责任。