ITPub博客

首页 > Linux操作系统 > Linux操作系统 > Oracle11g中SQL Plan Management (三)

Oracle11g中SQL Plan Management (三)

原创 Linux操作系统 作者:realkid4 时间:2011-11-06 21:19:11 0 删除 编辑

 

上面我们介绍了如果从各种源source中,将SQL执行计划转换为SQL Plan Baseline。本篇中我们继续介绍Baseline的选择和进化。

 

4、SQL Plan Management Selection

 

在CBO环境下,一个SQL的执行计划过程如下:每次SQL语句被编译后,优化器首先是根据CBO原则生成一个CBO执行计划。接下来要检查Baseline存储库(在sysaux表空间里面),如果初始化参数optimizer_use_sql_plan_baseline设置为true(默认选项),在CBO执行计划执行之前,Oracle会检查一下SQL Plan Baseline存储中有没有匹配的执行计划语句。

 

 

SQL> show parameter use_sql_plan;

 

NAME                                 TYPE        VALUE

------------------------------------ ----------- ------------------------------

optimizer_use_sql_plan_baselines     boolean     TRUE

 

 

 

如果找到相同的执行计划,就直接执行该计划。如果没有与Baseline相同的执行计划,新生成的执行计划就会直接的加入到SQL History中,等待进一步确认为“Accepted”的Baseline。

 

如果产生的新的执行计划,Oracle SPM的作用开始体现出来:首先,Oracle不会选择新生成的执行计划作为真实执行计划,而且从Baseline中选择出一个已经“Accepted”的执行计划作为实际的执行计划进行替换。

 

注意:同样一条SQL语句,在Baseline中可能存在多条“Accepted”的执行计划。此时,Oracle会选择一条成本较低的执行计划替换掉本次的执行计划。但是,如果此前有数据字典内容的变化,如索引增加修改和删除操作,那么新生成的执行计划就会被执行。

 

从多条相同语句Baseline中,选择一条合适执行Baseline Plan的过程,我们称之为:“costing plan”。这种选择的过程,是消耗资源的。

 

下面我们进行简单的实验。

 

 

SQL> create table t as select * from dba_objects where wner='SCOTT';

Table created

 

SQL> create index idx_t_owner on t(owner);

Index created

 

SQL> exec dbms_stats.gather_table_stats(user,'T',cascade => true);

PL/SQL procedure successfully completed

 

SQL> select count(*) from t;

 

  COUNT(*)

----------

        15

 

 

我们构建了数据表T,此时虽然存在索引,但是以owner=’SCOTT’为条件时,还是会走索引路径。

 

--正式执行该语句;

SQL> select /* spm_demo */* from t where wner='SCOTT';

(篇幅原因,结果集合略……)

 

15 rows selected

 

 

SQL> select sql_id, sql_text, PLAN_HASH_VALUE, executions, child_number from v$sql where sql_text like 'select /* spm_demo */* from%';

 

SQL_ID        SQL_TEXT                       PLAN_HASH_VALUE EXECUTIONS CHILD_NUMBER

------------- ------------------------------ --------------- ---------- ------------

fmtuuwubyf8dq select /* spm_demo */* from t       1601196873          1            0

              where wner='SCOTT'                                      

 

SQL> select * from table(dbms_xplan.display_cursor('fmtuuwubyf8dq',0,'ALL'));

 

PLAN_TABLE_OUTPUT

--------------------------------------------------------------------------------

SQL_ID  fmtuuwubyf8dq, child number 0

-------------------------------------

select /* spm_demo */* from t where wner='SCOTT'

Plan hash value: 1601196873

--------------------------------------------------------------------------

| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |

--------------------------------------------------------------------------

|   0 | SELECT STATEMENT  |      |       |       |     2 (100)|          |

|*  1 |  TABLE ACCESS FULL| T    |    15 |  1230 |     2   (0)| 00:00:01 |

--------------------------------------------------------------------------

(篇幅原因,有省略)

 

35 rows selected

 

 

用SQL Baseline Capture的方法,将其Load如Baseline中。

 

 

SQL> var i number;

SQL> exec :i := dbms_spm.load_plans_from_cursor_cache(sql_id => 'fmtuuwubyf8dq' , plan_hash_value =>'1601196873');

 

PL/SQL procedure successfully completed

i

---------

1

 

SQL> select sql_handle, sql_text, plan_name, ENABLED, ACCEPTED, FIXED, EXECUTIONS from dba_sql_plan_baselines;

 

SQL_HANDLE                     SQL_TEXT                       PLAN_NAME                      ENABLED ACCEPTED FIXED EXECUTIONS

