SQLProfile(第二篇)

通过SQL Tuning Advisor使用SQL profile

在11GR2上SQL Tuning Advisor已经变得非常好用,我非常喜欢将一些非常复杂的SQL语句交给SQL Tuning Advisor来调优,几乎每次都不让我失望,通常调优结束后,SQL Tuning Advisor都会给你一些建议,例如建议你创建索引或者收集统计信息,或者建议你接受SQL Profile并且给出了接受SQL Profile后性能将得到的提升。本节将会给出一个示例来演示如何通过SQL Tuning Advisor来使用SQL Profile。首先我们需要构建一下需要用到的测试表:

10年积累的成都网站设计、成都网站制作经验,可以快速应对客户对网站的新想法和需求。提供各种问题对应的解决方案。让选择我们的客户得到更好、更有力的网络服务。我虽然不认识你,你也不认识我。但先网站制作后付款的网站建设流程,更有仁和免费网站建设让你可以放心的选择与我们合作。

SQL>CREATE TABLE test

  2   AS

  3   SELECT ROWNUM id,

  4          DBMS_RANDOM.STRING('A', 12) name,

  5          DECODE(MOD(ROWNUM, 500), 0, 'Inactive', 'Active') status

  6     FROM all_objects a,dba_objects b

  7    WHERE ROWNUM <= 50000;

 

Table created.

 

SQL>create index t_ind on t(status);

 

Index created.

 

SQL>begin

  2     dbms_stats.gather_table_stats(ownname          =>'test',

  3                                   tabname          => 'test',

  4                                   no_invalidate    => FALSE,

  5                                   estimate_percent => 100,

  6                                   force            => true,

  7                                   degree         => 5,

  8                              method_opt       => 'for all columns  size 1',

  9                                   cascade          => true);

 10   end;

 11   /

 

SQL>select status,count(*) from test group by status;

 

STATUS             COUNT(*)

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

Active                49900

Inactive                100

 

上面的代码做了下面几件事:

l 创建了一张测试表test,总记录数50000。

l 表上的字段status一共有2个唯一值:Active和Inactive,此字段有数据倾斜。

l 列status上值为Active的值有49900个,占了表里绝大多数的记录,为Inactive的记录非常少,只有100个。

l status字段上有索引,分析了表的统计信息,但是status字段没有收集直方图。

我们来对status为Inactive的值做查询,由于status为Inactive的值非常少,因此走索引扫描性能更好,但是由于列上缺少直方图,因此执行计划会走全表扫描:

SQL>select count(name) from test where status='Inactive';

 

COUNT(NAME)

-----------

        100

 

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

PLAN_TABLE_OUTPUT

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

SQL_ID  c37q7z5qjnwwf, child number 0

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

select count(name) from test where status='Inactive'

Plan hash value: 1950795681

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

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

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

|   0 | SELECT STATEMENT   |      |       |       |    51 (100)|          |

|   1 |  SORT AGGREGATE    |      |     1 |    21 |            |          |

|*  2 |   TABLE ACCESS FULL| TEST | 25000 |   512K|    51   (2)| 00:00:01 |

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

上面执行计划里显示的经过谓词过滤后的全表扫描返回的基数为25000,因为缺少直方图,因此优化器就简单的通过 基数=表的总记录数/status字段的唯一值数量=50000/2=25000来得出基数。我们来通过SQL Tuning Advisor分析一下这个SQL,看看优化器能不能识别到这是一个低效的执行计划,能否给出我们一些建议:

SQL>var c varchar2(100)

SQL>exec :c := dbms_sqltune.CREATE_TUNING_TASK(SQL_ID=>'c37q7z5qjnwwf')

 

PL/SQL procedure successfully completed.

 

SQL>exec dbms_sqltune.execute_tuning_task(task_name => :c)

 

PL/SQL procedure successfully completed.

 

SQL>select dbms_sqltune.report_tuning_task(:c) from dual;

 

DBMS_SQLTUNE.REPORT_TUNING_TASK(:C)

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

GENERAL INFORMATION SECTION

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

Tuning Task Name   : TASK_1112

Tuning Task Owner  : TEST

Workload Type      : Single SQL Statement

Scope              : COMPREHENSIVE

Time Limit(seconds): 1800

Completion Status  : COMPLETED

Started at         : 08/01/2014 15:59:32

Completed at       : 08/01/2014 15:59:33

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

Schema Name: TEST

SQL ID     : c37q7z5qjnwwf

SQL Text   : select count(name) from test where status='Inactive'

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

FINDINGS SECTION (1 finding)

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

