原文發表在:http://l4j.cc/2019/04/19/oracle-sql-plan-management/sql
不少時候可能咱們都但願CBO可以幫咱們生成正確、高效的執行計劃,可是不少時候事實並不是如此,可能由於各類各樣的緣由(如,統計信息不正確或者CBO天生的缺陷等)都會致使生成的執行計劃特別的低效。以前的一家公司有一臺專門用於批量作數據校驗清洗的數據庫,每次校驗清洗完成數據就會清理掉,統計信息常常會發生較大的變動,以前跑得好好的SQL,可能有時候跑5-6個小時都跑不完了,這時候查看執行計劃,發現不正確的統計信息致使了執行計劃的變動。數據庫
<!-- more -->oracle
這時候咱們就但願數據庫中運行的SQL都能有正確、穩定的執行計劃,在10g開始的版本中能夠經過SQL Profile來穩定執行計劃或者在不改變SQL的狀況下修改執行計劃。11g開始可使用偏主動的穩定執行計劃的手段——SPM(SQL PLAN MANAGEMENT),保證只有被驗證過的執行計劃纔會被啓用。ide
SQL Profile是包含特定於SQL語句的輔助統計信息的數據庫對象,能夠改進優化器基數估計,從而選擇更好的執行計劃。 當選擇執行計劃時優化器會考慮如下信息:函數
因此,上面兩個條件的任意一個發生變化,都有可能致使執行計劃的改變。下面看下SQL Profile的一些基本操做以及如何在線進行SQL的調整。性能
經過DBMS_SQLTUNE.ACCEPT_SQL_PROFILE
存儲過程能夠接受一個SQL Profile,只有在咱們接受了一個SQL Profile以後,優化器才能使用他做爲產生執行計劃的輸入。這個存儲過程有兩個比較重要的參數:測試
REGULAR_PROFILE
不更改成並行執行,PX_PROFLE
用於更改並行執行的SQL Profile。下面是ACCEPT_SQL_PROFILE的例子:優化
DECLARE my_sqlprofile_name VARCHAR2(30); BEGIN my_sqlprofile_name := DBMS_SQLTUNE.ACCEPT_SQL_PROFILE ( task_name => 'STA_SPECIFIC_EMP_TASK' , name => 'my_sql_profile' , profile_type => DBMS_SQLTUNE.PX_PROFILE , force_match => true ); END; /
能夠經過DBA_SQL_PROFILES
數據字典視圖來查看存儲在數據庫中的SQL Profile。this
SQL> SELECT NAME,CATEGORY,SQL_TEXT,FORCE_MATCHING,STATUS FROM DBA_SQL_PROFILES; NAME CATEGORY SQL_TEXT FOR STATUS ------------------------------ ---------- ------------------------- --- -------- SYS_SQLPROF_016986bccd640000 DEFAULT select /*+ use_nl(a b) in NO ENABLED dex(b) */a.brwyid,a.yljgd m,a.jzlsh,b.mzzddm from t est_e
經過ALTER_SQL_PROFILE
中的attribute_name
參數能夠修改SQL Profile相應的參數值。調試
BEGIN DBMS_SQLTUNE.ALTER_SQL_PROFILE ( name => 'my_sql_profile' , attribute_name => 'FORCE_MATCH' , value => 'TRUE' ); END; /
經過DROP_SQL_PROFILE
存儲過程能夠刪除特定的SQL Profile
BEGIN DBMS_SQLTUNE.DROP_SQL_PROFILE ( name => 'my_sql_profile' ); END; /
經過手工建立SQL Profile的方式,能夠在不更改目標SQL的SQL文本的狀況下修改SQL的執行計劃,並且能夠很好的穩定SQL的執行計劃。 下面是手工建立SQL Profile的例子,在TEST_ENV.TB_TABLE_LIST的列TABLE_NAME上有一個名爲IDX_TB_TABLE_LIST_TBNAME的B樹索引: 一、首先加一個全表掃描的HINTS來執行下面的SQL,模擬線上的一個執行低效的SQL,並查看其執行計劃。
SQL> SELECT /*+FULL(T)*/ TABLE_NAME FROM TEST_ENV.TB_TABLE_LIST T WHERE TABLE_NAME='ACCESS$'; TABLE_NAME ------------------------------ ACCESS$ SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(NULL, NULL, 'advanced')); PLAN_TABLE_OUTPUT ---------------------------------------------------------------------------------------------------- SQL_ID 1a319c1c2b3rz, child number 0 ------------------------------------- SELECT /*+FULL(T)*/ TABLE_NAME FROM TEST_ENV.TB_TABLE_LIST T WHERE TABLE_NAME='ACCESS$' Plan hash value: 1475094007 ----------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ----------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 31 (100)| | PLAN_TABLE_OUTPUT ---------------------------------------------------------------------------------------------------- |* 1 | TABLE ACCESS FULL| TB_TABLE_LIST | 1 | 18 | 31 (0)| 00:00:01 | ----------------------------------------------------------------------------------- Query Block Name / Object Alias (identified by operation id): ------------------------------------------------------------- 1 - SEL$1 / T@SEL$1 Outline Data ------------- PLAN_TABLE_OUTPUT ---------------------------------------------------------------------------------------------------- /*+ BEGIN_OUTLINE_DATA IGNORE_OPTIM_EMBEDDED_HINTS OPTIMIZER_FEATURES_ENABLE('18.1.0') DB_VERSION('18.1.0') ALL_ROWS OUTLINE_LEAF(@"SEL$1") FULL(@"SEL$1" "T"@"SEL$1") END_OUTLINE_DATA */ PLAN_TABLE_OUTPUT ---------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter("TABLE_NAME"='ACCESS$') Column Projection Information (identified by operation id): ----------------------------------------------------------- 1 - (rowset=256) "TABLE_NAME"[VARCHAR2,128] 已選擇 43 行。
二、而後加入走索引的HINTS來更正這個SQL的執行計劃,獲得下面的執行計劃相關信息,此時咱們就須要用這個執行計劃來替換掉上面走全表掃描的SQL的執行計劃。
SQL> SELECT /*+ INDEX(T IDX_TB_TABLE_LIST_TBNAME) */TABLE_NAME FROM TEST_ENV.TB_TABLE_LIST T WHERE TABLE_NAME='ACCESS$'; TABLE_NAME ------------------------------ ACCESS$ SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(NULL, NULL, 'advanced')); PLAN_TABLE_OUTPUT ---------------------------------------------------------------------------------------------------- SQL_ID 44j1ysb93cwdq, child number 0 ------------------------------------- SELECT /*+ INDEX(T IDX_TB_TABLE_LIST_TBNAME) */TABLE_NAME FROM TEST_ENV.TB_TABLE_LIST T WHERE TABLE_NAME='ACCESS$' Plan hash value: 3318876060 --------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 1 (100)| | PLAN_TABLE_OUTPUT ---------------------------------------------------------------------------------------------------- |* 1 | INDEX RANGE SCAN| IDX_TB_TABLE_LIST_TBNAME | 1 | 18 | 1 (0)| 00:00:01 | --------------------------------------------------------------------------------------------- Query Block Name / Object Alias (identified by operation id): ------------------------------------------------------------- 1 - SEL$1 / T@SEL$1 Outline Data ------------- PLAN_TABLE_OUTPUT ---------------------------------------------------------------------------------------------------- /*+ BEGIN_OUTLINE_DATA IGNORE_OPTIM_EMBEDDED_HINTS OPTIMIZER_FEATURES_ENABLE('18.1.0') DB_VERSION('18.1.0') ALL_ROWS OUTLINE_LEAF(@"SEL$1") INDEX(@"SEL$1" "T"@"SEL$1" ("TB_TABLE_LIST"."TABLE_NAME")) END_OUTLINE_DATA */ PLAN_TABLE_OUTPUT ---------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - access("TABLE_NAME"='ACCESS$') Column Projection Information (identified by operation id): ----------------------------------------------------------- 1 - "TABLE_NAME"[VARCHAR2,128] 已選擇 43 行。
三、下面查看對應的SQL_ID。
SQL> SELECT SQL_TEXT,SQL_ID FROM V$SQLAREA WHERE SQL_TEXT LIKE '%TABLE_NAME FROM TEST_ENV.TB_TABLE%'; SQL_TEXT SQL_ID ------------------------- ------------- SELECT /*+ INDEX(T IDX_TB 44j1ysb93cwdq _TABLE_LIST_TBNAME) */TAB LE_NAME FROM TEST_ENV.TB_ TABLE_LIST T WHERE TABLE_ NAME='ACCESS$' SELECT SQL_TEXT,SQL_ID FR g4v1sg4ycf96y OM V$SQLAREA WHERE SQL_TE XT LIKE '%TABLE_NAME FROM TEST_ENV.TB_TABLE%' SQL_TEXT SQL_ID ------------------------- ------------- SELECT /*+FULL(T)*/ TABLE 1a319c1c2b3rz _NAME FROM TEST_ENV.TB_TA BLE_LIST T WHERE TABLE_NA ME='ACCESS$'
四、建立SQL PROFILE,用正確的執行計劃的OUT LINE DATA來建立SQL Profile
SQL> declare 2 v_hints sys.sqlprof_attr; 3 clsql_text clob; 4 begin 5 v_hints := sys.sqlprof_attr('BEGIN_OUTLINE_DATA', 6 'IGNORE_OPTIM_EMBEDDED_HINTS', 7 'OPTIMIZER_FEATURES_ENABLE(''18.1.0'')', 8 'DB_VERSION(''18.1.0'')', 9 'ALL_ROWS', 10 'OUTLINE_LEAF(@"SEL$1")', 11 'INDEX(@"SEL$1" "T"@"SEL$1" ("TB_TABLE_LIST"."TABLE_NAME"))', 12 'END_OUTLINE_DATA'); 13 14 select sql_fulltext into clsql_text from v$sqlarea where sql_id='1a319c1c2b3rz'; 15 16 dbms_sqltune.import_sql_profile(clsql_text,v_hints,'my_sql_profile',force_match=>true,replace=>true); 17 end; 18 / PL/SQL 過程已成功完成。
五、最後再來看加FULL這個HINTS的SQL語句的執行計劃,能夠看到此時已是作的索引範圍掃描了。
SQL> SELECT /*+FULL(T)*/ TABLE_NAME FROM TEST_ENV.TB_TABLE_LIST T WHERE TABLE_NAME='ACCESS$'; TABLE_NAME ------------------------------ ACCESS$ SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(NULL, NULL, 'advanced')); PLAN_TABLE_OUTPUT ---------------------------------------------------------------------------------------------------- SQL_ID 1a319c1c2b3rz, child number 0 ------------------------------------- SELECT /*+FULL(T)*/ TABLE_NAME FROM TEST_ENV.TB_TABLE_LIST T WHERE TABLE_NAME='ACCESS$' Plan hash value: 3318876060 --------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 1 (100)| | PLAN_TABLE_OUTPUT ---------------------------------------------------------------------------------------------------- |* 1 | INDEX RANGE SCAN| IDX_TB_TABLE_LIST_TBNAME | 1 | 18 | 1 (0)| 00:00:01 | --------------------------------------------------------------------------------------------- Query Block Name / Object Alias (identified by operation id): ------------------------------------------------------------- 1 - SEL$1 / T@SEL$1 Outline Data ------------- PLAN_TABLE_OUTPUT ---------------------------------------------------------------------------------------------------- /*+ BEGIN_OUTLINE_DATA IGNORE_OPTIM_EMBEDDED_HINTS OPTIMIZER_FEATURES_ENABLE('18.1.0') DB_VERSION('18.1.0') ALL_ROWS OUTLINE_LEAF(@"SEL$1") INDEX(@"SEL$1" "T"@"SEL$1" ("TB_TABLE_LIST"."TABLE_NAME")) END_OUTLINE_DATA */ PLAN_TABLE_OUTPUT ---------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - access("TABLE_NAME"='ACCESS$') Column Projection Information (identified by operation id): ----------------------------------------------------------- 1 - "TABLE_NAME"[VARCHAR2,128] Note PLAN_TABLE_OUTPUT ---------------------------------------------------------------------------------------------------- ----- - SQL profile my_sql_profile used for this statement 已選擇 47 行。
SQL Plan Management(SPM)能夠有效避免執行計劃變動而致使的性能降低的問題,只有被驗證和接受的執行計劃纔是可用的。SPM採用了一種叫SQL PLAN BASELINE的機制,它是一系列被驗證性能良好的SQL執行計劃的集合。無論SQL Plan Baseline仍是SQL Profile都是經過內部使用hints來實現的,他們之間的區別以下:
能夠經過如下兩個參數來控制SPM的行爲:
經過數據字典DBA_SQL_PLAN_BASELINES
能夠查詢到存儲在數據庫中的SQL Plan Baselines,而後使用DBMS_XPLAN.DISPLAY_SQL_PLAN_BASELINE
函數能夠查看對應的執行計劃。
SELECT SIGNATURE,SQL_TEXT,SQL_HANDLE,PLAN_NAME FROM DBA_SQL_PLAN_BASELINES; SELECT * from table(DBMS_XPLAN.DISPLAY_SQL_PLAN_BASELINE('SQL_787830cec4402bdf','SQL_PLAN_7hy1htv240ayz01f095c0','advanced'));
經過DBMS_SPM包提供的相關函數,咱們能夠從SQL Tuning Set、 Shared SQL Area和Staging Table中加載SQL Plan Baseline。比較經常使用的是經過Staging Table的方式來進行不一樣數據庫之間SQL Plan Baseline的遷移,好比咱們在測試庫中調試好了一批SQL,須要將其執行計劃導入到生產庫中。 下面是經過Staging Table方式將A庫的SQL Plan Baseline遷移到B庫的基本流程:
BEGIN DBMS_SPM.CREATE_STGTAB_BASELINE ( table_name => 'stage1'); END; /
DECLARE v_plan_cnt NUMBER; BEGIN v_plan_cnt := DBMS_SPM.PACK_STGTAB_BASELINE ( table_name => 'stage1' , enabled => 'yes' , creator => 'spm' ); END; /
DECLARE v_plan_cnt NUMBER; BEGIN v_plan_cnt := DBMS_SPM.UNPACK_STGTAB_BASELINE ( table_name => 'stage1' , fixed => 'yes' ); END; /
經過DBMS_SPM.DROP_SQL_PLAN_BASELINE
函數能夠刪除已經保存的SQL Plan Baseline:
DBA_SQL_PLAN_BASELINES
獲取到對應的SQL_HANDLESQL> EXEC DBMS_SPM.DROP_SQL_PLAN_BASELINE (sql_handle => 'SQL_b6b0d1c71cd1807b');
optimizer_capture_sql_plan_baselines
參數啓用自動捕獲SQL> alter system flush shared_pool; 系統已更改。 SQL> alter system flush buffer_cache; 系統已更改。 SQL> show parameter sql_plan NAME TYPE VALUE ------------------------------------ ----------- ------------------------------ optimizer_capture_sql_plan_baselines boolean FALSE optimizer_use_sql_plan_baselines boolean TRUE SQL> alter system set optimizer_capture_sql_plan_baselines=TRUE; 系統已更改。 SQL> show parameter sql_pla NAME TYPE VALUE ------------------------------------ ----------- ------------------------------ optimizer_capture_sql_plan_baselines boolean TRUE optimizer_use_sql_plan_baselines boolean TRUE
SQL> SET PAGES 10000 LINES 140 SQL> SET SERVEROUTPUT ON SQL> COL SQL_TEXT FOR A20 SQL> COL SQL_HANDLE FOR A20 SQL> COL PLAN_NAME FOR A30 SQL> COL ORIGIN FOR A12 SQL> COL TABLE_NAME FOR A20 SQL> SET LONGC 60535 SQL> SET LONG 60535 SQL> SET ECHO ON SQL> SELECT TABLE_NAME FROM TEST_ENV.TB_TABLE_LIST T WHERE TABLE_NAME='ACCESS$'; TABLE_NAME -------------------- ACCESS$ SQL> SELECT SQL_HANDLE, SQL_TEXT, PLAN_NAME, ORIGIN, ENABLED, ACCEPTED, FIXED, AUTOPURGE FROM DBA_SQL_PLAN_BASELINES W HERE SQL_TEXT LIKE '%TEST_ENV.TB_TABLE_LIST%'; 未選定行
SQL> SELECT TABLE_NAME FROM TEST_ENV.TB_TABLE_LIST T WHERE TABLE_NAME='ACCESS$'; TABLE_NAME -------------------- ACCESS$ SQL> SELECT SQL_HANDLE, SQL_TEXT, PLAN_NAME, ORIGIN, ENABLED, ACCEPTED, FIXED, AUTOPURGE FROM DBA_SQL_PLAN_BASELINES W HERE SQL_TEXT LIKE '%TEST_ENV.TB_TABLE_LIST%'; SQL_HANDLE SQL_TEXT PLAN_NAME ORIGIN ENA ACC FIX AUT -------------------- -------------------- ------------------------------ ------------ --- --- --- --- SQL_787830cec4402bdf SELECT TABLE_NAME FR SQL_PLAN_7hy1htv240ayzc127edb7 AUTO-CAPTURE YES YES NO YES OM TEST_ENV.TB_TABLE _LIST T WHERE TABLE_ NAME='ACCESS$'
SQL> CREATE INDEX test_env.IDX_TB_TABLE_LIST_TBNAME ON TEST_ENV.TB_TABLE_LIST(TABLE_NAME); 索引已建立。 SQL> EXEC DBMS_STATS.GATHER_TABLE_STATS('TEST_ENV','TB_TABLE_LIST',CASCADE=>TRUE); PL/SQL 過程已成功完成。 SQL> SELECT TABLE_NAME FROM TEST_ENV.TB_TABLE_LIST T WHERE TABLE_NAME='ACCESS$'; TABLE_NAME -------------------- ACCESS$ SQL> SELECT SQL_HANDLE, SQL_TEXT, PLAN_NAME, ORIGIN, ENABLED, ACCEPTED, FIXED, AUTOPURGE FROM DBA_SQL_PLAN_BASELINES W HERE SQL_TEXT LIKE '%TEST_ENV.TB_TABLE_LIST%'; SQL_HANDLE SQL_TEXT PLAN_NAME ORIGIN ENA ACC FIX AUT -------------------- -------------------- ------------------------------ ------------ --- --- --- --- SQL_787830cec4402bdf SELECT TABLE_NAME FR SQL_PLAN_7hy1htv240ayz01f095c0 AUTO-CAPTURE YES NO NO YES OM TEST_ENV.TB_TABLE _LIST T WHERE TABLE_ NAME='ACCESS$' SQL_787830cec4402bdf SELECT TABLE_NAME FR SQL_PLAN_7hy1htv240ayzc127edb7 AUTO-CAPTURE YES YES NO YES OM TEST_ENV.TB_TABLE _LIST T WHERE TABLE_ NAME='ACCESS$'
SQL> explain plan for SELECT TABLE_NAME FROM TEST_ENV.TB_TABLE_LIST T WHERE TABLE_NAME='ACCESS$'; 已解釋。 SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY(null, null, 'basic +note')); PLAN_TABLE_OUTPUT ------------------------------------------------------------------------------------------------------------------------ -------------------- Plan hash value: 1475094007 ------------------------------------------- | Id | Operation | Name | ------------------------------------------- | 0 | SELECT STATEMENT | | | 1 | TABLE ACCESS FULL| TB_TABLE_LIST | ------------------------------------------- Note ----- - SQL plan baseline "SQL_PLAN_7hy1htv240ayzc127edb7" used for this statement 已選擇 12 行。
能夠看到,執行這個SQL依然走的是全表掃描,注意note部分,表示這個執行計劃使用了"SQL_PLAN_7hy1htv240ayzc127edb7"的SQL Plan Baseline。代表了SPM能夠很好的固定特定SQL的執行計劃。 6. 可是實際上此時應該是走索引範圍掃描纔是最高效的,即"SQL_PLAN_7hy1htv240ayz01f095c0"這個SQL Plan Baseline,如何啓用改baseline呢?首先建立一個evole任務,並執行該任務。
SQL> var cnt NUMBER SQL> var tk_name VARCHAR2(50) SQL> var exe_name VARCHAR2(50) SQL> var evol_out CLOB SQL> EXEC :tk_name := DBMS_SPM.CREATE_EVOLVE_TASK(sql_handle => 'SQL_787830cec4402bdf', plan_name => 'SQL_PLAN_7hy1htv2 40ayz01f095c0'); PL/SQL 過程已成功完成。 SQL> print :tk_name TK_NAME -------- 任務_31 SQL> EXEC :exe_name :=DBMS_SPM.EXECUTE_EVOLVE_TASK(task_name=>:tk_name); PL/SQL 過程已成功完成。 SQL> print :exe_name EXE_NAME -------- EXEC_151
REPORT_EVOLVE_TASK
能夠查看到相應的任務報告。Findings部分告訴咱們發現了一個比基線效率更高的執行計劃,並提供了建議方案。SQL> EXEC :evol_out := DBMS_SPM.REPORT_EVOLVE_TASK( task_name=>:tk_name, execution_name=>:exe_name ); PL/SQL 過程已成功完成。 SQL> SELECT :evol_out FROM DUAL; :EVOL_OUT ------------------------------------------------------------------------------------------------------------------------ -------------------- GENERAL INFORMATION SECTION --------------------------------------------------------------------------------------------- Task Information: --------------------------------------------- Task Name : 任務_31 Task Owner : SYS Execution Name : EXEC_151 Execution Type : SPM EVOLVE Scope : COMPREHENSIVE Status : COMPLETED Started : 04/19/2019 16:45:33 Finished : 04/19/2019 16:45:33 Last Updated : 04/19/2019 16:45:33 Global Time Limit : 2147483646 Per-Plan Time Limit : UNUSED Number of Errors : 0 --------------------------------------------------------------------------------------------- SUMMARY SECTION --------------------------------------------------------------------------------------------- Number of plans processed : 1 Number of findings : 1 Number of recommendations : 1 Number of errors : 0 --------------------------------------------------------------------------------------------- DETAILS SECTION --------------------------------------------------------------------------------------------- Object ID : 2 Test Plan Name : SQL_PLAN_7hy1htv240ayz01f095c0 Base Plan Name : SQL_PLAN_7hy1htv240ayzc127edb7 SQL Handle : SQL_787830cec4402bdf Parsing Schema : SYS Test Plan Creator : SYS SQL Text : SELECT TABLE_NAME FROM TEST_ENV.TB_TABLE_LIST T WHERE TABLE_NAME='ACCESS$' Execution Statistics: ----------------------------- Base Plan Test Plan ---------------------------- ---------------------------- Elapsed Time (s): .000016 .000001 CPU Time (s): 0 0 Buffer Gets: 10 0 Optimizer Cost: 31 1 Disk Reads: 0 0 Direct Writes: 0 0 Rows Processed: 0 0 Executions: 10 10 FINDINGS SECTION --------------------------------------------------------------------------------------------- Findings (1): ----------------------------- 1. 計劃已在 0.03200 秒內驗證完畢。此計劃符合收益標準, 這是由於其驗證性能比基線計劃的性能高 50.50000 倍。 Recommendation: ----------------------------- Consider accepting the plan. Execute dbms_spm.accept_sql_plan_baseline(task_name => '任務_31', object_id => 2, task_owner => 'SYS'); EXPLAIN PLANS SECTION --------------------------------------------------------------------------------------------- Baseline Plan ----------------------------- Plan Id : 201 Plan Hash Value : 3240619447 ------------------------------------------------------------------------------ | Id | Operation | Name | Rows | Bytes | Cost | Time | ------------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | 1 | 18 | 31 | 00:00:01 | | * 1 | TABLE ACCESS FULL | TB_TABLE_LIST | 1 | 18 | 31 | 00:00:01 | ------------------------------------------------------------------------------ Predicate Information (identified by operation id): ------------------------------------------ * 1 - filter("TABLE_NAME"='ACCESS$') Test Plan ----------------------------- Plan Id : 202 Plan Hash Value : 32544192 ---------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost | Time | ---------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 18 | 1 | 00:00:01 | | * 1 | INDEX RANGE SCAN | IDX_TB_TABLE_LIST_TBNAME | 1 | 18 | 1 | 00:00:01 | ---------------------------------------------------------------------------------------- Predicate Information (identified by operation id): ------------------------------------------ * 1 - access("TABLE_NAME"='ACCESS$') ---------------------------------------------------------------------------------------------
IMPLEMENT_EVOLVE_TASK
函數。SQL> EXEC :cnt := DBMS_SPM.IMPLEMENT_EVOLVE_TASK( task_name=>:tk_name, execution_name=>:exe_name ); PL/SQL 過程已成功完成。 SQL> SELECT SQL_HANDLE, SQL_TEXT, PLAN_NAME, ORIGIN, ENABLED, ACCEPTED FROM DBA_SQL_PLAN_BASELINES WHERE SQL_HANDLE=' SQL_787830cec4402bdf'; SQL_HANDLE SQL_TEXT PLAN_NAME ORIGIN ENA ACC -------------------- -------------------- ------------------------------ ------------ --- --- SQL_787830cec4402bdf SELECT TABLE_NAME FR SQL_PLAN_7hy1htv240ayz01f095c0 AUTO-CAPTURE YES YES OM TEST_ENV.TB_TABLE _LIST T WHERE TABLE_ NAME='ACCESS$' SQL_787830cec4402bdf SELECT TABLE_NAME FR SQL_PLAN_7hy1htv240ayzc127edb7 AUTO-CAPTURE YES YES OM TEST_ENV.TB_TABLE _LIST T WHERE TABLE_ NAME='ACCESS$'
SQL> explain plan for SELECT TABLE_NAME FROM TEST_ENV.TB_TABLE_LIST T WHERE TABLE_NAME='ACCESS$'; 已解釋。 SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY(null, null, 'basic +note')); PLAN_TABLE_OUTPUT ------------------------------------------------------------------------------------------------------------------------ Plan hash value: 3318876060 ----------------------------------------------------- | Id | Operation | Name | ----------------------------------------------------- | 0 | SELECT STATEMENT | | | 1 | INDEX RANGE SCAN| IDX_TB_TABLE_LIST_TBNAME | ----------------------------------------------------- Note ----- - SQL plan baseline "SQL_PLAN_7hy1htv240ayz01f095c0" used for this statement 已選擇 12 行。
能夠看到此時已經走了索引範圍掃描,使用的是"SQL_PLAN_7hy1htv240ayz01f095c0"這個SQL Plan Baseline。當存在多個計劃的ACCEPTED和ENABLE的值都爲YES的時候,優化器會選擇一個成本更低的計劃來執行。
接下來看下手工生成SQL Plan Baseline的方法。其實很是簡單,核心就是經過DBMS_STATS.LOAD_PLANS_FROM_CURSOR_CACHE
來從Shared SQL Area中加載執行計劃。
SQL> alter system set optimizer_capture_sql_plan_baselines=false; 系統已更改。 SQL> show parameter sql_pla NAME TYPE VALUE ------------------------------------ ----------- ------------------------------ optimizer_capture_sql_plan_baselines boolean FALSE optimizer_use_sql_plan_baselines boolean TRUE SQL> EXEC :cnt :=DBMS_SPM.DROP_SQL_PLAN_BASELINE('SQL_787830cec4402bdf'); PL/SQL 過程已成功完成。 SQL> EXEC :cnt :=DBMS_SPM.DROP_SQL_PLAN_BASELINE('SQL_061becb140fad607'); PL/SQL 過程已成功完成。 SQL> EXEC :cnt :=DBMS_SPM.DROP_SQL_PLAN_BASELINE('SQL_ecca2815c7166fb6'); PL/SQL 過程已成功完成。 SQL> EXEC :cnt :=DBMS_SPM.DROP_SQL_PLAN_BASELINE('SQL_9049245213a986b3'); PL/SQL 過程已成功完成。 SQL> EXEC :cnt :=DBMS_SPM.DROP_SQL_PLAN_BASELINE('SQL_85372e07e425b213'); PL/SQL 過程已成功完成。 SQL> DELETE FROM SQLLOG$; 已刪除 9 行。 SQL> commit; 提交完成。 SQL> alter system flush shared_pool; 系統已更改。 SQL> alter system flush buffer_cache; 系統已更改。
SQL> SELECT TABLE_NAME FROM TEST_ENV.TB_TABLE_LIST T WHERE TABLE_NAME='ACCESS$'; TABLE_NAME -------------------- ACCESS$ SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(NULL, NULL, 'advanced')); PLAN_TABLE_OUTPUT ------------------------------------------------------------------------------------------------------------------------ SQL_ID drmkgq2ppg7kg, child number 0 ------------------------------------- SELECT TABLE_NAME FROM TEST_ENV.TB_TABLE_LIST T WHERE TABLE_NAME='ACCESS$' Plan hash value: 1475094007 ----------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ----------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 31 (100)| | PLAN_TABLE_OUTPUT ------------------------------------------------------------------------------------------------------------------------ |* 1 | TABLE ACCESS FULL| TB_TABLE_LIST | 1 | 18 | 31 (0)| 00:00:01 | ----------------------------------------------------------------------------------- Query Block Name / Object Alias (identified by operation id): ------------------------------------------------------------- 1 - SEL$1 / T@SEL$1 Outline Data ------------- PLAN_TABLE_OUTPUT ------------------------------------------------------------------------------------------------------------------------ /*+ BEGIN_OUTLINE_DATA IGNORE_OPTIM_EMBEDDED_HINTS OPTIMIZER_FEATURES_ENABLE('18.1.0') DB_VERSION('18.1.0') ALL_ROWS OUTLINE_LEAF(@"SEL$1") FULL(@"SEL$1" "T"@"SEL$1") END_OUTLINE_DATA */ PLAN_TABLE_OUTPUT ------------------------------------------------------------------------------------------------------------------------ Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter("TABLE_NAME"='ACCESS$') Column Projection Information (identified by operation id): ----------------------------------------------------------- 1 - (rowset=256) "TABLE_NAME"[VARCHAR2,128] 已選擇 43 行。
SQL> var cnt number SQL> exec :cnt := DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE(sql_id=>'drmkgq2ppg7kg', plan_hash_value=>'1475094007'); PL/SQL 過程已成功完成。 SQL> SELECT SQL_HANDLE, SQL_TEXT, PLAN_NAME, ORIGIN, ENABLED, ACCEPTED FROM DBA_SQL_PLAN_BASELINES WHERE SQL_TEXT LIKE '%TEST_ENV.TB_TABLE_LIST%'; SQL_HANDLE SQL_TEXT PLAN_NAME ORIGIN ENA ACC -------------------- -------------------- ------------------------------ ------------ --- --- SQL_787830cec4402bdf SELECT TABLE_NAME FR SQL_PLAN_7hy1htv240ayzc127edb7 MANUAL-LOAD- YES YES OM TEST_ENV.TB_TABLE FROM-CURSOR- _LIST T WHERE TABLE_ CACHE NAME='ACCESS$'
SQL> SELECT TABLE_NAME FROM TEST_ENV.TB_TABLE_LIST T WHERE TABLE_NAME='ACCESS$'; TABLE_NAME -------------------- ACCESS$ SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(NULL, NULL, 'advanced')); PLAN_TABLE_OUTPUT ----------------------------------------------------------------------------------------------------------------------- SQL_ID drmkgq2ppg7kg, child number 1 ------------------------------------- SELECT TABLE_NAME FROM TEST_ENV.TB_TABLE_LIST T WHERE TABLE_NAME='ACCESS$' Plan hash value: 1475094007 ----------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ----------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 31 (100)| | PLAN_TABLE_OUTPUT ----------------------------------------------------------------------------------------------------------------------- |* 1 | TABLE ACCESS FULL| TB_TABLE_LIST | 1 | 18 | 31 (0)| 00:00:01 | ----------------------------------------------------------------------------------- Query Block Name / Object Alias (identified by operation id): ------------------------------------------------------------- 1 - SEL$1 / T@SEL$1 Outline Data ------------- PLAN_TABLE_OUTPUT ----------------------------------------------------------------------------------------------------------------------- /*+ BEGIN_OUTLINE_DATA IGNORE_OPTIM_EMBEDDED_HINTS OPTIMIZER_FEATURES_ENABLE('18.1.0') DB_VERSION('18.1.0') ALL_ROWS OUTLINE_LEAF(@"SEL$1") FULL(@"SEL$1" "T"@"SEL$1") END_OUTLINE_DATA */ PLAN_TABLE_OUTPUT ----------------------------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter("TABLE_NAME"='ACCESS$') Column Projection Information (identified by operation id): ----------------------------------------------------------- 1 - (rowset=256) "TABLE_NAME"[VARCHAR2,128] Note PLAN_TABLE_OUTPUT ----------------------------------------------------------------------------------------------------------------------- ----- - SQL plan baseline SQL_PLAN_7hy1htv240ayzc127edb7 used for this statement 已選擇 47 行。
SQL> SELECT SQL_HANDLE, SQL_TEXT, PLAN_NAME, ORIGIN, ENABLED, ACCEPTED FROM DBA_SQL_PLAN_BASELINES WHERE SQL_TEXT LIKE '%TEST_ENV.TB_TABLE_LIST%'; SQL_HANDLE SQL_TEXT PLAN_NAME ORIGIN ENA ACC -------------------- -------------------- ------------------------------ ------------ --- --- SQL_787830cec4402bdf SELECT TABLE_NAME FR SQL_PLAN_7hy1htv240ayz01f095c0 AUTO-CAPTURE YES NO OM TEST_ENV.TB_TABLE _LIST T WHERE TABLE_ NAME='ACCESS$' SQL_787830cec4402bdf SELECT TABLE_NAME FR SQL_PLAN_7hy1htv240ayzc127edb7 MANUAL-LOAD- YES YES OM TEST_ENV.TB_TABLE FROM-CURSOR- _LIST T WHERE TABLE_ CACHE NAME='ACCESS$'
SQL> var result clob; SQL> EXEC :result :=DBMS_SPM.EVOLVE_SQL_PLAN_BASELINE(SQL_HANDLE=>'SQL_787830cec4402bdf', PLAN_NAME=>'SQL_PLAN_7hy1htv24 0ayz01f095c0', VERIFY=>'NO', COMMIT=>'YES'); PL/SQL 過程已成功完成。 SQL> SELECT SQL_HANDLE, SQL_TEXT, PLAN_NAME, ORIGIN, ENABLED, ACCEPTED FROM DBA_SQL_PLAN_BASELINES WHERE SQL_TEXT LIKE '%TEST_ENV.TB_TABLE_LIST%'; SQL_HANDLE SQL_TEXT PLAN_NAME ORIGIN ENA ACC -------------------- -------------------- ------------------------------ ------------ --- --- SQL_787830cec4402bdf SELECT TABLE_NAME FR SQL_PLAN_7hy1htv240ayz01f095c0 AUTO-CAPTURE YES YES OM TEST_ENV.TB_TABLE _LIST T WHERE TABLE_ NAME='ACCESS$' SQL_787830cec4402bdf SELECT TABLE_NAME FR SQL_PLAN_7hy1htv240ayzc127edb7 MANUAL-LOAD- YES YES OM TEST_ENV.TB_TABLE FROM-CURSOR- _LIST T WHERE TABLE_ CACHE NAME='ACCESS$'
SQL> SELECT TABLE_NAME FROM TEST_ENV.TB_TABLE_LIST T WHERE TABLE_NAME='ACCESS$'; TABLE_NAME -------------------- ACCESS$ SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(NULL, NULL, 'advanced')); PLAN_TABLE_OUTPUT ------------------------------------------------------------------------------------------------------------------------ SQL_ID drmkgq2ppg7kg, child number 0 ------------------------------------- SELECT TABLE_NAME FROM TEST_ENV.TB_TABLE_LIST T WHERE TABLE_NAME='ACCESS$' Plan hash value: 3318876060 --------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 1 (100)| | PLAN_TABLE_OUTPUT ------------------------------------------------------------------------------------------------------------------------ |* 1 | INDEX RANGE SCAN| IDX_TB_TABLE_LIST_TBNAME | 1 | 18 | 1 (0)| 00:00:01 | --------------------------------------------------------------------------------------------- Query Block Name / Object Alias (identified by operation id): ------------------------------------------------------------- 1 - SEL$1 / T@SEL$1 Outline Data ------------- PLAN_TABLE_OUTPUT ------------------------------------------------------------------------------------------------------------------------ /*+ BEGIN_OUTLINE_DATA IGNORE_OPTIM_EMBEDDED_HINTS OPTIMIZER_FEATURES_ENABLE('18.1.0') DB_VERSION('18.1.0') ALL_ROWS OUTLINE_LEAF(@"SEL$1") INDEX(@"SEL$1" "T"@"SEL$1" ("TB_TABLE_LIST"."TABLE_NAME")) END_OUTLINE_DATA */ PLAN_TABLE_OUTPUT ------------------------------------------------------------------------------------------------------------------------ Predicate Information (identified by operation id): --------------------------------------------------- 1 - access("TABLE_NAME"='ACCESS$') Column Projection Information (identified by operation id): ----------------------------------------------------------- 1 - "TABLE_NAME"[VARCHAR2,128] Note PLAN_TABLE_OUTPUT ------------------------------------------------------------------------------------------------------------------------ ----- - SQL plan baseline SQL_PLAN_7hy1htv240ayz01f095c0 used for this statement 已選擇 47 行。
SQL> exec :cnt :=DBMS_SPM.DROP_SQL_PLAN_BASELINE (sql_handle=>'SQL_787830cec4402bdf', plan_name=>'SQL_PLAN_7hy1htv240ayz c127edb7'); PL/SQL 過程已成功完成。 SQL> SELECT /*+FULL(T)*/ TABLE_NAME FROM TEST_ENV.TB_TABLE_LIST T WHERE TABLE_NAME='ACCESS$'; TABLE_NAME -------------------- ACCESS$ SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(NULL, NULL, 'advanced')); PLAN_TABLE_OUTPUT ---------------------------------------------------------------------------------------------------------------------- SQL_ID 1a319c1c2b3rz, child number 0 ------------------------------------- SELECT /*+FULL(T)*/ TABLE_NAME FROM TEST_ENV.TB_TABLE_LIST T WHERE TABLE_NAME='ACCESS$' Plan hash value: 1475094007 ----------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ----------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 31 (100)| | PLAN_TABLE_OUTPUT ---------------------------------------------------------------------------------------------------------------------- |* 1 | TABLE ACCESS FULL| TB_TABLE_LIST | 1 | 18 | 31 (0)| 00:00:01 | ----------------------------------------------------------------------------------- Query Block Name / Object Alias (identified by operation id): ------------------------------------------------------------- 1 - SEL$1 / T@SEL$1 Outline Data ------------- PLAN_TABLE_OUTPUT ---------------------------------------------------------------------------------------------------------------------- /*+ BEGIN_OUTLINE_DATA IGNORE_OPTIM_EMBEDDED_HINTS OPTIMIZER_FEATURES_ENABLE('18.1.0') DB_VERSION('18.1.0') ALL_ROWS OUTLINE_LEAF(@"SEL$1") FULL(@"SEL$1" "T"@"SEL$1") END_OUTLINE_DATA */ PLAN_TABLE_OUTPUT ---------------------------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter("TABLE_NAME"='ACCESS$') Column Projection Information (identified by operation id): ----------------------------------------------------------- 1 - (rowset=256) "TABLE_NAME"[VARCHAR2,128] 已選擇 43 行。
此時從索引範圍掃描變成了全表掃描,SQL_ID爲1a319c1c2b3rz,PLAN_HASH_VALUE爲1475094007 9. 此時的DBA_SQL_PLAN_BASELINES依然以後剛剛那一條記錄。
SQL> SELECT SQL_HANDLE, SQL_TEXT, PLAN_NAME, ORIGIN, ENABLED, ACCEPTED FROM DBA_SQL_PLAN_BASELINES WHERE SQL_TEXT LIKE '%TEST_ENV.TB_TABLE_LIST%'; SQL_HANDLE SQL_TEXT PLAN_NAME ORIGIN ENA ACC -------------------- -------------------- ------------------------------ ------------ --- --- SQL_787830cec4402bdf SELECT TABLE_NAME FR SQL_PLAN_7hy1htv240ayz01f095c0 AUTO-CAPTURE YES YES OM TEST_ENV.TB_TABLE _LIST T WHERE TABLE_ NAME='ACCESS$'
SQL> exec :cnt := dbms_spm.load_plans_from_cursor_cache(sql_id => '1a319c1c2b3rz', plan_hash_value=>1475094007, sql_handle=>'SQL_787830cec4402bdf') PL/SQL 過程已成功完成。 SQL> SELECT SQL_HANDLE, SQL_TEXT, PLAN_NAME, ORIGIN, ENABLED, ACCEPTED FROM DBA_SQL_PLAN_BASELINES WHERE SQL_TEXT LIKE '%TEST_ENV.TB_TABLE_LIST%'; SQL_HANDLE SQL_TEXT PLAN_NAME ORIGIN ENA ACC -------------------- -------------------- ------------------------------ ------------ --- --- SQL_787830cec4402bdf SELECT TABLE_NAME FR SQL_PLAN_7hy1htv240ayz01f095c0 AUTO-CAPTURE YES YES OM TEST_ENV.TB_TABLE _LIST T WHERE TABLE_ NAME='ACCESS$' SQL_787830cec4402bdf SELECT TABLE_NAME FR SQL_PLAN_7hy1htv240ayzc127edb7 MANUAL-LOAD- YES YES OM TEST_ENV.TB_TABLE FROM-CURSOR- _LIST T WHERE TABLE_ CACHE NAME='ACCESS$'
SQL> EXEC :CNT := DBMS_SPM.ALTER_SQL_PLAN_BASELINE(SQL_HANDLE=>'SQL_787830cec4402bdf', PLAN_NAME=>'SQL_PLAN_7hy1htv240ay z01f095c0', ATTRIBUTE_NAME=>'ENABLED', ATTRIBUTE_VALUE=>'NO'); PL/SQL 過程已成功完成。 SQL> SELECT SQL_HANDLE, SQL_TEXT, PLAN_NAME, ORIGIN, ENABLED, ACCEPTED FROM DBA_SQL_PLAN_BASELINES WHERE SQL_TEXT LIKE '%TEST_ENV.TB_TABLE_LIST%'; SQL_HANDLE SQL_TEXT PLAN_NAME ORIGIN ENA ACC -------------------- -------------------- ------------------------------ ------------ --- --- SQL_787830cec4402bdf SELECT TABLE_NAME FR SQL_PLAN_7hy1htv240ayz01f095c0 AUTO-CAPTURE NO YES OM TEST_ENV.TB_TABLE _LIST T WHERE TABLE_ NAME='ACCESS$' SQL_787830cec4402bdf SELECT TABLE_NAME FR SQL_PLAN_7hy1htv240ayzc127edb7 MANUAL-LOAD- YES YES OM TEST_ENV.TB_TABLE FROM-CURSOR- _LIST T WHERE TABLE_ CACHE NAME='ACCESS$' SQL> SELECT TABLE_NAME FROM TEST_ENV.TB_TABLE_LIST T WHERE TABLE_NAME='ACCESS$'; TABLE_NAME -------------------- ACCESS$ SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(NULL, NULL, 'advanced')); PLAN_TABLE_OUTPUT ------------------------------------------------------------------------------------------------------------------------ SQL_ID drmkgq2ppg7kg, child number 1 ------------------------------------- SELECT TABLE_NAME FROM TEST_ENV.TB_TABLE_LIST T WHERE TABLE_NAME='ACCESS$' Plan hash value: 1475094007 ----------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ----------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 31 (100)| | PLAN_TABLE_OUTPUT ------------------------------------------------------------------------------------------------------------------------ |* 1 | TABLE ACCESS FULL| TB_TABLE_LIST | 1 | 18 | 31 (0)| 00:00:01 | ----------------------------------------------------------------------------------- Query Block Name / Object Alias (identified by operation id): ------------------------------------------------------------- 1 - SEL$1 / T@SEL$1 Outline Data ------------- PLAN_TABLE_OUTPUT ------------------------------------------------------------------------------------------------------------------------ /*+ BEGIN_OUTLINE_DATA IGNORE_OPTIM_EMBEDDED_HINTS OPTIMIZER_FEATURES_ENABLE('18.1.0') DB_VERSION('18.1.0') ALL_ROWS OUTLINE_LEAF(@"SEL$1") FULL(@"SEL$1" "T"@"SEL$1") END_OUTLINE_DATA */ PLAN_TABLE_OUTPUT ------------------------------------------------------------------------------------------------------------------------ Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter("TABLE_NAME"='ACCESS$') Column Projection Information (identified by operation id): ----------------------------------------------------------- 1 - (rowset=256) "TABLE_NAME"[VARCHAR2,128] Note PLAN_TABLE_OUTPUT ------------------------------------------------------------------------------------------------------------------------ ----- - SQL plan baseline SQL_PLAN_7hy1htv240ayzc127edb7 used for this statement 已選擇 47 行。
能夠看到此時的執行計劃就是剛剛加HINTS的SQL語句的執行計劃了。