執行計劃繫結
需要繫結SQL執行計劃常見的幾種情況:
SQL執行計劃突變,導致資料庫效能下降,從歷史執行計劃找一個合理的,進行繫結。
SQL無法使用更優的執行計劃,且無歷史執行計劃,可透過hint手工構造的方式,進行繫結。
某些Bug引起最佳化器生成較差的執行計劃。在bug修復前,進行繫結。
ORACLE固定執行計劃的3種方式:
Oracle 9i使用outline (可跨版本10,11g均可使用)
Oracle 10g使用sql profile (11g也可使用)
Oracle 11g使用sql plan manage
接下來簡述如何使用這3種方式進行執行計劃的固定,並舉例說明3種固定執行計劃的優缺點,透過對比選擇合適的固定執行計劃來應對不同的業務場景。也就是什麼場景下使用何種執行計劃固定比較合適。
一、大綱(Stored Outline)
語法:(手動建立outline)
CREATE [ OR REPLACE ]
[ PUBLIC | PRIVATE ] OUTLINE [ outline ]
[ FROM [ PUBLIC | PRIVATE ] source_outline ]
[ FOR CATEGORY category ]
[ ON statement ] ;
1、當SQL執行計劃因新版本變更,統計資訊不準確,新建索引,引數改變等發生改變時,儲存大綱可以使SQL語句的執行計劃保持不變。在建立某條語句的大綱時,ORACLE會將SQL語句的文字,執行計劃和語句使用的hints儲存在一個系統預設使用者OUTLN的3個表OL$,OL$HINTS,OL$NODES上。
2、使用大綱(outline)固定執行計劃
--環境構建,建立測試表
SQL> create table zw as select * from dba_objects where object_id is not null;
Table created.
SQL> explain plan for select count(*) from zw;
Explained.
SQL> set lines 200
SQL> select * from table(dbms_xplan.display());
PLAN_TABLE_OUTPUT
---------------------------------------------------------------------------------------------------
Plan hash value: 249608387
-------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
-------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 339 (1)| 00:00:05 |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | TABLE ACCESS FULL| ZW | 100K| 339 (1)| 00:00:05 |
-------------------------------------------------------------------
Note
-----
- dynamic sampling used for this statement (level=2)
13 rows selected.
--查詢資料字典dba_outlines:
SQL> select NAME,OWNER,CATEGORY,USED,SQL_TEXT,ENABLED,TIMESTAMP from dba_outlines;
no rows selected
--建立大綱(全表掃描),預設是私有outline
SQL> create or replace outline zwoutline for category mycate on select count(*) from zw;
Outline created.
--再次查詢dba_outlines;
col NAME for a10
col OWNER for a10
col CATEGORY for a10
col SQL_TEXT for a30
select NAME,OWNER,CATEGORY,USED,SQL_TEXT,ENABLED,TIMESTAMP from dba_outlines;
NAME OWNER CATEGORY USED SQL_TEXT ENABLED TIMESTAMP
---------- ---------- ---------- ------ ------------------------------ -------- -------------------
ZWOUTLINE SYS MYCATE UNUSED select count(*) from zw ENABLED 2017-09-02 15:36:33
--建立object_id列索引,將該列屬性設定為非空
--索引不儲存null值
SQL> alter table zw modify object_id not null;
Table altered.
SQL> create index idx_zw_obj_id on zw(object_id);
Index created.
SQL> analyze table zw compute statistics;
Table analyzed.
SQL> explain plan for select count(*) from zw;
Explained.
SQL> select * from table(dbms_xplan.display());
PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------------------------------------------------------------
Plan hash value: 1836624960
-------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
-------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 54 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | INDEX FAST FULL SCAN| IDX_ZW_OBJ_ID | 100K| 54 (0)| 00:00:01 |
-------------------------------------------------------------------------------
--使用大綱固定執行計劃:(alter system/session set use_stored_outlines=mycate;)系統級或會話級別;
SQL> alter system set use_stored_outlines=mycate;
System altered.
--查詢dba_ouitlines(sql還未應用)
SQL> select NAME,OWNER,CATEGORY,USED,SQL_TEXT,ENABLED,TIMESTAMP from dba_outlines;
NAME OWNER CATEGORY USED SQL_TEXT ENABLED TIMESTAMP
---------- ---------- ---------- ------ ------------------------------ -------- -------------------
ZWOUTLINE SYS MYCATE UNUSED select count(*) from zw ENABLED 2017-09-02 15:36:33
--固定執行計劃之後,就會按照創大綱時的執行計劃去執行。
實際執行驗證:(實際執行後就會應用outline)
SQL> select count(*) from zw;
COUNT(*)
----------
87036
SQL> select * from table(dbms_xplan.display_cursor(null, null, 'advanced'));
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 | | | 54 (100)| |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | INDEX FAST FULL SCAN| IDX_ZW_OBJ_ID | 87036 | 54 (0)| 00:00:01 |
-------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
1 - SEL$1
2 - SEL$1 / ZW@SEL$1
Outline Data
-------------
/*+
BEGIN_OUTLINE_DATA
IGNORE_OPTIM_EMBEDDED_HINTS
OPTIMIZER_FEATURES_ENABLE('11.2.0.4')
DB_VERSION('11.2.0.4')
ALL_ROWS
OUTLINE_LEAF(@"SEL$1")
INDEX_FFS(@"SEL$1" "ZW"@"SEL$1" ("ZW"."OBJECT_ID"))
END_OUTLINE_DATA
*/
Column Projection Information (identified by operation id):
-----------------------------------------------------------
1 - (#keys=0) COUNT(*)[22]
39 rows selected.
--查詢dba_outlines
select NAME,OWNER,CATEGORY,USED,SQL_TEXT,ENABLED,TIMESTAMP from dba_outlines;
NAME OWNER CATEGORY USED SQL_TEXT ENABLED TIMESTAMP
---------- ---------- ---------- ------ ------------------------------ -------- -------------------
ZWOUTLINE SYS MYCATE UNUSED select count(*) from zw ENABLED 2017-09-02 15:54:31
上述的建立的大綱為公有大綱,為了不影響其它使用者的使用,可以建立私有大綱如下:
create or replace private outline zwoutline2 for category mycate2 on select count(*) from zw;
思考:為什麼我構建測試時,固定的是全表掃描,而不是比較最佳化的索引掃描?
其實這裡我想說明的是outline的缺點是比較死板的,當建立新的索引,或者資料量大幅度變化時是無法做出相應改變的,也就是說它是固定死的。
關於outline具體參考如下連結:
http://blog.csdn.net/whiteoldbig/article/details/17210079
- 這種方法不能指定outline的具體名字,由系統自動生成,可以透過alter outline SYS_OUTLINE_14061114223605901 rename to dh_test2修改
- 不指定類別的話預設為default,而且此處建立時,不能指定為default類別(會報錯)。
- 我們使用outline固定執行計劃時,一般都是選用此種方法
參考:
實驗如下:
SQL> create table dh_stat as select rownum id ,object_name name ,object_type type from dba_objects;
Table created.
SQL> create index ind_1 on dh_stat(id) compute statistics;
Index created.
SQL> alter system flush shared_pool;
System altered.
SQL> exec DBMS_STATS.GATHER_TABLE_STATS(OWNNAME=>'sys',TABNAME=>'dh_stat',ESTIMATE_PERCENT=>30,METHOD_OPT=>'FOR ALL COLUMNS SIZE 1',NO_INVALIDATE=>FALSE,CASCADE=>TRUE,DEGREE => 1);
PL/SQL procedure successfully completed.
SQL> col name format a15
SQL> col name format a30
SQL> col sql_text for a55
SQL> select /* outlinetest1 */ * from dh_stat where id=771;
ID NAME TYPE
---------- ------------------------------ -------------------
771 SYS_IL0000000772C00002$$ INDEX
SQL> set lines 200
SQL> select sql_text,sql_id,hash_value,child_number from v$sql a where sql_text like '%outlinetest1%' and sql_text not like '%v$sql%';
SQL_TEXT SQL_ID HASH_VALUE CHILD_NUMBER
------------------------------------------------------- ------------- ---------- ------------
select /* outlinetest1 */ * from dh_stat where id=771 9a69w18a8uuhk 344812050 0
SQL> select * from table(dbms_xplan.display_cursor('9a69w18a8uuhk',null));
PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------------
SQL_ID 9a69w18a8uuhk, child number 0
-------------------------------------
select /* outlinetest1 */ * from dh_stat where id=771
Plan hash value: 2780970545
---------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 2 (100)| |
| 1 | TABLE ACCESS BY INDEX ROWID| DH_STAT | 1 | 39 | 2 (0)| 00:00:01 |
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------------
|* 2 | INDEX RANGE SCAN | IND_1 | 1 | | 1 (0)| 00:00:01 |
---------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("ID"=771)
19 rows selected.
---根據sql已經有的執行計劃生成outline:
SQL> exec DBMS_OUTLN.create_outline(hash_value=>344812050,child_number => 0,category=>'TEST');
PL/SQL procedure successfully completed.
--查詢outline情況:
SQL> select name,category,used,sql_text from dba_outlines;
NAME CATEGORY USED SQL_TEXT
------------------------------ ------------------------------ ------ -------------------------------------------------------
SYS_OUTLINE_17090216454529101 TEST UNUSED select /* outlinetest1 */ * from dh_stat where id=771
此處outline的USED狀態沒有改變,因為我們沒有啟用TEST類別的outline,再次申明,outline必須透過use_stored_outlines引數啟用後,最佳化器才會使用outline
---下面這一步啟用TEST類別的OUTLINE,也可以在系統級啟用OUTLINE
SQL> alter session set use_stored_outlines=TEST;
驗證省略。。。。。。。。。。。
二、SQL_PROFILE
1、DBMS_SQLTUNE是10g引入的一個新特性,它可以透過自動最佳化效能較差SQL,並給出合理的最佳化建議,其中最佳化建議中的sql_profile檔案它是一個儲存在資料字典中的資訊集合。sql_profile不包含單獨的執行計劃,提供資料庫配置、繫結變數、最佳化統計資訊、資料集等資訊供最佳化器選擇執行計劃。這裡不對SQL最佳化建議工具SQL Tuning Advisor STA)進行介紹,有興趣的童鞋研究一下DBMS_SQLTUNE包。
語法:
SQL text format:
DBMS_SQLTUNE.CREATE_TUNING_TASK( sql_text IN CLOB, bind_list IN sql_binds := NULL, user_name IN VARCHAR2 := NULL, scope IN VARCHAR2 := SCOPE_COMPREHENSIVE, time_limit IN NUMBER := TIME_LIMIT_DEFAULT, task_name IN VARCHAR2 := NULL, description IN VARCHAR2 := NULL) RETURN VARCHAR2;
SQL ID format:
DBMS_SQLTUNE.CREATE_TUNING_TASK( sql_id IN VARCHAR2, plan_hash_value IN NUMBER := NULL, scope IN VARCHAR2 := SCOPE_COMPREHENSIVE, time_limit IN NUMBER := TIME_LIMIT_DEFAULT, task_name IN VARCHAR2 := NULL, description IN VARCHAR2 := NULL) RETURN VARCHAR2;
Workload Repository format:
DBMS_SQLTUNE.CREATE_TUNING_TASK( begin_snap IN NUMBER, end_snap IN NUMBER, sql_id IN VARCHAR2, plan_hash_value IN NUMBER := NULL, scope IN VARCHAR2 := SCOPE_COMPREHENSIVE, time_limit IN NUMBER := TIME_LIMIT_DEFAULT, task_name IN VARCHAR2 := NULL, description IN VARCHAR2 := NULL) RETURN VARCHAR2; 實驗1:sql_id format 1. sys使用者建立oracle最佳化任務(v_sqlid,task_name根據實際情況改變) set long 10000000 longchunksize 1000000 linesize 150 pagesize 0 serveroutput on size 1000000 verify off declare my_task_name varchar2(30); v_sqlid varchar2(50); begin v_sqlid:='11wrxmug9y4a7'; my_task_name := dbms_sqltune.create_tuning_task (sql_id=> v_sqlid, scope => 'comprehensive', time_limit=>160, task_name=>'task_00000', description => 'tuning task'); dbms_sqltune.execute_tuning_task('task_00000'); end; / 注:dbms_sqltune.execute_tuning_task('task_00000'),是執行最佳化任務 2. 列印最佳化任務,裡面有一些具體的改進措施 select dbms_sqltune.report_tuning_task('task_00000') from dual; 3.根據最佳化建議,繫結profile,理論上可提高語句執行效率 execute dbms_sqltune.accept_sql_profile(task_name => 'task_00000',task_owner => 'sys', replace => true); 實驗2:sql_text format SQL> alter session set statistics_level=all; Session altered. SQL> set serveroutput off SQL> select * from scott.emp where ename='SCOTT' and DEPTNO=20; EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO ---------- ---------- --------- ---------- ------------------- ---------- ---------- ---------- 7788 SCOTT ANALYST 7566 1987-04-19 00:00:00 3000 20 SQL> SELECT * FROM table(dbms_xplan.display_cursor(NULL,NULL,'runstats_last')); PLAN_TABLE_OUTPUT -------------------------------------------------------------------------------- SQL_ID 4dsqbp572auuu, child number 0 ------------------------------------- select * from scott.emp where ename='SCOTT' and DEPTNO=20 Plan hash value: 3956160932 ------------------------------------------------------------------------------------ | Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | ------------------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.01 | 7 | |* 1 | TABLE ACCESS FULL| EMP | 1 | 1 | 1 |00:00:00.01 | 7 | PLAN_TABLE_OUTPUT --------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter(("ENAME"='SCOTT' AND "DEPTNO"=20)) 18 rows selected. SQL> DECLARE my_task_name VARCHAR2(30); my_sqltext CLOB; BEGIN my_sqltext := 'select * from scott.emp where ename= :name and DEPTNO= :deptno'; my_task_name := DBMS_SQLTUNE.CREATE_TUNING_TASK( sql_text => my_sqltext, bind_list => sql_binds(anydata.convertvarchar2(10),anydata.convertnumber(2)), user_name => 'SYS', scope => 'COMPREHENSIVE', time_limit => 60, task_name => 'test_sql_tuning', description => 'Task to tune a query on emp'); END; / PL/SQL procedure successfully completed. 引數說明: bind_list:多個繫結變數以','逗號分隔。引數值一定要根據繫結變數對應的列的型別書寫.如:emp.ename型別是VARCHAR2(10),那麼就要寫成 bind_list =>sql_binds(anydata.convertvarchar2(10)), time_limit:執行的最長時間,預設是60。 scope:LIMITED,用大概1秒時間去最佳化SQL語句,但是並不進行SQL Profiling分析;COMPREHENSIVE,進行全面分析,包含SQL Profiling分析;比LIMITED用時更長。 --檢視任務名 SQL> select task_name from dba_advisor_log where task_name='test_sql_tuning'; TASK_NAME ------------------------------ test_sql_tuning --執行sql tuning任務 SQL> exec DBMS_SQLTUNE.EXECUTE_TUNING_TASK( task_name => 'test_sql_tuning' ); PL/SQL procedure successfully completed. --檢視sql tunning任務狀態 SQL> select task_name,status from dba_advisor_log where task_name='test_sql_tuning'; TASK_NAME STATUS ------------------------------ ----------- test_sql_tuning COMPLETED ----展示sql tunning結果 set long 10000 set longchunksize 1000 set linesize 100 select dbms_sqltune.report_tuning_task('test_sql_tuning') from dual; ...........省略 --根據建議accept_sql_profile execute dbms_sqltune.accept_sql_profile(.....) --完成後刪除sql tunning任務 exec dbms_sqltune.drop_tuning_task('test_sql_tuning'); --檢視SQL Tuning Advisor的進展(task執行很久) set lines 200 col opname for a20 col ADVISOR_NAME for a20 select sid,serial#,username,opname,advisor_name,target_desc,start_time sofar,totalwork from v$advisor_progress where username = 'SYS';
三、coe_xfr_sql_profile.sql固定執行計劃(也是生成sql_profile)
--環境構建,建立測試表,與outline測試一樣
create table zw as select * from dba_objects where object_id is not null;
alter table zw modify object_id not null; --索引不儲存null值
create index idx_zw_obj_id on zw(object_id);
analyze table zw compute statistics;
select count(*) from zw;
COUNT(*)
----------
87038
SQL> select * from table(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 | | | 54 (100)| |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | INDEX FAST FULL SCAN| IDX_ZW_OBJ_ID | 87038 | 54 (0)| 00:00:01 |
-------------------------------------------------------------------------------
--使用hint提示,強制走全表,生成一個執行計劃:
SQL> select /*+ full(zw) */ count(*)from zw;
COUNT(*)
----------
87038
SQL> select * from table(dbms_xplan.display_cursor(null,0));
PLAN_TABLE_OUTPUT
---------------------------------------------------------------------
SQL_ID fp5ksbhww4594, child number 0
-------------------------------------
select /*+ full(zw) */ count(*)from zw
Plan hash value: 249608387
-------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
-------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | 339 (100)| |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | TABLE ACCESS FULL| ZW | 87038 | 339 (1)| 00:00:05 |
-------------------------------------------------------------------
14 rows selected.
--執行coe_xfr_sql_profile指令碼:(固定執行計劃)
SQL> @coe_xfr_sql_profile.sql
Parameter 1:
SQL_ID (required)
Enter value for 1: 1f5n0rapts695 --原sql的sql_id
PLAN_HASH_VALUE AVG_ET_SECS
--------------- -----------
1836624960 .02
Parameter 2:
PLAN_HASH_VALUE (required)
Enter value for 2: 249608387 --想要選擇的執行計劃的PLAN_HASH_VALUE值
Values passed to coe_xfr_sql_profile:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SQL_ID : "1f5n0rapts695"
PLAN_HASH_VALUE: "249608387"
SQL>BEGIN
2 IF :sql_text IS NULL THEN
3 RAISE_APPLICATION_ERROR(-20100, 'SQL_TEXT for SQL_ID &&sql_id. was not found in memory (gv$sqltext_with_newlines) or AWR (dba_hist_sqltext).');
4 END IF;
5 END;
6 /
SQL>SET TERM OFF;
SQL>BEGIN
2 IF :other_xml IS NULL THEN
3 RAISE_APPLICATION_ERROR(-20101, 'PLAN for SQL_ID &&sql_id. and PHV &&plan_hash_value. was not found in memory (gv$sql_plan) or AWR (dba_hist_sql_plan).');
4 END IF;
5 END;
6 /
SQL>SET TERM OFF;
Execute coe_xfr_sql_profile_1f5n0rapts695_249608387.sql
on TARGET system in order to create a custom SQL Profile
with plan 249608387 linked to adjusted sql_text.
COE_XFR_SQL_PROFILE completed.
SQL> @coe_xfr_sql_profile_1f5n0rapts695_249608387.sql
省略.................
coe_xfr_sql_profile_1f5n0rapts695_249608387 completed
SQL> explain plan for select count(*) from zw;
Explained.
SQL> 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 | 87038 | 312 (1)| 00:00:04 |
-------------------------------------------------------------------
Note
PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------------
-SQL profile "coe_1f5n0rapts695_249608387"used for this statement
--查詢:
SQL> select name,category,status,sql_text from dba_sql_profiles;
NAME CATEGORY STATUS SQL_TEXT
------------------------------ ------------ -------------------------------------------
coe_1f5n0rapts695_249608387 DEFAULE ENABLED select count(*) from zw
當存在只有一個執行計劃時,而又不想讓應用不改sql程式碼的情況下改變執行計劃,請參照自動/手動型別sql_profile(dbms_sqltune/coe_xfr_sql_profile.sql)
使用coe_xfr_sql_profile.sql固定計劃是不是很好用呢?是的,這一切都歸功於oracle mos上的功勞,需要的童鞋可以到matelink上查詢和下載。還有其它兩個有關的指令碼:coe_load_sql_baseline.sql,coe_load_sql_profile.sql,有興趣的童鞋可以一起下載研究。
思考:當在使用SQL_PROFILE繫結之前,使用了OUTLINE進行固定的話,誰的優先順序高呢?
根據網上的一些資料說是OUTLINE的優先順序最高,但都是簡短的一句話,沒有證明。可是經過我無數次的測試,發現都是SQL_PROFILE的優先順序較高,具體相關測試結果我就不貼上出來了。(或許是我測試語句的特殊性,需再進一步驗證)
值得一提的是,sql_profile並不會以outline方式儲存凍結執行計劃,當表中資料增長或索引被刪除或重建時,在sql_profile不變的情況下執行計劃也可以發生變化,資訊的儲存和與資料的分佈或者訪問路徑有關。
四、SQL PLAN MANAGE(baseline)
1、從11g開始,oracle引入了SQL執行計劃管理(SQLPlan Management)這個新特性,與Oracle 9i 的outline和10g 的profile相比,Oracle 11g的SPM相對更加的靈活,允許你同時接受多個執行計劃。
2、使用SQL PlanManagement固定執行計劃
--一條帶有繫結變數的SQL語句,但資料分佈不均,嚴重傾斜時,最好的執行計劃會根據繫結變數的值而不同。執行時,根據不同的變數值,SPM會花費很少的運算從中選擇一條最合適的。
SQL> select id,count(*) from test group by 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 * from table(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,density from 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的時候走全表掃描是最合理的執行計劃。
那麼面對這種情況,我們該如何讓這種情況下的執行計劃達到最優呢?方法有如下幾個:
--1、去除繫結變數,直接硬解析的方式(非理想的,如果涉及要該程式程式碼這是很不可取的)
--2、啟用11g的新特性ACS(自適應遊標,這個BUG不是一般的多,不建議啟用)
--3、收集直方圖資訊(如果在生產高峰期,收集直方圖資訊所佔資源無法評估)
--4、使用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、在各種固定執行計劃都使用的情況下,那種優先順序更高?
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31397003/viewspace-2144265/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Oracle-繫結執行計劃Oracle
- sqm執行計劃的繫結
- 知識篇 | ORACLE 如何執行計劃繫結Oracle
- [20231210]執行計劃與繫結變數.txt變數
- sqlprofile繫結執行計劃實驗測試SQL
- Oracle利用coe_load_sql_profile指令碼繫結執行計劃OracleSQL指令碼
- 【CURSOR】Oracle繫結變數、執行計劃對遊標的影響Oracle變數
- Oracle DB 相關常用sql彙總7【手工繫結sql執行計劃】OracleSQL
- 執行計劃-1:獲取執行計劃
- 分析執行計劃優化SQLORACLE的執行計劃(轉)優化SQLOracle
- 【YashanDB知識庫】繫結引數,同一個sql多個執行計劃的問題SQL
- MySQL執行計劃MySql
- SYBASE執行計劃
- MySQL 執行計劃MySql
- 【執行計劃】Oracle獲取執行計劃的幾種方法Oracle
- PostgreSQL DBA(9) - 執行計劃資料結構SQL資料結構
- MySQL執行計劃解析MySql
- mysql explain 執行計劃MySqlAI
- explain執行計劃分析AI
- mysql執行計劃explainMySqlAI
- oracle 固定執行計劃Oracle
- Oracle sql執行計劃OracleSQL
- 執行計劃執行步驟原則
- 【PG執行計劃】Postgresql資料庫執行計劃統計資訊簡述SQL資料庫
- mongodb執行計劃解釋MongoDB
- 檢視 OceanBase 執行計劃
- MySQL執行計劃解析(四)MySql
- 讀懂MySQL執行計劃MySql
- Explain執行計劃詳解AI
- PostgreSQL執行計劃變化SQL
- explain 查詢執行計劃AI
- Vue echarts 繫結事件重複執行問題VueEcharts事件
- 執行緒繫結cpu核心的程式碼研究執行緒
- [20190111]執行計劃bitmap and.txt
- mysql explain 執行計劃詳解MySqlAI
- 十六、Mysql之Explain執行計劃MySqlAI
- SqlServer的執行計劃如何分析?SQLServer
- 柱狀圖與執行計劃
- DB2執行計劃分析DB2