1- SQL Profile Finding (see explain plans section below)

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

  A potentially better execution plan was found for this statement.

  Recommendation (estimated benefit: 51.46%)

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

  - Consider accepting the recommended SQL Profile.

    execute dbms_sqltune.accept_sql_profile(task_name => 'TASK_1112',

            task_owner => 'TEST', replace => TRUE);

 

  Validation results

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

  The SQL Profile was tested by executing both its plan and the original plan

  and measuring their respective execution statistics. A plan may have been

  only partially executed if the other could be run to completion in less time.

 

                           Original Plan  With SQL Profile  % Improved

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

  Completion Status:            COMPLETE          COMPLETE

  Elapsed Time (s):              .00212           .000221      89.57 %

  CPU Time (s):                 .002099             .0002      90.47 %

  User I/O Time (s):                  0                 0

  Buffer Gets:                      210               102      51.42 %

  Physical Read Requests:             0                 0

  Physical Write Requests:            0                 0

  Physical Read Bytes:                0                 0

  Physical Write Bytes:               0                 0

  Rows Processed:                     1                 1

  Fetches:                            1                 1

  Executions:                         1                 1

 

  Notes

  -----

  1. Statistics for the original plan were averaged over 10 executions.

  2. Statistics for the SQL Profile plan were averaged over 10 executions.

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

EXPLAIN PLANS SECTION

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

1- Original With Adjusted Cost

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

Plan hash value: 1950795681

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

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

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

|   0 | SELECT STATEMENT   |      |     1 |    21 |    51   (2)| 00:00:01 |

|   1 |  SORT AGGREGATE    |      |     1 |    21 |            |          |

|*  2 |   TABLE ACCESS FULL| TEST |   100 |  2100 |    51   (2)| 00:00:01 |

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

2- Using SQL Profile

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

Plan hash value: 4130896540

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

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

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

|   0 | SELECT STATEMENT             |       |     1 |    21 |     2   (0)| 00:00:01 |

|   1 |  SORT AGGREGATE              |       |     1 |    21 |            |          |

|   2 |   TABLE ACCESS BY INDEX ROWID| TEST  |   100 |  2100 |     2   (0)| 00:00:01 |

|*  3 |    INDEX RANGE SCAN          | T_IND |   100 |       |     1   (0)| 00:00:01 |

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

Dbms_sqltune包被用来创建调优任务、执行调优任务、查看调优结果,通过dbms_sqltune包的CREATE_TUNING_TASK函数来为SQL_ID为c37q7z5qjnwwf的SQL创建了一个调优任务。然后通过execute_tuning_task过程来执行这个调优任务,任务运行后,优化器会利用动态采样等技术去验证评估内容与实际内容的差异,并且根据差异去调整执行计划。最后通过report_tuning_task来产生report查看调优的结果。调优结果里为我们提供了一个建议,建议我们采用一个SQL Profile,并且比对了采用SQL Profile后的性能提升,report的后面EXPLAIN PLANS SECTION部分展示了采用SQL Profile后,执行计划变为了索引扫描,而且基数的评估非常准确,从25000已经变为了100。

Note:当你运行SQLTuning Advisor后,建议你接受一个SQL Profile,如果你想在接受SQL Profile前知道它到底为你提供了些什么,可以运行以下查询获得:

sys@DLSP>select                                              

  2   --    b.ATTR1        -- 10g 列     

  3         b.ATTR5        -- 11g 列     

  4   from                               

  5           wri$_adv_tasks     a,      

  6           wri$_adv_rationale b       

  7   where                              

  8           a.name = 'TASK_1112'       

  9   and     b.task_id = a.id           

 10   order by                           

 11           b.rec_id, b.id             

 12   ;                   

 

ATTR5

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

OPT_ESTIMATE(@"SEL$1", TABLE, "TEST"@"SEL$1", SCALE_ROWS=0.004)

OPT_ESTIMATE(@"SEL$1", INDEX_SCAN, "TEST"@"SEL$1", "T_IND", SCALE_ROWS=0.004)

OPTIMIZER_FEATURES_ENABLE(default)

 

wri$_adv_tasks的name字段为任务名,在我们上面的例子里,可以通过print c在SQLPLUS环境下获得任务名,也可以在dbms_sqltune.report_tuning_task(:c)的输出里找到任务名。

我们接受这个SQL Profile来看看再次查询是否能用到刚创建的SQL Profile:

SQL>execute dbms_sqltune.accept_sql_profile(task_name => 'TASK_1112',-

>             task_owner => 'TEST', replace => TRUE);

 

