Chinaunix首页 | 论坛 | 博客
  • 博客访问: 245989
  • 博文数量: 50
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 533
  • 用 户 组: 普通用户
  • 注册时间: 2015-07-28 21:56
个人简介

活着,寻找生存。

文章分类

全部博文(50)

文章存档

2017年(1)

2016年(20)

2015年(29)

我的朋友

分类: Oracle

2015-11-21 18:05:18

oracle 11g及其以上得版本中,有如下两种方法可以产生目标sql的sql plan baseline:
  
 。自动捕获。
 。手工生成、批量导入(批量导入尤其适用于oracle数据库大版本的升级,它可以确保升级后原有系统所有sql得执行计划不会变更)。
  
我们来看一个自动捕获sql plan baseline并据此来稳定执行计划的实例。
  
oracle 11g中optimizer_capture_sql_plan_baselines的默认值为false,optimizer_use_sql_plan_baselines的默认值为true:
22:10:16 SQL> show parameter sql_plan
  
NAME                     TYPE     VALUE
------------------------------------ ----------- ------------------------------
optimizer_capture_sql_plan_baselines boolean     FALSE
optimizer_use_sql_plan_baselines     boolean     TRUE
  
在当前session中禁掉spm并同时开启自动捕获sql plan baseline:
  
22:16:51 SQL> alter session set optimizer_use_sql_plan_baselines=false;
  
Session altered.
  
Elapsed: 00:00:00.00
22:17:35 SQL> alter session set optimizer_capture_sql_plan_baselines=true;
  
Session altered.
  
Elapsed: 00:00:00.00
  
创建测试表cap_spm表:
22:17:53 SQL> create table cap_spm as select * from dba_objects;
  
Table created.
  
Elapsed: 00:00:00.92
22:19:21 SQL> create index idx_cs on cap_spm(object_id);
  
Index created.
  
Elapsed: 00:00:00.23
22:23:13 SQL> exec dbms_stats.gather_table_stats(ownname=>'SYS',tabname=>'CAP_SPM',estimate_percent=>100,cascade=>true);
  
PL/SQL procedure successfully completed.
  
Elapsed: 00:00:01.71
22:28:34 SQL>  select object_id,object_name from cap_spm where object_id between 20 and 28;
 OBJECT_ID OBJECT_NAME
---------- --------------------------------------------------------------------------------------------------------------------------------
    20 ICOL$
    21 COL$
    22 USER$
    23 PROXY_DATA$
    24 I_PROXY_DATA$
    25 PROXY_ROLE_DATA$
    26 I_PROXY_ROLE_DATA$_1
    27 I_PROXY_ROLE_DATA$_2
    28 CON$
  
9 rows selected.
  
Elapsed: 00:00:00.04
22:30:57 SQL>select * from table(dbms_xplan.display_cursor(null,null,'advanced'));
   
PLAN_TABLE_OUTPUT
--------------------------------------------------------------
SQL_ID    3v3s3h87rj717, child number 0
-------------------------------------
select object_id,object_name from cap_spm where object_id between 20
and 28
  
Plan hash value: 3149005651
  
