总有一种SQL执行计划绑定方式合适你

须要绑定SQL执行计划常见的几种状况:sql

SQL执行计划突变,致使数据库性能降低,从历史执行计划找一个合理的,进行绑定。数据库

SQL没法使用更优的执行计划,且无历史执行计划,可经过hint手工构造的方式,进行绑定。session

某些Bug引发优化器生成较差的执行计划。在bug修复前,进行绑定。oracle

 

ORACLE固定执行计划的3种方式:ide

Oracle 9i使用outline (可跨版本1011g都可使用)工具

Oracle 10g使用sql profile 11g也可以使用)性能

Oracle 11g使用sql plan manage测试

 

接下来简述如何使用这3种方式进行执行计划的固定,并举例说明3种固定执行计划的优缺点,经过对比选择合适的固定执行计划来应对不一样的业务场景。也就是什么场景下使用何种执行计划固定比较合适。优化

 

1、大纲(Stored Outlineui

一、当SQL执行计划因新版本变动,统计信息不许确,新建索引,参数改变等发生改变时,存储大纲可使SQL语句的执行计划保持不变。在建立某条语句的大纲时,ORACLE会将SQL语句的文本,执行计划和语句使用的hints存储在一个系统默认用户OUTLN的3个表OL$,OL$HINTS,OL$NODES上。

二、使用大纲(outline)固定执行计划

--环境构建,创建测试表
create table zw as select * fromdba_objects where object_id is not null;
explain plan for select count(*) from zw;
select * from table(dbms_xplan.display());
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 249608387
 
-------------------------------------------------------------------
| Id | Operation       | Name | Rows  | Cost (%CPU)| Time       |
-------------------------------------------------------------------
|   0| SELECT STATEMENT   |      |   1 |  312   (1)| 00:00:04 |
|   1|  SORT AGGREGATE    |            |   1 |          |            |
|   2|   TABLE ACCESS FULL| ZW   | 83926 |  312   (1)| 00:00:04 |
-------------------------------------------------------------------
 
Note
 
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
   -dynamic sampling used for this statement (level=2)
 
--建立大纲(全表扫描)
create or replace outline zwoutline forcategory mycate on select count(*) from zw;
--建立object_id列索引,将该列属性设置为非空
alter table zw modify object_id not null;  --索引不存储null值
create index idx_zw_obj_id onzw(object_id);
analyze table zw compute statistics;
explain plan for select count(*) from zw;
select * from table(dbms_xplan.display());
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 1836624960
 
-------------------------------------------------------------------------------
| Id | Operation          | Name               | Rows | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------
|   0| SELECT STATEMENT      |           |    1 |    50         (0)| 00:00:01 |
|   1|  SORT AGGREGATE       |                 |    1 |      |                |
|   2|   INDEX FAST FULL SCAN| IDX_ZW_OBJ_ID |79741 |    50        (0)| 00:00:01 |
-------------------------------------------------------------------------------
 
--使用大纲固定执行计划
alter system/session setuse_stored_outlines=mycate;
--固定执行计划以后,就会按照创大纲时的执行计划去执行。

 

上述的创建的大纲为公有大纲,为了避免影响其它用户的使用,能够创建私有大纲以下:

create or replace private outlinezwoutline2 for category mycate2 on select count(*) from zw;

 

思考:为何我构建测试时,固定的是全表扫描,而不是比较优化的索引扫描?

其实这里我想说明的是outline的缺点是比较死板的,当建立新的索引,或者数据量大幅度变化时是没法作出相应改变的,也就是说它是固定死的。

 

2、SQL_PROFILE

一、DBMS_SQLTUNE是10g引入的一个新特性,它能够经过自动优化性能较差SQL,并给出合理的优化建议,其中优化建议中的sql_profile文件它是一个存储在数据字典中的信息集合。sql_profile不包含单独的执行计划,提供数据库配置、绑定变量、优化统计信息、数据集等信息供优化器选择执行计划。这里不对SQL优化建议工具SQL Tuning Advisor STA)进行介绍,有兴趣的童鞋研究一下DBMS_SQLTUNE包。

二、使用coe_xfr_sql_profile.sql固定执行计划