------------------------------ ------------------------------ ------------------------------ ------- -------- ----- ----------

SYS_SQL_244e0c2d91d5afb6       select /* spm_demo */* from t  SQL_PLAN_28mhc5q8xbbxq94ecae5c YES     YES      NO             1

                               where wner='SCOTT'                                                                 

 

 

注意:作为第一次加载到Baseline中的计划,被设置为“Accepted”状态,执行次数为1。

 

那么,我们接下来简单处理一下数据内容。

 

 

SQL> insert into t select * from dba_objects where owner != 'SCOTT';

72556 rows inserted

 

SQL> commit;

Commit complete

 

 

那么此时,数据执行计划应该是什么?

 

--重新收集统计量,反映分布变化;

SQL> exec dbms_stats.gather_table_stats(user,'T',cascade => true);

PL/SQL procedure successfully completed

 

SQL> explain plan for select * from t where wner='SCOTT';

Explained

 

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

PLAN_TABLE_OUTPUT

--------------------------------------------------------------------------------

Plan hash value: 1516787156

--------------------------------------------------------------------------------

| Id  | Operation                   | Name        | Rows  | Bytes | Cost (%CPU)|

--------------------------------------------------------------------------------

|   0 | SELECT STATEMENT            |             |    26 |  2522 |     2   (0)|

|   1 |  TABLE ACCESS BY INDEX ROWID| T           |    26 |  2522 |     2   (0)|

|*  2 |   INDEX RANGE SCAN          | IDX_T_OWNER |    26 |       |     1   (0)|

--------------------------------------------------------------------------------

Predicate Information (identified by operation id):

---------------------------------------------------

   2 - access("OWNER"='SCOTT')

 

14 rows selected

 

 

下面,执行该SQL。

 

 

SQL> select /* spm_demo */* from t where wner='SCOTT';

(结果集合省略……)

 

15 rows selected

 

SQL> select sql_handle,plan_name, ENABLED, ACCEPTED, FIXED, EXECUTIONS from dba_sql_plan_baselines;

 

SQL_HANDLE                     PLAN_NAME                      ENABLED ACCEPTED FIXED EXECUTIONS

------------------------------ ------------------------------ ------- -------- ----- ----------

SYS_SQL_244e0c2d91d5afb6       SQL_PLAN_28mhc5q8xbbxq7f85a438 YES     NO       NO             0

SYS_SQL_244e0c2d91d5afb6       SQL_PLAN_28mhc5q8xbbxq94ecae5c YES     YES      NO             1

 

 

注意,此时生成了一个新的SQL Plan History,但是对应的状态是非“ACCEPTED”,表明虽然新执行计划生成,但是尚未通过验证。执行的SQL执行计划应该是已经被确认的全表扫描。

 

我们首先看一下新生成的Baseline对应的是何种执行计划。

 

 

SQL> select * from table(dbms_xplan.display_sql_plan_baseline(sql_handle =>'SYS_SQL_244e0c2d91d5afb6' ,plan_name => 'SQL_PLAN_28mhc5q8xbbxq7f85a438'));

 

PLAN_TABLE_OUTPUT

--------------------------------------------------------------------------------

--------------------------------------------------------------------------------

SQL handle: SYS_SQL_244e0c2d91d5afb6

SQL text: select /* spm_demo */* from t where wner='SCOTT'

--------------------------------------------------------------------------------

--------------------------------------------------------------------------------

Plan name: SQL_PLAN_28mhc5q8xbbxq7f85a438         Plan id: 2139464760

Enabled: YES     Fixed: NO      Accepted: NO      Origin: AUTO-CAPTURE

--------------------------------------------------------------------------------

Plan hash value: 1516787156

--------------------------------------------------------------------------------

| Id  | Operation                   | Name        | Rows  | Bytes | Cost (%CPU)|

--------------------------------------------------------------------------------

|   0 | SELECT STATEMENT            |             |    26 |  2522 |     2   (0)|

|   1 |  TABLE ACCESS BY INDEX ROWID| T           |    26 |  2522 |     2   (0)|

|*  2 |   INDEX RANGE SCAN          | IDX_T_OWNER |    26 |       |     1   (0)|

--------------------------------------------------------------------------------

Predicate Information (identified by operation id):

---------------------------------------------------

   2 - access("OWNER"='SCOTT')

 

25 rows selected

 

 

说明:当前SQL语句执行,在CBO下,的确是生成了新的执行计划。但是由于是非“Accepted”计划,所以作为Plan History保存下来,等待确认。

 

 

Liberary Cache中情况如下,:

 

--父游标情况