---------------------------------------------------------------------------------------
| Id  | Operation            | Name    | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT        |          |       |       |     3 (100)|          |
|   1 |  TABLE ACCESS BY INDEX ROWID| CAP_SPM |     8 |   240 |     3    (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN        | IDX_CS  |     8 |       |     2    (0)| 00:00:01 |
---------------------------------------------------------------------------------------
  
从上述显示内容中可以看出,现在目标sql的执行计划走的是对索引IDX_CS的索引范围扫描。
  
因为目标sql只执行过一次,所以oracle现在不会自动捕获其sql plan baseline。从如下查询结果中可以看出,该sql限制确实没有对应的sql plan baseline:
  
22:37:33 SQL> select sql_handle,plan_name,origin,enabled,accepted,sql_text from dba_sql_plan_baselines where sql_text like 'select object_id,object_name from cap_spm where object_id between 20 and 28';
  
no rows selected
  
Elapsed: 00:00:00.03
22:37:46 SQL> 23:14:42 SQL> select object_id,object_name from cap_spm where object_id between 20 and 28;
  
 OBJECT_ID OBJECT_NAME
---------- --------------------------------------------------------------------------------------------------------------------------------
    20 ICOL$
    21 COL$
    22 USER$
    23 PROXY_DATA$
    24 I_PROXY_DATA$
    25 PROXY_ROLE_DATA$
    26 I_PROXY_ROLE_DATA$_1
    27 I_PROXY_ROLE_DATA$_2
    28 CON$
  
9 rows selected.
  
Elapsed: 00:00:00.06
  
其执行计划并未发现任何改变,走的还是对索引IDX_CS的索引范围扫描:
  
22:38:14 SQL> select * from table(dbms_xplan.display_cursor(null,null,'advanced'));
  
PLAN_TABLE_OUTPUT
--------------------------------------------------------------
SQL_ID    3v3s3h87rj717, child number 0
-------------------------------------
select object_id,object_name from cap_spm where object_id between 20
and 28
  
Plan hash value: 3149005651
  
---------------------------------------------------------------------------------------
| Id  | Operation            | Name    | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT        |          |       |       |     3 (100)|          |
|   1 |  TABLE ACCESS BY INDEX ROWID| CAP_SPM |     8 |   240 |     3    (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN        | IDX_CS  |     8 |       |     2    (0)| 00:00:01 |
---------------------------------------------------------------------------------------
  
因为目标sql已经重复执行,所以现在oracle就回自动捕获sql plan baseline了。从如下查询结果中可以看出,oracle已经针对上述执行计划(即对索引IDX_CS的索引范围扫描)产生了一个sql plan baseline,其enable和accepted的值均为“yes”:
  
22:38:45 SQL>select sql_handle,plan_name,origin,enabled,accepted,sql_text from dba_sql_plan_baselines where sql_text like 'select object_id,object_name from cap_spm where object_id between 20 and 28';
  
SQL_HANDLE               PLAN_NAME              ORIGIN         ENA ACC SQL_TEXT
------------------------------ ------------------------------ -------------- --- --- --------------------------------------------------------------------------------
SQL_592f279722dc064a           SQL_PLAN_5kbt7kwjds1kad7d172c1 AUTO-CAPTURE   YES YES select object_id,object_name from cap_spm where object_id be
                                             tween 20 and 28
   
Elapsed: 00:00:00.02
  
这里我们将索引IDX_CS的聚族因子修改为2400万,目的为了能让目标sql的执行计划为对表cap_spm的全部扫描:
  
22:46:30 SQL> exec dbms_stats.set_index_stats(ownname=>'SYS',indname=>'IDX_CS',clstfct=>24000000,no_invalidate=>false);
  
PL/SQL procedure successfully completed.
  
Elapsed: 00:00:00.02 
  
从如下查询结果里可以看出,索引idx_cs的聚族因子确实已修改为2400万:
  
22:48:31 SQL> select index_name,clustering_factor from dba_indexes where index_name='IDX_CS';
INDEX_NAME               CLUSTERING_FACTOR
------------------------------ -----------------
IDX_CS                    24000000
Elapsed: 00:00:00.09
  
重新执行目标sql:
  
22:48:39 SQL> select object_id,object_name from cap_spm where object_id between 20 and 28;
 OBJECT_ID OBJECT_NAME
---------- --------------------------------------------------------------------------------------------------------------------------------
    20 ICOL$
    28 CON$
    25 PROXY_ROLE_DATA$
    26 I_PROXY_ROLE_DATA$_1
    21 COL$
    23 PROXY_DATA$
    24 I_PROXY_DATA$
    22 USER$
    27 I_PROXY_ROLE_DATA$_2
  
9 rows selected.
  
Elapsed: 00:00:00.08
  
其执行计划为如下所示:
  
23:25:46 SQL> select * from table(dbms_xplan.display_cursor(null,null,'advanced'));
  
PLAN_TABLE_OUTPUT
--------------------------------------------------------------
SQL_ID    3v3s3h87rj717, child number 0
-------------------------------------
select object_id,object_name from cap_spm where object_id between 20
and 28
  
Plan hash value: 619725477
  
-----------------------------------------------------------------------------
| Id  | Operation      | Name    | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |        |        |        |    297 (100)|        |
|*  1 |  TABLE ACCESS FULL| CAP_SPM |      8 |    240 |    297   (1)| 00:00:04 |
-----------------------------------------------------------------------------
  
从上述显示内容中可以看出,现在该sql的执行计划已经从对索引IDX_CS的索引范围扫描变为对表cap_spm的全表扫描,即执行计划已经发生了变更。
  
因为目标sql已经重复执行且同时又产生了一个新的执行计划,索引现在oracle就回自动捕获并创建这个新的执行计划对应的sql plan baseline了。
  
从如下查询如果中可以看出,oracle已经针对上述执行计划(即对表cap_spm得全表扫描)产生了一个新的sql plan baseline,其enabled的值依然为yes,但accepted的值变为了“no”:
  
23:25:48 SQL> select sql_handle,plan_name,origin,enabled,accepted,sql_text from dba_sql_plan_baselines where sql_text like 'select object_id,object_name from cap_spm where object_id between 20 and 28';
  
SQL_HANDLE               PLAN_NAME              ORIGIN         ENA ACC SQL_TEXT
------------------------------ ------------------------------ -------------- --- --- --------------------------------------------------------------------------------
SQL_592f279722dc064a           SQL_PLAN_5kbt7kwjds1kad7d172c1 AUTO-CAPTURE   YES YES select object_id,object_name from cap_spm where object_id be
                                             tween 20 and 28
  
SQL_592f279722dc064a           SQL_PLAN_5kbt7kwjds1kaf4dd99e6 AUTO-CAPTURE   YES NO  select object_id,object_name from cap_spm where object_id be
                                             tween 20 and 28
  
Elapsed: 00:00:00.03
  
然后我们对当前session关闭自动捕获sql plan baseline并同时开启wpm,即相当于恢复了oracle11g中的默认设置:
  
23:35:40 SQL> alter session set optimizer_capture_sql_plan_baselines=false;
  
Session altered.
  
Elapsed: 00:00:00.00
23:35:53 SQL> alter session set optimizer_use_sql_plan_baselines=true;
  
Session altered.
  
Elapsed: 00:00:00.00
  
现在索引idx_cs的聚族因子依然是2400万:
  
23:36:06 SQL> select index_name,clustering_factor from dba_indexes where index_name='IDX_CS';
INDEX_NAME               CLUSTERING_FACTOR
------------------------------ -----------------
IDX_CS                    24000000
  
Elapsed: 00:00:00.11
  
再次执行目标sql:
  
23:37:10 SQL> select object_id,object_name from cap_spm where object_id between 20 and 28;
  
 OBJECT_ID OBJECT_NAME
---------- --------------------------------------------------------------------------------------------------------------------------------
    20 ICOL$
    21 COL$
    22 USER$
    23 PROXY_DATA$
    24 I_PROXY_DATA$
    25 PROXY_ROLE_DATA$
    26 I_PROXY_ROLE_DATA$_1
    27 I_PROXY_ROLE_DATA$_2
    28 CON$
  
9 rows selected.
  
Elapsed: 00:00:00.01
  
执行计划如下:
  
23:37:26 SQL> select * from table(dbms_xplan.display_cursor(null,null,'advanced'));
  
PLAN_TABLE_OUTPUT
---------------------------------------------------------------
SQL_ID    3v3s3h87rj717, child number 2
-------------------------------------
select object_id,object_name from cap_spm where object_id between 20
and 28
  
Plan hash value: 3149005651
  
---------------------------------------------------------------------------------------
| Id  | Operation            | Name    | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT        |          |       |       |  2520 (100)|          |
|   1 |  TABLE ACCESS BY INDEX ROWID| CAP_SPM |     8 |   240 |  2520    (1)| 00:00:31 |
|*  2 |   INDEX RANGE SCAN        | IDX_CS  |     8 |       |     2    (0)| 00:00:01 |
---------------------------------------------------------------------------------------
  
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
,,,,,,,,,,,,
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------
   2 - "CAP_SPM".ROWID[ROWID,10], "OBJECT_ID"[NUMBER,22]
  
Note
-----
   - SQL plan baseline SQL_PLAN_5kbt7kwjds1kad7d172c1 used for this statement
  
50 rows selected.
  
Elapsed: 00:00:00.12
  
从上述显示内容中可以看出,现在目标sql的执行计划已经从对表cap_spm的全表扫描恢复为对表索引idx_cs的索引范围扫描。注意到上述执行计划中的note部分有如下内容:”SQL plan baseline SQL_PLAN_5kbt7kwjds1kad7d172c1 used for this statement”,这表明在wpm以开启的情况下,即使目标sql产生了新的执行计划,oracle依然只会应用该sql的enabled和accepted的值均为”yes“的sql plan baseline。
  
 从如下查询结果中可以看出,目标sql所应用的sql plan baseline名为SQL_PLAN_5kbt7kwjds1kad7d172c1,所对应的enabled和accepted的值确实是”yes“(它就是目标sql第一次执行时走索引范围扫描的执行计划所对应的sql plan baseline):
  
23:37:34 SQL> select sql_handle,plan_name,origin,enabled,accepted,sql_text from dba_sql_plan_baselines where sql_text like 'select object_id,object_name from cap_spm where object_id between 20 and 28';
  
SQL_HANDLE               PLAN_NAME              ORIGIN         ENA ACC SQL_TEXT
------------------------------ ------------------------------ -------------- --- --- --------------------------------------------------------------------------------
SQL_592f279722dc064a           SQL_PLAN_5kbt7kwjds1kad7d172c1 AUTO-CAPTURE   YES YES select object_id,object_name from cap_spm where object_id be
                                             tween 20 and 28
  
SQL_592f279722dc064a           SQL_PLAN_5kbt7kwjds1kaf4dd99e6 AUTO-CAPTURE   YES NO  select object_id,object_name from cap_spm where object_id be
                                             tween 20 and 28
Elapsed: 00:00:00.01
   
从上述测试结果中可以很清晰地看到,spm确实能够稳定目标sql的执行计划。当启用了spm后,它确实能够保证只有被验证过的执行计划(即enabled和accepted的值均为”yes“的sql plan baseline所对应的执行计划)才会被启用,当由于某种原因(比如统计信息的变更)而导致目标sql产生了新的执行计划后,这个新的执行计划并不会被oracle启用。
  
如果想启用目标sql新的执行计划(即对表cap_spm得全表扫描),应该如何做呢?
  
针对不同的oracle数据库版本,会有不同的处理办法。比如这里想启用目标sql新的执行计划(即对表cap_spm得全表扫描),如果是oracle11gr1的环境,则只需将目标sql所采用的名为SQL_PLAN_5kbt7kwjds1kad7d172c1的sql plan baseline(即对索引idx_cs的索引范围扫描所对应的sql plan baseline)的accepted值设为”no”就可以了。但遗憾的是,上述sql的执行环境是oracle 11gr2,所以以如下方式执行dbms_spm.alter_sql_plan_baseline时oracle会报错(因为在oracle11gr2中,所有已被accepted的sql plan baseline的accepted值将不再能够被设置为“no”):
  
23:55:55 SQL> exec :temp:=dbms_spm.alter_sql_plan_baseline(sql_handle=>'SQL_592f279722dc064a',plan_name=>'SQL_PLAN_5kbt7kwjds1kad7d172c1',attribute_name=>'accepted',attribute_value=>'NO');
BEGIN :temp:=dbms_spm.alter_sql_plan_baseline(sql_handle=>'SQL_592f279722dc064a',plan_name=>'SQL_PLAN_5kbt7kwjds1kad7d172c1',attribute_name=>'accepted',attribute_value=>'NO'); END;
  
*
ERROR at line 1:
ORA-38136: invalid attribute name ACCEPTED specified
ORA-06512: at "SYS.DBMS_SPM", line 2532
ORA-06512: at line 1
  
  
Elapsed: 00:00:00.12
  
在oracle11gr2中,我们可以联合使用dbms_spm.evolve_sql_plan_baseline和dbms_spm.alter_sql_plan_baseline达到启用目标sql新的执行计划的目的。
  
先使用dbms_spm.evolve_sql_plan_baseline将目标sql新的执行计划(即对表cap_spm的全表扫描)所对应的名为SQL_PLAN_5kbt7kwjds1kaf4dd99e6的sql plan baseline的accepted值设为“yes”:
  
00:01:40 SQL> exec :temp:=dbms_spm.evolve_sql_plan_baseline(sql_handle=>'SQL_592f279722dc064a',plan_name=>'SQL_PLAN_5kbt7kwjds1kaf4dd99e6',verify=>'NO',commit=>'YES');
  
PL/SQL procedure successfully completed.
  
Elapsed: 00:00:00.04
  
从如下查询结果可以看到,名为SQL_PLAN_5kbt7kwjds1kaf4dd99e6的sql plan baseline的accepted值确实已经从之前的“no”变为了“yes”
  
00:03:47 SQL> select sql_handle,plan_name,origin,enabled,accepted,sql_text from dba_sql_plan_baselines where sql_text like 'select object_id,object_name from cap_spm where object_id between 20 and 28';
  
SQL_HANDLE               PLAN_NAME              ORIGIN         ENA ACC SQL_TEXT
------------------------------ ------------------------------ -------------- --- --- --------------------------------------------------------------------------------
SQL_592f279722dc064a           SQL_PLAN_5kbt7kwjds1kad7d172c1 AUTO-CAPTURE   YES YES select object_id,object_name from cap_spm where object_id be
                                             tween 20 and 28
  
SQL_592f279722dc064a           SQL_PLAN_5kbt7kwjds1kaf4dd99e6 AUTO-CAPTURE   YES YES select object_id,object_name from cap_spm where object_id be
                                             tween 20 and 28
  
  
Elapsed: 00:00:00.01
   
然后我们再使用dbms_spm.alter_sql_plan_baseline将原先的执行计划(即对索引idx_cs的索引范围扫描)所对应的sql plan baseline的enabled的值设为“no”:
  
00:03:50 SQL> exec :temp:=dbms_spm.alter_sql_plan_baseline(sql_handle=>'SQL_592f279722dc064a',plan_name=>'SQL_PLAN_5kbt7kwjds1kad7d172c1',attribute_name=>'ENABLED',attribute_value=>'NO');
  
PL/SQL procedure successfully completed.
  
Elapsed: 00:00:00.02
  
从如下查询结果中可以看出,原先的执行计划(即对索引idx_cs的索引范围扫描)所对应得sql plan baseline(即SQL_PLAN_5kbt7kwjds1kad7d172c1)的enabled值确实从之前的“yes“变为了”no“:
  
00:07:57 SQL> select sql_handle,plan_name,origin,enabled,accepted,sql_text from dba_sql_plan_baselines where sql_text like 'select object_id,object_name from cap_spm where object_id between 20 and 28';
  
SQL_HANDLE               PLAN_NAME              ORIGIN         ENA ACC SQL_TEXT
------------------------------ ------------------------------ -------------- --- --- --------------------------------------------------------------------------------
SQL_592f279722dc064a           SQL_PLAN_5kbt7kwjds1kad7d172c1 AUTO-CAPTURE   NO  YES select object_id,object_name from cap_spm where object_id be
                                             tween 20 and 28
  
SQL_592f279722dc064a           SQL_PLAN_5kbt7kwjds1kaf4dd99e6 AUTO-CAPTURE   YES YES select object_id,object_name from cap_spm where object_id be
                                             tween 20 and 28
  
  
Elapsed: 00:00:00.01
  
再次执行目标sql:
  
00:11:05 SQL> select object_id,object_name from cap_spm where object_id between 20 and 28;
  
 OBJECT_ID OBJECT_NAME
---------- --------------------------------------------------------------------------------------------------------------------------------
    20 ICOL$
    28 CON$
    25 PROXY_ROLE_DATA$
    26 I_PROXY_ROLE_DATA$_1
    21 COL$
    23 PROXY_DATA$
    24 I_PROXY_DATA$
    22 USER$
    27 I_PROXY_ROLE_DATA$_2
  
9 rows selected.
  
Elapsed: 00:00:00.02
  
执行计划如下:
  
00:11:09 SQL> select * from table(dbms_xplan.display_cursor(null,null,'advanced'));
  
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------
SQL_ID    3v3s3h87rj717, child number 1
-------------------------------------
select object_id,object_name from cap_spm where object_id between 20
and 28
  
Plan hash value: 619725477
  
-----------------------------------------------------------------------------
| Id  | Operation      | Name    | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |        |        |        |    297 (100)|        |
|*  1 |  TABLE ACCESS FULL| CAP_SPM |      8 |    240 |    297   (1)| 00:00:04 |
-----------------------------------------------------------------------------
。。。。。。。。。。
  
Note
  
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------
-----
   - SQL plan baseline SQL_PLAN_5kbt7kwjds1kaf4dd99e6 used for this statement
  
  
47 rows selected.
  
Elapsed: 00:00:00.04
  
从上述显示内容中可以看出,现在sql的执行计划已经对索引idx_cs的索引范围扫描变成了对表cap_spm的全表扫描,即我们要启用新的执行计划(对表cap_spm的全表扫描)的目的已经实现。注意到执行计划中的note部分如下内容:”SQL plan baseline SQL_PLAN_5kbt7kwjds1kaf4dd99e6 used for this statement“,这表明目标sql新的执行计划所对应的sql plan baseline已经被成功启用。
  
从上述测试结果中可以看出,实际上我们可以轻易地在目标sql的多个执行计划中切换,索引spm确实是既能够主动地稳定执行计划,又保留了继续是欧诺个新的执行计划的计划,并且我们很容易能够启用新的执行计划。

参考文献:【基于oracle的sql优化】---崔华
阅读(1412) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~