--环境构建,创建测试表,与outline测试同样
SQL> create table zw as select * fromdba_objects where object_id is not null;
SQL> alter table zw modify object_id notnull;  --索引不存储null值
SQL> create index idx_zw_obj_id onzw(object_id);
SQL> analyze table zw computestatistics;
SQL> select count(*) from zw;  -- INDEX FAST FULLSCAN
 
 COUNT(*)
----------
    79741
 
SQL> select * fromtable(dbms_xplan.display_cursor(null,0));
 
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------
SQL_ID     1f5n0rapts695, child number 0
-------------------------------------
select count(*) from zw
 
Plan hash value: 1836624960
 
-------------------------------------------------------------------------------
| Id | Operation          | Name               | Rows | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------
|   0| SELECT STATEMENT      |           |      |    50 (100)|         |
|   1|  SORT AGGREGATE       |                 |    1 |      |                |
|   2|   INDEX FAST FULL SCAN| IDX_ZW_OBJ_ID |79741 |    50        (0)| 00:00:01 |
-------------------------------------------------------------------------------
 
SQL> select /*+ full(zw) */ count(*)from zw; --使用hint提示,强制走全表,生成一个执行计划
 
 COUNT(*)
----------
    79741
 
SQL> select * fromtable(dbms_xplan.display_cursor(null,0));
 
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------
SQL_ID     g5yd6rabqtdbp,child number 0
-------------------------------------
select /*+ full(zw) */ count(*) from zw
 
Plan hash value: 249608387
 
-------------------------------------------------------------------
| Id | Operation       | Name | Rows  | Cost (%CPU)| Time       |
-------------------------------------------------------------------
|   0| SELECT STATEMENT   |      |    |   312 (100)|      |
|   1|  SORT AGGREGATE    |            |   1 |          |            |
|   2|   TABLE ACCESS FULL| ZW   | 79741 |  312   (1)| 00:00:04 |
-------------------------------------------------------------------
SQL> @coe_xfr_sql_profile.sql
 
Parameter 1:
SQL_ID (required)
 
Enter value for 1: 1f5n0rapts695
 
 
PLAN_HASH_VALUE AVG_ET_SECS
--------------- -----------
    1836624960          .02
 
Parameter 2:
PLAN_HASH_VALUE (required)
 
Enter value for 2: 249608387
 
Values passed to coe_xfr_sql_profile:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SQL_ID            : "1f5n0rapts695"
PLAN_HASH_VALUE: "249608387"
 
Executecoe_xfr_sql_profile_1f5n0rapts695_249608387.sql
on TARGET system in order to create acustom SQL Profile
with plan 249608387 linked to adjustedsql_text.
 
 
COE_XFR_SQL_PROFILE completed.
 
SQL> explain plan for select count(*)from zw;
 
Explained.
 
SQL> select * fromtable(dbms_xplan.display());
 
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 249608387
 
-------------------------------------------------------------------
| Id | Operation       | Name | Rows  | Cost (%CPU)| Time       |
-------------------------------------------------------------------
|   0| SELECT STATEMENT   |      |   1 |  312   (1)| 00:00:04 |
|   1|  SORT AGGREGATE    |            |   1 |          |            |
|   2|   TABLE ACCESS FULL| ZW   | 79741 |  312   (1)| 00:00:04 |
-------------------------------------------------------------------
 
Note
 
PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------------
   -SQL profile "coe_1f5n0rapts695_249608387"used for this statement
 
SQL> selectname,category,status,sql_text from dba_sql_profiles;
 
NAME                                CATEGORY     STATUS  SQL_TEXT
------------------------------      ------------      -------------------------------------------
coe_1f5n0rapts695_249608387    DEFAULE   ENABLED  select count(*) from zw

使用coe_xfr_sql_profile.sql固定计划是否是很好用呢?是的,这一切都归功于oracle mos上的功劳,须要的童鞋能够到matelink上查找和下载。还有其它两个有关的脚本:coe_load_sql_baseline.sqlcoe_load_sql_profile.sql,有兴趣的童鞋能够一块儿下载研究。

 

思考:当在使用SQL_PROFILE绑定以前,使用了OUTLINE进行固定的话,谁的优先级高呢?

根据网上的一些资料说是OUTLINE的优先级最高,但都是简短的一句话,没有证实。但是通过我无数次的测试,发现都是SQL_PROFILE的优先级较高,具体相关测试结果我就不粘贴出来了。(或许是我测试语句的特殊性,需再进一步验证)

 

