SQL Profile 如何使用

这些文章是我从《基于Oracle的SQL优化》 书中找到的

可以参考一下我的实验过程


SQL Profile

Oracle10G中的SQL Profile可以说是Oracle 9i中的Stored Outline的进化。

Stored Outline能够实现的功能SQL Profile也完全能够实现。

与Stored Outline相比,SQL Profile具备如下优点:

  • 更容易生成、更改和控制。

  • 在对SQL语句的支持上做得更好,也就是说适用范围更广。

使用SQL Profile可以容易实现如下两个目的:

  • 锁定或者说稳定执行计划。

  • 在不能修改目标SQL的SQL文本的情况下使用目标SQL语句按指定的执行计划运行


SQL Profile有两种类型:

  • Automatic

  • Manual


************************** Automatic类型的SQL Profile ************************** 

Automatic类型的SQL Profile其实就是针对目标SQL的一些额外的调整信息,这些信息都存储在数据字典里。

当有了Automatic类型的SQL Profile后,Oracle在产生执行计划时就会根据它对目标SQL所涉及的统计信息等内容做相应的调整,因此能够在一定程度上避免产生错误的执行计划。

你不用担心Automatic类型的SQL Profile的准确性,因为Oracle会使用类似于动态采样技术那样的手段来保证这些额外调整信息相对准确。

举个简单的例子,表A的原始统计信息中记录表A只有100条数据,但其实实际的数据量确实100万条。

此时如果CBO仅仅根据表A的原始统计信息来对目标SQL产生相应的执行计划的话,那么所得到的执行计划很有可能就是错误的;

如果此时对表A使用了Automatic类型的SQL Profile,则Oracle只需在数据字典里记录目标SQL的“SCALE_ROWS=10000”,表示目标SQL中表A的实际记录数应该为其原始统计信息的10,000倍,

那么CBO再针对目标SQL产生执行计划时就会认为表A的记录数是100*10=100万,

据此产生的执行计划很有可能是正确的执行计划了。

这里需要注意的是,Automatic类型的SQL Profile并不会像Stored Outline那样锁定目标SQL的执行计划,因为Automatic类型的SQL Profile的本质就是针对目标SQL的一些额外的调整信息,这些额外的调整信息一旦发生变化,即使原有Automatic类型的SQL Profile并没有改变,该SQL的执行计划也可能会发生变化。

从这个意义上将,Automatic类型的SQL Profile并不能完全起到稳定目标SQL的执行计划的作用,虽然他确实可以用来调整执行计划。

我们来看一个在不更改目标SQL的SQL文本的情况下使用Automatic类型的SQL Profile来调整执行计划的实例

SQL> select * from v$version;

BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.5.0 - 64bi
PL/SQL Release 10.2.0.5.0 - Production
CORE    10.2.0.5.0      Production
TNS for Linux: Version 10.2.0.5.0 - Production
NLSRTL Version 10.2.0.5.0 - Production
SQL> grant dba to scott;

Grant succeeded.
SQL> conn scott/tiger
Connected.
SQL> create table t1 (n number);

Table created.

插入数据 

SQL> declare
  2  begin
  3  for i in 1..10000
  4  loop
  5  insert into t1 values(i);
  6  commit;
  7  end loop;
  8  end;
  9  /

PL/SQL procedure successfully completed.

SQL> select count(*) from t1;

  COUNT(*)
----------
     10000

SQL> create index idx_t1 on t1(n);

Index created.


收集统计信息

SQL> exec dbms_stats.gather_table_stats(ownname=>'SCOTT',tabname=>'T1',method_opt=>'for all columns size 1',cascade=>true);

PL/SQL procedure successfully completed.

SQL> select /*+ no_index(t1 idx_t1) */ * from t1 where n=1;

         N
----------
         1

SQL> select * from table(dbms_xplan.display_cursor(null,null,'advanced'));

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
SQL_ID  1kg76709mx29d, child number 0
-------------------------------------
select /*+ no_index(t1 idx_t1) */ * from t1 where n=1

Plan hash value: 3617692013

