總有一種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、在各類固定執行計劃都使用的狀況下,那種優先級更高?

相關文章
相關標籤/搜索