ITPub博客

首页 > Linux操作系统 > Linux操作系统 > ORACLE11G中SQL计划管理

ORACLE11G中SQL计划管理

原创 Linux操作系统 作者:shilei1 时间:2013-01-15 09:51:48 0 删除 编辑
对数据库系统而言,SQL语句的性能是每一个开发和运维DBA格外关注的细节点。前端应用性能中有很大部分是依赖数据库SQL,特别是Query的执行效率。所以,对于数据库调优而言,很大的功夫就在于进行SQL的调优。
 
SQL语句的执行是依赖优化器生成的执行计划。执行计划SQL Plan是真正获取到结果集合的步骤方法。为SQL生成一个更“好”的执行计划,是所有DBMS优化器的重要职责。
 
1、Oracle中的CBO和统计量困局
 
Oracle优化器Optimizer经过RBO到CBO时代的转化,应该说是向着智能化和灵活化实现了飞跃。从简单的规则序列生成执行计划,转而参考数据对象的统计信息。CBO通过统计信息来进行执行计划生成,依据成本cost对比来确定最优的执行计划。
 
伴随周期性的统计量收集,CBO的工作似乎无懈可击。但是,很多时候,即使在CBO时代,SQL的执行计划还是存在性能问题。注意,我们这里并不涉及CBO本身算法的缺陷。
 
最直接的症状,我们经常可以听到这样的抱怨:原本一条SQL或者SP一直或者大部分时间运行良好,但是“时不时”速度变慢。联系DBA进行Tuning后,常常是重新收集一次统计量(增加直方图或者提高采样率),SQL执行恢复正常。之后,在某一个时间点,问题照旧。
 
出现这种问题的根本原因在于:元数据中统计量信息同实际数据表的差异导致了错误执行计划的生成。导致不一致的原因很多,比如统计量收集(自动)时间点不合适、大批量数据操作后没有及时收集或者统计量异常。
 
简单的说,就是CBO对于统计量的变化过于“敏感”,而统计量收集又是和数据表数据不能做到完全同步。
 
对于周期性收集的统计量信息,SQL执行计划的偶然性蜕变似乎是不能解决的困局。因为我们不能做到每次执行SQL前都收集统计量。所以,我们需要做的就是避免异常SQL执行计划的生成,对执行计划生成进行控制。
 
从一些文献看,Oracle对于统计量收集频度问题是比较矛盾的。一种推荐的做法是设置假设,认为在一个时间段内,正常数据表的数据列分布是稳定的,不会有大的变化。在一次稳定的统计量收集之后,就将统计量固定“Freeze”住。这样就不会有一场SQL执行计划的生成。
 
另一种做法,是借助SQL BaseLine,保存一份“健康”时刻的关键SQL执行计划,作为一个留存。当出现紧急的SQL性能问题时,现将保存的执行计划baseline替换上去,支持系统运行。之后再分析出现问题的原因。
 
SQL执行计划异常的常见场景还包括系统升级。不同版本的Oracle优化器,特别是CBO的变化很大,一个在旧版本中工作良好的SQL可能新版本中生成一个很有问题的执行计划。所以,一些系统的移植升级过程中,现有执行计划的保留移植也是重要的考虑因素之一。
 
在11g中,Oracle开始尝试解决这种问题。11g推出了专门针对执行计划管理的组件内容SPM(SQL Plan Management)。在Baseline技术基础上提供了执行计划自动、手动优化进化的功能。
 
 
SQL> select * from v$version;
 
BANNER
--------------------------------------------------------------------------------
Oracle Database11gEnterpriseEdition Release11.2.0.1.0 - Production
PL/SQL Release11.2.0.1.0 - Production
CORE    11.2.0.1.0      Production
 
TNS for Linux: Version11.2.0.1.0 - Production
NLSRTL Version11.2.0.1.0 - Production
 
 
SQL> select parameter ,value from v$option where parameter like '%SQL%';
 
PARAMETER                     VALUE
------------------------------ ----------
SQL Plan Management           TRUE
 
 
2、SPM执行计划“三部曲”
 
我们说,SQL执行计划是和统计量紧密相关,偶然性的统计量错误或者不完全是造成执行计划Degrade的本质原因。SPM(SQL Plan Mangement)期望实现的在执行运行时能够保证SQL性能不会存在退化的情况,而且可以保证不断的推进执行计划性能。
 
简单的说:SPM保证只有那些被接受“Accepted”的可信执行计划才会被使用到,其他生成的执行计划都需要进行确认验证“Verified”。只有那些之后验证性能优化好的执行计划才会最终被接受为执行。
 