--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |       |       |     6 (100)|          |
|*  1 |  TABLE ACCESS FULL| T1   |     1 |     3 |     6   (0)| 00:00:01 |
--------------------------------------------------------------------------

Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------

   1 - SEL$1 / T1@SEL$1

Outline Data
-------------

  /*+

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
      BEGIN_OUTLINE_DATA
      IGNORE_OPTIM_EMBEDDED_HINTS
      OPTIMIZER_FEATURES_ENABLE('10.2.0.5')
      ALL_ROWS
      OUTLINE_LEAF(@"SEL$1")
      FULL(@"SEL$1" "T1"@"SEL$1")
      END_OUTLINE_DATA
  */

Predicate Information (identified by operation id):
---------------------------------------------------

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------

   1 - filter("N"=1)

Column Projection Information (identified by operation id):
-----------------------------------------------------------

   1 - "N"[NUMBER,22]


41 rows selected.
针对上述SQL创建一个Automatic类型的SQL Profile的自动调整任务:
SQL> declare
  2  my_task_name varchar2(30);
  3  my_sqltext clob;
  4  begin
  5  my_sqltext :='select /*+ no_index(t1 idx_t1) */ * from t1 where n=1';
  6  my_task_name := dbms_sqltune.create_tuning_task(
  7  sql_text => my_sqltext,
  8  user_name => 'SCOTT',
  9  scope => 'COMPREHENSIVE',
10  time_limit => 60,
11  task_name => 'my_sql_tuning_task_2',
12  description => 'Task to tune a query on table t1');
13  end;
14  /

PL/SQL procedure successfully completed.


执行上述自动调整任务

SQL> begin
  2  dbms_sqltune.execute_tuning_task( task_name => 'my_sql_tuning_task_2');
  3  end;
  4  /

PL/SQL procedure successfully completed.


查看刚刚运行的自动调整任务的调整结果:

SQL> set long 9000
SQL> set longchunksize 1000
SQL> set lines 800
SQL> select dbms_sqltune.report_tuning_task('my_sql_tuning_task_2') from dual;

DBMS_SQLTUNE.REPORT_TUNING_TASK('MY_SQL_TUNING_TASK_2')
-------------------------------------------------------------------------------
GENERAL INFORMATION SECTION
-------------------------------------------------------------------------------
Tuning Task Name                  : my_sql_tuning_task_2
Tuning Task Owner                 : SCOTT
Scope                             : COMPREHENSIVE
Time Limit(seconds)               : 60
Completion Status                 : COMPLETED
Started at                        : 07/12/2015 04:34:15
Completed at                      : 07/12/2015 04:34:16
Number of SQL Profile Findings    : 1

-------------------------------------------------------------------------------
Schema Name: SCOTT
SQL ID     : 4bh6sn1zvpgq7
SQL Text   : select /*+ no_index(t1 idx_t1) */ * from t1 where n=1

-------------------------------------------------------------------------------
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: 83.98%)
  ------------------------------------------
  - Consider accepting the recommended SQL profile.
    execute dbms_sqltune.accept_sql_profile(task_name =>
            'my_sql_tuning_task_2', replace => TRUE);

-------------------------------------------------------------------------------
EXPLAIN PLANS SECTION
-------------------------------------------------------------------------------

1- Original With Adjusted Cost
------------------------------
Plan hash value: 3617692013

--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |     1 |     3 |     6   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| T1   |     1 |     3 |     6   (0)| 00:00:01 |
--------------------------------------------------------------------------


Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter("N"=1)

2- Using SQL Profile
--------------------
Plan hash value: 1369807930

---------------------------------------------------------------------------
| Id  | Operation        | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT |        |     1 |     3 |     1   (0)| 00:00:01 |
|*  1 |  INDEX RANGE SCAN| IDX_T1 |     1 |     3 |     1   (0)| 00:00:01 |
---------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - access("N"=1)

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

按照上面提示,输入下面的命令,接收这个SQL Profile:

SQL> execute dbms_sqltune.accept_sql_profile(task_name =>'my_sql_tuning_task_2', replace => TRUE);

PL/SQL procedure successfully completed.
SQL> select /*+ no_index(t1 idx_t1) */ * from t1 where n=1;

         N