SQL> select sql_id, sql_text, PLAN_HASH_VALUE, executions, version_count from v$sqlarea where sql_text like 'select /* spm_demo */* from%';

 

SQL_ID        SQL_TEXT                       PLAN_HASH_VALUE EXECUTIONS VERSION_COUNT

------------- ------------------------------ --------------- ---------- -------------

fmtuuwubyf8dq select /* spm_demo */* from t       1601196873          3             3

              where wner='SCOTT'                                      

 

 

--子游标情况

SQL> select sql_id, sql_text, PLAN_HASH_VALUE, executions, child_number from v$sql where sql_text like 'select /* spm_demo */* from%';

 

SQL_ID        SQL_TEXT                       PLAN_HASH_VALUE EXECUTIONS CHILD_NUMBER

------------- ------------------------------ --------------- ---------- ------------

fmtuuwubyf8dq select /* spm_demo */* from t       1601196873          1            0

              where wner='SCOTT'                                       

 

fmtuuwubyf8dq select /* spm_demo */* from t       1601196873          2            2

              where wner='SCOTT'                                      

 

 

两个执行计划分别为:

 

--获取到child_number=0的执行计划;

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

 

PLAN_TABLE_OUTPUT

--------------------------------------------------------------------------------

SQL_ID  fmtuuwubyf8dq, child number 0

-------------------------------------

select /* spm_demo */* from t where wner='SCOTT'

Plan hash value: 1601196873

--------------------------------------------------------------------------

| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |

--------------------------------------------------------------------------

|   0 | SELECT STATEMENT  |      |       |       |     2 (100)|          |

|*  1 |  TABLE ACCESS FULL| T    |    15 |  1230 |     2   (0)| 00:00:01 |

--------------------------------------------------------------------------

Predicate Information (identified by operation id):

---------------------------------------------------

   1 - filter("OWNER"='SCOTT')

 

18 rows selected

--获取到child_number=2的执行计划

SQL> select * from table(dbms_xplan.display_cursor('fmtuuwubyf8dq',2));

 

PLAN_TABLE_OUTPUT

--------------------------------------------------------------------------------

SQL_ID  fmtuuwubyf8dq, child number 2

-------------------------------------

select /* spm_demo */* from t where wner='SCOTT'

Plan hash value: 1601196873

--------------------------------------------------------------------------

| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |

--------------------------------------------------------------------------

|   0 | SELECT STATEMENT  |      |       |       |   283 (100)|          |

|*  1 |  TABLE ACCESS FULL| T    |    26 |  2522 |   283   (1)| 00:00:04 |

--------------------------------------------------------------------------

Predicate Information (identified by operation id):

---------------------------------------------------

   1 - filter("OWNER"='SCOTT')

Note

-----

   - SQL plan baseline SQL_PLAN_28mhc5q8xbbxq94ecae5c used for this statement

 

22 rows selected

 

 

上面的结果可以告诉我们几点内容:

 

首先,child_number=0的那个执行计划,是我们第一次执行SQL缓存在其中的。只有一次执行次数。这个是正常的。而child_number=2的执行计划,虽然都是走的FTS全表扫描,但是依据的是Baseline生成的执行计划,而且那个Baseline就是我们“Accepted”的那个Baseline对应的。

 

其次,我们也可以知道Baseline工作的一个小细节,虽然使用Baseline是共用执行计划,但是共用的是Baseline之间的执行计划。第一次用Baseline生成的执行计划,还是走硬解析过程。

 

最后,Baseline的作用下,新的执行计划不会贸然的被采用,但是会被生成保存下来。进而等待确认。这样,起码保证SQL的执行计划不会一个不可控的状态,性能抖动degrade的概率有所降低。

 

 

此外,还有其他的因素影响到selection的过程,就是Baseline中的fixed列。当多个相同语句的Baseline存在时,Oracle要进行“costing”过程,这个取值会影响到costing过程。被标记为Fixed的执行计划在进行Baseline选择的时候,要受到最高的优先级。如果多个Baseline中只有一个Baseline是Fixed状态,且该计划可以执行,那么Oracle最终会选择这个计划。否则就会进行“costing”过程,找出一个相对成本较小的一个作为执行计划。

 

在实践中,我们通常要避免出现“costing”过程,这个过程对系统的负载压力很大。所以我们通常希望有一条可控的执行路径针对特定的SQL。

 

上面讲述了SQL Baseline Selection的过程,下面是最后的执行计划进化和其他一些相关问题。

来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/17203031/viewspace-710332/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论
求道~

注册时间:2010-11-30

  • 博文量
    545
  • 访问量
    8041220