值得一提的是,sql_profile并不会以outline方式存储冻结执行计划,当表中数据增加或索引被删除或重建时,在sql_profile不变的状况下执行计划也能够发生变化,信息的存储和与数据的分布或者访问路径有关。

 

3、SQL PLANMANAGE

一、11g开始,oracle引入了SQL执行计划管理(SQLPlan Management)这个新特性,与Oracle 9i outline10g profile比,Oracle 11gSPM相对更加的灵活,容许你同时接受多个执行计划。

二、使用SQL PlanManagement固定执行计划

--一条带有绑定变量的SQL语句,但数据分布不均,严重倾斜时,最好的执行计划会根据绑定变量的值而不一样。执行时,根据不一样的变量值,SPM会花费不多的运算从中选择一条最合适的。
SQL> select id,count(*) from test groupby id order by 2;
 
         ID   COUNT(*)
---------- ----------
         10      1100
         88      10100
999  1000000
--接下来定义一个变量a,分别赋值999和10,看它的执行计划是如何的
SQL>alter system flush shared_pool;
SQL>var a1 number;
SQL>exec :a1:=999;
SQL>select t.* from test t wheret.id=:a1;
 
1000000 rows selected.
 
Elapsed: 00:00:25.30
 
SQL> select * fromtable(dbms_xplan.display_cursor(null,0));
 
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
SQL_ID     cpsdn05zdq02p,child number 0
-------------------------------------
select t.* from test t where t.id=:a1
 
Plan hash value: 1357081020
 
--------------------------------------------------------------------------
| Id | Operation      | Name | Rows | Bytes | Cost (%CPU)| Time   |
--------------------------------------------------------------------------
|   0| SELECT STATEMENT  |       |     |      |  424 (100)|      |
|*  1|  TABLE ACCESS FULL| TEST |   337K| 1316K|   424   (2)| 00:00:06 |
 
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
 
Predicate Information (identified byoperation id):
---------------------------------------------------
 
   1- filter("T"."ID"=:A1)
 
-##########################ID列上有个索引IDX_ID ################################
SQL>alter system flush shared_pool;
SQL>var a1 number;
SQL>exec :a1:=10;
SQL>select t.* from test t wheret.id=:a1;
1100 rows selected.
 
Elapsed: 00:00:00.04
 
SQL> select * fromtable(dbms_xplan.display_cursor(null,0));
 
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
SQL_ID     cpsdn05zdq02p,child number 0
-------------------------------------
select t.* from test t where t.id=:a1
 
Plan hash value: 1357081020
 
--------------------------------------------------------------------------
| Id | Operation      | Name | Rows | Bytes | Cost (%CPU)| Time   |
--------------------------------------------------------------------------
|   0| SELECT STATEMENT  |       |     |      |  424 (100)|      |
|*  1|  TABLE ACCESS FULL| TEST |   337K| 1316K|   424   (2)| 00:00:06 |
 
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
 
Predicate Information (identified byoperation id):
---------------------------------------------------
 
   1- filter("T"."ID"=:A1)
 
--这里能够看到,不管赋值是999仍是10,其执行计划都是同样的,但根据理论来讲,咱们都知道,id=10
时走索引效率是最好的。假设数据是均匀分布的那么基数评估cardinality=density*num_rows。Density可经过user_tab_col_statistics查询。
select column_name,num_distinct,densityfrom user_tab_col_statistics where table_name='TEST';
 