SPM主要包括三个关键组件,分别扮演不同的角色:
 
ü       SQL执行Baseline捕获
 
对所有相关的SQL,都生成专门的SQL Baseline,保存在数据库中。注意,Baseline是占用SYSAUX表空间的。
 
ü       SQL Baseline选择
 
在系统的运行时,Oracle要保证每次执行SQL的执行计划都是使用SQL Baseline中的确定执行计划。同时,跟踪所有该statement执行中使用的新执行计划,作为Plan Histroy信息保存下来。
 
Plan Histroy是有不同的状态的。执行计划中包括的状态有“Accepted”和“Unaccepted”两种。非接受的状态SQL可能是由于收集捕获之后没有进行验证“Unvertified”或者被拒绝“Rejected”。
 
ü       SQL执行计划Baseline进化
 
将Plan History中的未验证“Unvertified”状态的执行计划进行判断,判断为“Accepted”或者“Rejected”。
 
从上面的介绍看,SPM本质上就是进行执行计划不断进化upgrade的工具组件。从Oracle10g开始,自动化和智能化就开始渗透到Oracle各个体系环节。自适应组件的频频推出,将过去一些难以确定、经验密集型的问题转为到Oracle自己解决。SPM就是Oracle在执行计划管理上的一个推进阶段。
 
下面,我们分别介绍三个组件的使用和功能。




上篇中我们简单介绍了Oracle11g中SPM(SQL Plan Management)。按照Oracle的意图,使用SPM可以有效的对执行计划进行管理,防止出现性能的Degrade现象。通过进化演进的过程,实现执行计划逐步优化的目标。

 

3、SQL plan baseline capture

 

SPM(SQL Plan Management)的第一步,就是确定好进行控制管理的SQL语句,生成执行计划Baseline。注意,生成的SQL Baseline,是基于当前版本的CBO优化器。由于各种原因,不同版本的CBO在执行计划生成方面差异很大。所以,我们通常将执行计划与特定的Oracle CBO版本绑定。

 

在收集执行计划方面,有两种方法选择:执行计划自动收集(Automatic Capture of Execution Plan)和执行计划批量加载(Bulk load execution plan)。

 

ü       Automatic Capture of Execution Plan

 

自动收集执行计划的功能是Oracle11g的一个可控制组件。

 

 

SQL> select parameter ,value from v$option where parameter like '%SQL%';

 

PARAMETER                     VALUE

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

SQL Plan Management           TRUE

 

 

默认情况下,该组件是不工作的。我们可以通过初始化参数OPTIMIZER_CAPTURE_SQL_PLAN_BASELINE设置为True,开启自动SQL Plan Baseline收集功能。该参数的默认值为false。

 

 

SQL> show parameter capture_sql_plan

 

NAME                                TYPE       VALUE

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

optimizer_capture_sql_plan_baselines boolean    FALSE

 

 

该参数能够在会话和实例级别进行改写。

 

 

SQL> select name,ISSES_MODIFIABLE, ISSYS_MODIFIABLE, ISINSTANCE_MODIFIABLE from v$parameter where name like 'optimizer_capture_sql%';

 

NAME                          ISSES_MODIFIABLE ISSYS_MODIFIABLE ISINSTANCE_MODIFIABLE

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

optimizer_capture_sql_plan_basTRUE            IMMEDIATE       TRUE

elines                                                          

 

SQL> alter session set optimizer_capture_sql_plan_baselines=TRUE;

 

Session altered

 

SQL> select name,value from v$parameter where name like 'optimizer_capture_sql%';

 

NAME                                    VALUE

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

optimizer_capture_sql_plan_baselines    TRUE

 

 

当启动了自动执行计划Baseline捕获之后,Oracle会启用SPM存储库(SPM Repository),将会话范围(Session Level)或者实例范围(System level)发出的所有SQL语句记录在SQL History里面。

 

在Baseline中,包括了执行SQL的语句、执行计划、CBO版本等信息。当一个新的SQL语句出现时,Oracle会将其记录在Baseline中,作为第一个“Accepted”的执行计划。

 

例如,刚刚在会话层面开启了自动收集,之后该会话所有发出的SQL,都会进入执行记录收集阶段。

 

 

SQL> select signature, sql_handle, sql_text,PLAN_NAME,ORIGIN,version, ACCEPTED from dba_sql_plan_baselines;

 

 SIGNATURE SQL_HANDLE                    SQL_TEXT                      PLAN_NAME                     ORIGIN        VERSION   ACCEPTED

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