----------
         1

SQL> select * from table(dbms_xplan.display_cursor(null,null,'advanced'));

PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------
SQL_ID  1kg76709mx29d, child number 1
-------------------------------------
select /*+ no_index(t1 idx_t1) */ * from t1 where n=1

Plan hash value: 1369807930

---------------------------------------------------------------------------
| Id  | Operation        | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT |        |       |       |     1 (100)|          |
|*  1 |  INDEX RANGE SCAN| IDX_T1 |     1 |     3 |     1   (0)| 00:00:01 |
---------------------------------------------------------------------------

Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------

   1 - SEL$1 / T1@SEL$1

Outline Data
-------------

  /*+
      BEGIN_OUTLINE_DATA
      IGNORE_OPTIM_EMBEDDED_HINTS
      OPTIMIZER_FEATURES_ENABLE('10.2.0.5')
      ALL_ROWS
      OUTLINE_LEAF(@"SEL$1")
      INDEX(@"SEL$1" "T1"@"SEL$1" ("T1"."N"))
      END_OUTLINE_DATA
  */

Predicate Information (identified by operation id):
---------------------------------------------------

PLAN_TABLE_OUTPUT

   1 - access("N"=1)

Column Projection Information (identified by operation id):
-----------------------------------------------------------

   1 - "N"[NUMBER,22]

Note
-----
   - SQL profile "SYS_SQLPROF_01539fb60e15c000" used for this statement


45 rows selected.

注意到Note部分有这样的内容“SQL profile "SYS_SQLPROF_01539fb60e15c000" used for this statement”,这说明我们刚才接受的SQL Profile已经起了作用,该SQL Profile的名字为SYS_SQLPROF_01539fb60e15c000。

从执行计划中也可以看到,执行计划确实已经从原先对表T1的全表扫描变为了对索引IDX_T1的索引范围扫描。

这同时也说明Automatic类型的SQL Profile确实可以在不改变目标SQL的SQL文本的情况下更改其执行计划。

DBMS_SQLTUNE.ACCEPT_SQL_PROFILE的输入参数FORCE_MATCH的默认值为FALSE,

表示只有在SQL文本完全匹配的情况下才会应用SQL Profile,

这种情况下只要目标SQL的SQL文本发生一点改动,原有的SQL Profile就会失去作用。

上述SQL的SQL文本是:select /*+ no_index(t1 idx_t1) */ * from t1 where n=1,

这里将其where条件由“n=1”改为“n=2”后再次执行:


SQL> select /*+ no_index(t1 idx_t1) */ * from t1 where n=2;

         N
----------
         2

SQL> select * from table(dbms_xplan.display_cursor(null,null,'advanced'));

PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------------
SQL_ID  c4j6hxkqudj1s, child number 0
-------------------------------------
select /*+ no_index(t1 idx_t1) */ * from t1 where n=2

Plan hash value: 3617692013

--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |       |       |     6 (100)|          |
|*  1 |  TABLE ACCESS FULL| T1   |     1 |     3 |     6   (0)| 00:00:01 |
--------------------------------------------------------------------------

Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------

   1 - SEL$1 / T1@SEL$1

Outline Data
-------------

  /*+

      BEGIN_OUTLINE_DATA
      IGNORE_OPTIM_EMBEDDED_HINTS
      OPTIMIZER_FEATURES_ENABLE('10.2.0.5')
      ALL_ROWS
      OUTLINE_LEAF(@"SEL$1")
      FULL(@"SEL$1" "T1"@"SEL$1")
      END_OUTLINE_DATA
  */

Predicate Information (identified by operation id):
---------------------------------------------------


   1 - filter("N"=2)

Column Projection Information (identified by operation id):
-----------------------------------------------------------

   1 - "N"[NUMBER,22]


41 rows selected.
将FORCE_MATCH 的值设为TRUE后重新执行一下DBMS_SQLTUNE.ACCEPT_SQL_PROFILE:
SQL> execute dbms_sqltune.accept_sql_profile(task_name =>'my_sql_tuning_task_2', task_owner=>'SCOTT', replace => TRUE, force_match=>true);