COLUMN_NAME                      NUM_DISTINCT    DENSITY
------------------------------    -------------   ---------
ID                                 3                .333333333
--咱们看到的Rows列预估的337k就是cardinality=density*num_rows=0.3333*1011200约等于337k行,
--可是咱们都知道ID=10只有1100行,而ID=999有1000000行,因此当ID=10的时候走索引全扫描,ID=999
--的时候走全表扫描是最合理的执行计划。那么面对这种状况,咱们该如何让这种状况下的执行计划达
--到最优呢?方法有以下几个:
--一、去除绑定变量,直接硬解析的方式(非理想的,若是涉及要该程序代码这是很不可取的)
--二、启用11g的新特性ACS(这个BUG不是通常的多,不建议启用)
--三、收集直方图信息(若是在生产高峰期,收集直方图信息所占资源没法评估)
--四、使用SPM把不一样的执行计划加入到SQLPlan Baseline中。
--使用手工捕获的方式
alter system flush shared_pool;
var a1 number;
exec :a1:=999;
select t.* from test t where t.id=:a1;
select * fromtable(dbms_xplan.display_cursor(null,0));
var temp varchar2(1000);
exec :temp:=dbms_spm.load_plans_from_cursor_cache(sql_id => 'cpsdn05zdq02p');
exec :temp :=dbms_spm.alter_sql_plan_baseline(sql_handle=>'SQL_d230ce970caa0077',plan_name=>'SQL_PLAN_d4c6fkw6an03r97bbe3d0',attribute_name=>'ENABLED',attribute_value=>'NO');  --先修改全表扫描的sql planbaselines的enabled属性为NO,否则捕获不了索引的。
exec :a1:=10;
select t.* from test t where t.id=:a1;
select * fromtable(dbms_xplan.display_cursor(null,0));
exec :temp:=dbms_spm.load_plans_from_cursor_cache(sql_id => 'cpsdn05zdq02p');
dbms_spm.alter_sql_plan_baseline(sql_handle=>'SQL_d230ce970caa0077',plan_name=>'SQL_PLAN_d4c6fkw6an03r97bbe3d0',attribute_name=>'ENABLED',attribute_value=>'YES');
 
SQL> select sql_handle,plan_name,origin,enabled,accepted,fixed from dba_sql_plan_baselines;
 
SQL_HANDLE        PLAN_NAME                        ORIGIN       ENA ACC FIX
-------------------------------------------------- -------------- --- --- ---
SQL_d230ce970caa0077SQL_PLAN_d4c6fkw6an03r97bbe3d0 MANUAL-LOAD   YES YES NO
SQL_d230ce970caa0077SQL_PLAN_d4c6fkw6an03rf98b55bb MANUAL-LOAD   YES YES NO
--验证结果:
SQL> var a1 number;
SQL> exec :a1:=10;
SQL> select t.* from test t wheret.id=:a1;
SQL> select * fromtable(dbms_xplan.display_cursor(null,0));
 
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
SQL_ID     cpsdn05zdq02p,child number 0
-------------------------------------
select t.* from test t where t.id=:a1
 
Plan hash value: 578627003
 
---------------------------------------------------------------------------
| Id | Operation     | Name   | Rows | Bytes | Cost (%CPU)| Time         |
---------------------------------------------------------------------------
|   0| SELECT STATEMENT |            |     |     |   5(100)|     |
|*  1|  INDEX RANGE SCAN| IDX_ID |  1280 | 5120 |  5   (0)| 00:00:01 |
 
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Predicate Information (identified byoperation id):
---------------------------------------------------
 
   1- access("T"."ID"=:A1)
 
Note
-----
   -SQL plan baseline SQL_PLAN_d4c6fkw6an03rf98b55bbused for this statement
 
22 rows selected.
 
SQL> var a1 number;
SQL> exec :a1:=999;
SQL> select t.* from test t wheret.id=:a1;
SQL> select * fromtable(dbms_xplan.display_cursor(null,0));
 
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
SQL_ID     cpsdn05zdq02p,child number 0
-------------------------------------
select t.* from test t where t.id=:a1
 
Plan hash value: 1357081020
 
--------------------------------------------------------------------------
| Id | Operation      | Name | Rows | Bytes | Cost (%CPU)| Time   |
--------------------------------------------------------------------------
|   0| SELECT STATEMENT  |       |     |      |  424 (100)|      |
|*  1|  TABLE ACCESS FULL| TEST |  1001K| 3912K|   424   (2)| 00:00:06 |
 
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Predicate Information (identified byoperation id):
---------------------------------------------------
 
   1- filter("T"."ID"=:A1)
 
Note
-----
   -SQL plan baseline SQL_PLAN_d4c6fkw6an03r97bbe3d0used for this statement
 
22 rows selected.


SPM的灵活之处在于,能够动态管理,不像存储大纲(stored outline)和SQL Profile须要DBA手工建立,固然SPM也能够,由于我在以上演示中也没让它自动捕获。

 

思考:1、何种状况下使用什么固定执行计划的方法更加有效?2、在各类固定执行计划都使用的状况下,那种优先级更高?

相关文章
相关标签/搜索