PL/SQL procedure successfully completed.

 

SQL>select count(name) from test where status='Inactive';

 

COUNT(NAME)

-----------

        100

 

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

 

PLAN_TABLE_OUTPUT

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

SQL_ID  c37q7z5qjnwwf, child number 0

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

select count(name) from test where status='Inactive'

 

Plan hash value: 4130896540

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

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

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

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

|   1 |  SORT AGGREGATE              |       |     1 |    21 |            |          |

|   2 |   TABLE ACCESS BY INDEX ROWID| TEST  |   100 |  2100 |     2   (0)| 00:00:01 |

|*  3 |    INDEX RANGE SCAN          | T_IND |   100 |       |     1   (0)| 00:00:01 |

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

Note

-----

   - SQL Profile SYS_SQLPROF_01479094feeb0003 used for this statement

 

接受SQL Tuning Advisor提供的SQL Profile后,执行计划Note部分:- SQL Profile SYS_SQLPROF_01479094feeb0003 used for this statement,显示已经使用到了SQL Profile,注意由SQL Tuning Advisor产生的SQL Profile名称都是SYS_SQLPROF作为前缀,使用到SQL Profile后执行计划也已经从全表扫描变为了索引扫描。dbms_sqltune的accept_sql_profile过程有多个参数可用,task_name、task_owner指创建SQL调优任务的任务名和所属用户,参数name和DESCRIPTION指SQL Profile的名字和对SQL Profile的描述,参数CATEGORY来指定创建的SQL Profile所属的类,默认的类为default。参数replace代表是否取代已有的SQL Profile,由于一个SQL只能有一个SQL Profile,不像Baseline,一个SQL可用有多个Baseline,因此如果一个SQL已经存在了SQL Profile,那么重新创建时,必须指定replace参数,设置为true,参数force_match指明了文本标准化的方式,默认为false。一旦接受SQL Profile,就可以通过视图dba_sql_profiles视图来查看SQL Profile的相关信息。因为SQL Profile并不属于某个用户,因此all_sql_profiles和user_sql_profiles视图都不可用。

如果一个SQL使用了SQL Profile,那么这个SQL的v$sql的sql_profile字段会显示使用到的SQL Profile的名字。下面的查询显示了系统中存在的SQL Profile和当前共享池中正在使用的SQL Profile的SQL。

SQL>select name, category, status, substr(sql_text,1,25) sql_text, force_matching

  2  from dba_sql_profiles

  3  where sql_text like nvl('&sql_text','%')

  4  and name like nvl('&name',name)

  5  order by last_modified

  6  ;

Enter value for sql_text:

Enter value for name:

 

NAME                           CATEGORY  STATUS   SQL_TEXT                   FORCE_

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

profile_c99yw1xkb4f1u_dwrose   DEFAULT   ENABLED  select * from test         NO

profile_bhm28h6575bjy_dwrose   DEFAULT   ENABLED  select test2.object_name,  NO

profile_51k1ug4rwah3c_dwrose   DEFAULT   ENABLED  select distinct substr(ma  NO

profile_cm6stbx539mcz_dwrose   DEFAULT   ENABLED  select count(*) from tt    NO

profile_c37q7z5qjnwwf_dwrose   DEFAULT   ENABLED  select count(name) from t  NO

 

SQL>select sql_id,

  2         child_number cn,

  3         plan_hash_value plan_hash,

  4         sql_profile,

  5         executions execs,

  6         buffer_gets / decode(nvl(executions, 0), 0, 1, executions) avg_lio

  7    from v$sql s

  8   where upper(sql_text) like upper(nvl('&sql_text', sql_text))

  9     and sql_text not like '%from v$sql where sql_text like nvl(%'

 10     and sql_id like nvl('&sql_id', sql_id)

 11     and sql_profile is not null

 12   order by 1, 2, 3 ;

Enter value for sql_text:

 

SQL_ID          CN  PLAN_HASH SQL_PROFILE                    EXECS    AVG_LIO

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

c37q7z5qjnwwf    0 4130896540 profile_c37q7z5qjnwwf_dwrose       1        108

c37q7z5qjnwwf    1 4130896540 profile_c37q7z5qjnwwf_dwrose       2        105

我们根据SQL_PROFILE的命名知道,这些SQL Profile都不是SQL Tuning Advisor创建的,是我们手工创建的,因为SQL Tuning Advisor创建的SQL Profile都是以SYS_SQLPROF作为前缀的。


本文名称:SQLProfile(第二篇)
URL分享:http://myzitong.com/article/ijocei.html