1.05553156 SYS_SQL_927c03b078db0b4c      select name,value from v$para SQL_PLAN_94z03q1wdq2ucc0c79166AUTO-CAPTURE  11.2.0.1.0 YES

                                         meter where name like 'optimiz                                                         

                                         er_capture_sql%'                                                                       

 

 

如果要查看Baseline对应的执行计划,可以使用dbms_xplan的对应工具包查看。

 

 

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

 

PLAN_TABLE_OUTPUT

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

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

SQL handle: SYS_SQL_927c03b078db0b4c

SQL text: select name,value from v$parameter where name like

         'optimizer_capture_sql%'

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

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

Plan name: SQL_PLAN_94z03q1wdq2ucc0c79166        Plan id: 3234304358

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

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

Plan hash value: 1128103955

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

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

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

|  0 | SELECT STATEMENT |         |    1 | 2115 |    1 (100)| 00:00:01 |

|* 1 | HASH JOIN       |         |    1 | 2115 |    1 (100)| 00:00:01 |

|* 2 |  FIXED TABLE FULL| X$KSPPI |    1 |   81 |    0  (0)| 00:00:01 |

|  3 |  FIXED TABLE FULL| X$KSPPCV |  100 |  198K|    0  (0)| 00:00:01 |

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

32 rows selected

 

 

当一个SQL的Baseline已经存在,再次执行这个SQL时,如果有新的执行计划被生成,SQL History中会记录上这个执行计划并且被判定为等待验证的Plan。

 

ü       Bulk Load批量加载

 

SQL性能出现抖动问题一个主要场景就是在数据库升级Upgrade后,由于CBO版本的变化,引起执行计划的变化。此时可以针对一部分SQL语句的执行计划进行控制管理,建立SQL Baseline。

 

建立批量Load的方法目前有三种:SQL Tuning Set、从当前Cursor Cache中获取还有就是从外部数据表中进行加载。

 

SQL Tuning Set(STS)

 

SQL Tuning Set是Oracle提供的一种进行自动化SQL调优的工具。当我们发现或者Oracle的自动Tunning Advice发现一个SQL运行有性能问题时,是可以放置在一个Tunning Set中进行调试。

 

在Oracle11g中,支持我们从一个SQL Tunning Set中将一个SQL的执行计划加载为SQL Plan Baseline。我们可以通过Oracle EM或者PL/SQL脚本实现Tunning Set到Baseline的转换。

 

 

 FUNCTION load_plans_from_sqlset( sqlset_name       IN VARCHAR2,

                                  sqlset_owner      IN VARCHAR2 := NULL,

                                  basic_filter      IN VARCHAR2 := NULL,

                                  fixed             IN VARCHAR2 := 'NO',

                                  enabled           IN VARCHAR2 := 'YES',

                                  commit_rows       IN NUMBER := 1000

                                )

 RETURN PLS_INTEGER;

 

 

输入SQL Tunning Set的名称和其他信息,就可以实现将其转化为Baseline。从实际操作上看,使用SQL Tunning Set转化为Baseline的场景还是很多的。当我们的数据库一部分SQL出现问题的时候,可以专门对其进行监管。

 

 

从Cursor Cache中加载

 

每条生成的SQL语句执行计划,都会以shared cursor组成部分的形式保存在library cache中。如果有一个SQL执行计划存在于Cursor Cache中,就可以将它直接转化为baseline。

 

输入shared cursor的model_name、schema和SQL_ID,调用DBMS_SPM包方法,就可以将指定SQL的执行计划加载为Baseline。

 

 

SQL> select count(*) from dba_sql_plan_baselines;

 

 COUNT(*)

----------

        0

 

SQL> select sql_text, sql_id, plan_hash_value from v$sql where sql_text like 'select /* demo_test */count(*)%';

 

SQL_TEXT                      SQL_ID       PLAN_HASH_VALUE

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

select /* demo_test */count(*) cd4g0jbf2m5wj     2966233522

 from t                                     

 

SQL> var i number;

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

 

PL/SQL procedure successfully completed

i

---------

1

 

SQL> select sql_handle, sql_text, plan_name, origin from dba_sql_plan_baselines;

 

SQL_HANDLE      SQL_TEXT              PLAN_NAME         ORIGIN

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

SYS_SQL_4bfcd2ee36110fce      select /* demo_test */count(*)SQL_PLAN_4rz6kxsv123yf3fdbb376 MANUAL-LOAD

                               from t                                                      

 

 

上面代码演示了如何从cursor cache中获取到指定的一个SQL执行计划作为Baseline。

 

从外部数据表中加载执行计划作为Baseline

 