PL/SQL procedure successfully completed.

SQL>
SQL> select /*+ no_index(t1 idx_t1) */ * from t1 where n=2;

         N
----------
         2

SQL> set pages 200
SQL> set lines 200
SQL> select * from table(dbms_xplan.display_cursor(null,null,'advanced'));

PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------------
SQL_ID  c4j6hxkqudj1s, child number 1
-------------------------------------
select /*+ no_index(t1 idx_t1) */ * from t1 where n=2

Plan hash value: 1369807930

---------------------------------------------------------------------------
| Id  | Operation        | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT |        |       |       |     1 (100)|          |
|*  1 |  INDEX RANGE SCAN| IDX_T1 |     1 |     3 |     1   (0)| 00:00:01 |
---------------------------------------------------------------------------

Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------

   1 - SEL$1 / T1@SEL$1

Outline Data
-------------

  /*+
      BEGIN_OUTLINE_DATA
      IGNORE_OPTIM_EMBEDDED_HINTS
      OPTIMIZER_FEATURES_ENABLE('10.2.0.5')
      ALL_ROWS
      OUTLINE_LEAF(@"SEL$1")
      INDEX(@"SEL$1" "T1"@"SEL$1" ("T1"."N"))
      END_OUTLINE_DATA
  */

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - access("N"=2)

Column Projection Information (identified by operation id):
-----------------------------------------------------------

   1 - "N"[NUMBER,22]

Note
-----
   - SQL profile "SYS_SQLPROF_01539fb946248001" used for this statement


45 rows selected.


************************** Manual类型的SQL Profile ************************** 

Manual类型的SQL Profile本质上就是一堆Hint的组合,这一对Hint的组合实际上来源于执行计划中Outline DATA部分的Hint组合。

Manual类型的SQL Profile同样可以在不更改目标SQL的SQL文本的情况下调整其执行计划,而且更为重要的是,Manual类型的SQL Profile可以起到很好的稳定目标SQL的执行计划的作用,这一点是Automatic类型的SQL Profile所不具备的。

我们来看一个在不改变目标SQL的SQL文本的情况下通过Manual类型的SQL Profile来调整其执行计划的实例。

这里还沿用刚刚的测试表T1和其上的索引IDX_T1。

先删掉类型为Automatic 的SQL Profile。

SQL> exec dbms_sqltune.drop_sql_profile('SYS_SQLPROF_01539fb946248001');

PL/SQL procedure successfully completed.
SQL> exec dbms_sqltune.drop_sql_profile('SYS_SQLPROF_01539fb60e15c000');

PL/SQL procedure successfully completed.
再次执行目标SQL:
SQL> select /*+ no_index(t1 idx_t1) */ * from t1 where n=3
  2  ;

         N
----------
         3

查看执行计划:
SQL> select * from table(dbms_xplan.display_cursor(null,null,'advanced'));

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID  8czm7qzgrxc1b, child number 0
-------------------------------------
select /*+ no_index(t1 idx_t1) */ * from t1 where n=3

Plan hash value: 3617692013

--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |       |       |     6 (100)|          |
|*  1 |  TABLE ACCESS FULL| T1   |     1 |     3 |     6   (0)| 00:00:01 |
--------------------------------------------------------------------------

Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------

   1 - SEL$1 / T1@SEL$1

Outline Data
-------------

  /*+
      BEGIN_OUTLINE_DATA
      IGNORE_OPTIM_EMBEDDED_HINTS
      OPTIMIZER_FEATURES_ENABLE('10.2.0.5')
      ALL_ROWS
      OUTLINE_LEAF(@"SEL$1")
      FULL(@"SEL$1" "T1"@"SEL$1")
      END_OUTLINE_DATA
  */

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter("N"=3)

Column Projection Information (identified by operation id):
-----------------------------------------------------------

   1 - "N"[NUMBER,22]


41 rows selected.
使用正确的执行计划,运行目标SQL:
SQL> select * from t1 where n=3;

         N
----------
         3

SQL> select * from table(dbms_xplan.display_cursor(null,null,'advanced'));

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID  dvc6rkgpkan8d, child number 0
-------------------------------------
select * from t1 where n=3

Plan hash value: 1369807930

---------------------------------------------------------------------------
| Id  | Operation        | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT |        |       |       |     1 (100)|          |
|*  1 |  INDEX RANGE SCAN| IDX_T1 |     1 |     3 |     1   (0)| 00:00:01 |
---------------------------------------------------------------------------

Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------

   1 - SEL$1 / T1@SEL$1

Outline Data
-------------

  /*+
      BEGIN_OUTLINE_DATA
      IGNORE_OPTIM_EMBEDDED_HINTS
      OPTIMIZER_FEATURES_ENABLE('10.2.0.5')
      ALL_ROWS
      OUTLINE_LEAF(@"SEL$1")
      INDEX(@"SEL$1" "T1"@"SEL$1" ("T1"."N"))
      END_OUTLINE_DATA
  */

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - access("N"=3)

Column Projection Information (identified by operation id):
-----------------------------------------------------------

   1 - "N"[NUMBER,22]


41 rows selected.

创建SQL Profile:

把Plan hash value: 1369807930 这条执行计划中的Outline DATA部分贴到下面去。

放在sqlprof_attr部分,贴的时候注意单引号和双引号。

大概过程是,先改写目标SQL的文本,在其中使用合适的Hint,知道加入Hint后的SQL能走出我们想要的执行计划。

然后对加入合适Hint,查询其Outline DATA部分的值,然后添加到Manual类型的SQL Profile中。

SQL> declare
  2  v_hints sys.sqlprof_attr;
  3  begin
  4  v_hints:=sys.sqlprof_attr(
  5  'BEGIN_OUTLINE_DATA',
  6  'IGNORE_OPTIM_EMBEDDED_HINTS',
  7  'OPTIMIZER_FEATURES_ENABLE("10.2.0.5")',
  8  'ALL_ROWS',
  9  'OUTLINE_LEAF(@"SEL$1")',
10  'INDEX(@"SEL$1" "T1"@"SEL$1" ("T1"."N"))',
11  'END_OUTLINE_DATA'
12  );
13  dbms_sqltune.import_sql_profile(
14  'select /*+ no_index(t1 idx_t1) */ * from t1 where n=3',
15  v_hints,'SQLPROFILE_001',
16  force_match=>true,replace=>false);
17  end;
18  /


PL/SQL procedure successfully completed.
SQL> select /*+ no_index(t1 idx_t1) */ * from t1 where n=2;
         N
----------
         2

manual类型同样也有FORCE_MATCH属性,我这里使用的是true,所以即使SQL中where部分不同也没有关系。

SQL> select * from table(dbms_xplan.display_cursor(null,null,'advanced'));
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID  c4j6hxkqudj1s, child number 1
-------------------------------------
select /*+ no_index(t1 idx_t1) */ * from t1 where n=2
Plan hash value: 1369807930
---------------------------------------------------------------------------
| Id  | Operation        | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT |        |       |       |     1 (100)|          |
|*  1 |  INDEX RANGE SCAN| IDX_T1 |     1 |     3 |     1   (0)| 00:00:01 |
---------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
   1 - SEL$1 / T1@SEL$1
Outline Data
-------------
  /*+
      BEGIN_OUTLINE_DATA
      IGNORE_OPTIM_EMBEDDED_HINTS
      OPTIMIZER_FEATURES_ENABLE('10.2.0.5')
      ALL_ROWS
      OUTLINE_LEAF(@"SEL$1")
      INDEX(@"SEL$1" "T1"@"SEL$1" ("T1"."N"))
      END_OUTLINE_DATA
  */
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - access("N"=2)
Column Projection Information (identified by operation id):
-----------------------------------------------------------
   1 - "N"[NUMBER,22]
Note
-----
   - SQL profile "SQLPROFILE_001" used for this statement
45 rows selected.

注意上面的Note部分,有记录使用了“SQL profile "SQLPROFILE_001" used for this statement

我们有一个更简单的方法创建SQL Profile,通过附件中的脚本。

附件:通过这个脚本可以生成SQL Profile


你可能感兴趣的:(sql,执行计划,profile)