对很多应用系统,特别是运行复杂SQL的应用程序,在数据库层面经常有很多调试好的执行计划。当系统部署或者升级的时候,这些执行计划是保证系统高性能运行的重要保证。

 

SPM允许执行计划Baseline从一个系统中打包为一个table,在转移到另一个系统中进行释放。这样实现了从一个Table加载Baseline计划。具体执行过程如下:

 

首先,在原有的系统中,我们使用dbms_spm.create_stgtab_baseline创建Baseline容纳的数据表。

 

 

SQL> exec dbms_spm.create_stgtab_baseline(table_name => 'BASELINE_TEST',table_owner => 'SCOTT');

 

PL/SQL procedure successfully completed

 

SQL> desc scott.baseline_test;

Name               Type         Nullable Default Comments

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

VERSION            NUMBER       Y                        

SIGNATURE          NUMBER       Y                        

SQL_HANDLE         VARCHAR2(30) Y                        

OBJ_NAME           VARCHAR2(30) Y                        

OBJ_TYPE           VARCHAR2(30) Y                        

PLAN_ID            NUMBER       Y       

(篇幅原因,有省略…)

 

 

注意,在SYS下创建stage table,通常会报错。

 

 

SQL> exec dbms_spm.create_stgtab_baseline(table_name => 'BASELINE_TEST');

 

begin dbms_spm.create_stgtab_baseline(table_name => 'BASELINE_TEST'); end;

 

ORA-19381:无法在SYS方案中创建登台表

ORA-06512:在"SYS.DBMS_SMB", line 313

ORA-06512:在"SYS.DBMS_SPM", line 2940

ORA-06512:在line 1

 

 

之后,可以使用dbms_spm.pack_stgtab_baseline方法将指定(或者全部)Baseline保存在Stage Table中。

 

 

SQL> select count(*) from scott.baseline_test;

 

 COUNT(*)

----------

        0

 

SQL> var i number;

SQL> exec :i := dbms_spm.pack_stgtab_baseline(table_name => 'BASELINE_TEST', table_owner => 'SCOTT');

 

PL/SQL procedure successfully completed

i

---------

1

 

SQL> select count(*) from scott.baseline_test;

 

 COUNT(*)

----------

        1

 

 

在数据表中,保存了导出的baseline信息,之后可以借助数据导出工具将数据表转移到一个新系统中(这里我们将原有Baseline删除,用于模拟环境)。

 

--删除Baseline

SQL> var i number;

SQL> exec :i := dbms_spm.drop_sql_plan_baseline(sql_handle => 'SYS_SQL_4bfcd2ee36110fce', plan_name => 'SQL_PLAN_4rz6kxsv123yf3fdbb376');

 

PL/SQL procedure successfully completed

i

---------

1

 

 

SQL> select sql_handle, sql_text, plan_name from dba_sql_plan_baselines;

 

SQL_HANDLE                    SQL_TEXT                      PLAN_NAME

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

 

 

当转移到一个新系统之后,使用unpack_stgtab_baseline方法,将数据加载到系统Baseline中。

 

--重新加载Baseline

SQL> var i number;

SQL> exec :i := dbms_spm.unpack_stgtab_baseline(table_name => 'BASELINE_TEST',table_owner => 'SCOTT');

 

PL/SQL procedure successfully completed

i

---------

1

 

SQL> select sql_handle, sql_text, plan_name from dba_sql_plan_baselines;

 

SQL_HANDLE                    SQL_TEXT                      PLAN_NAME

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

SYS_SQL_4bfcd2ee36110fce      select /* demo_test */count(*) SQL_PLAN_4rz6kxsv123yf3fdbb376

                               from t                       

 

 

实现了将Baseline移植的操作过程,这个在升级等场景中意义重大。

 

上面我们详细介绍了SPM的第一个组件内容——SQL Plan Baseline Capture。主要分为自动和手动两种。默认情况下,Oracle11g中关闭自动开关的,这个也是有相应的考虑。因为开启SPM是在一些特殊的情况下才有优势作用。

 

所以,在实际使用时,还是推荐用Bulk Load的方式,针对一些特定的SQL进行SPM管理。

 

下面我们将继续介绍SPM的第二个操作Plan Baseline Selection。




上面我们介绍了如果从各种源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 asselect * 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_28mhc5q8xbbxq94ecae5cYES    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_28mhc5q8xbbxq94ecae5cYES    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_28mhc5q8xbbxq94ecae5cused 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/196700/viewspace-752707/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2018-10-10

  • 博文量
    548
  • 访问量
    130255