一.查看執行計划:
先從開頭一直連續往右看,直到看到最右邊的並列的地方,對於不並列的,靠右的先執行;如果見到並列的,就從上往下看,對於並列的部分,考上的先執行。
總結兩個字: 右 上
二.oracle里面常見的執行計划
2.1.與表訪問相關的執行計划
全表掃描:(TABLE ACCESS BY ROWID)
rowid掃描:(TABLE ACCESS BY USER ROWID或者TABLE ACCESS BY INDEX ROWID)
2.2.與B樹索引相關的執行計划
索引唯一掃描:index unique scan
索引范圍掃描:index range scan
索引全掃描:index full scan
索引快速掃描:index fast scan
索引跳躍式掃描:index skip scan
2.3.與位圖索引相關的執行計划
2.3.1.位圖所以主要用於數據倉庫或者DSS系統。
2.3.2.結構:被索引的鍵值,對應的rowid的下限,對應rowid的上限,位圖段。
位圖索引的結構就表明了位圖索引中鎖的粒度是位圖段,沒有行鎖這個概念,但是多個數據行可能對應同一個索引行的位圖段,這個鎖的粒度就決定了位圖索引不適合於高並發且頻繁修改的OLTP系統,如果在高並發且頻繁修改的OLTP系統中使用了位圖索引,很可能導致嚴重的並發問題,甚至產生死鎖。
2.3.3.與btree索引相比,位圖索引的優勢:
因為位圖索引的位圖段是壓縮后存儲的,所以如果被索引列的distinct值較少,會節省很多空間。
位圖索引能夠快速處理一下包含各種and或者or查詢條件的sql,因為位圖索引能夠實現快捷的按位運算。
2.3.4.相關執行計划: 位圖索引單鍵值掃描,位圖索引范圍掃描,位圖索引全掃描,位圖索引快速全掃描,位圖按位與,位圖按位或,位圖按位減等。
2.4.與表連接相關的執行計划
排序合並連接--sort join和 merge join。
嵌套循環連接--nested loops
哈希連接--hash join
反連接--ant1
半連接---sem1
三.oracle里執行計划的穩定
oracle11g推出了SPM(SQL Plan Management),SPM是一種主動的穩定執行計划的手段,能夠保證只有被驗證過得執行計划才會被啟用。當啟用了SPM之后,每一個sql都會存在對應的sql plan baseline,這個sql plan baseline里面存儲的就是該sql的執行計划,可以從DBA_SQL_PLAN_BASELINES查看目標sql的所有sql plan baseline。
在oracle11g機及其以上的版本中,有兩種方法可以產生目標sql的sql plan baseline
自動不活。
手工生成/批量導入。(批量導入適用於oracle數據庫大版本的升級)
參數 OPTIMIZER_CAPTURE_SQL_BASELINES用於控制是否開啟自動捕獲sql plan baseline,默認值為false。
這個值可以再session和系統級別動態修改,當我們將其改為TRUE后,oracle會對參數影響范圍內所有重復執行的sql自動捕獲其sql plan baseline。
參數OPTIMIZER_USER_SQL_PLAN_BASELINES用於控制是否啟用SQL plan Baseline,其默認值為true,表示在默認情況下,oracle在生成執行計划時就會啟用SPM,使用已有的sql plan baseline,這個參數也可以在session和系統級別動態修改。
就是說oracle默認不會捕獲sql base baseline,會默認固定執行計划。
sql plan baseline有兩個值ENABLED和ACCEPTED,當新產生的sql plan baseline的這兩個值都為yes的時候,sql的執行計划才會采納這個新的sql plan baseline.
實用一.我們想啟用新的執行計划
11gr2:
前提是我們已經啟用自動捕獲:optimizer_capture_sql_plan_baselines,並且已經生成了新的sql base baseline。然后我們想使用新的執行計划。
就是先將我們新的sql base baseline的accepted改為yesyes,然后把原來sql base baseline的ENABLED改為NO。
var temp number
DBMS_SPM.EVOLVE_SQL_PLAN_BASELINE將目標sql新的執行計划將accepted改為yes。
exec :temp := DBMS_SPM.EVOLVE_SQL_PLAN_BASELINE(sql_handle => '',plan_name=>'',verify=>'NO',commit=>'YES');
DBMS_SPM.ALTER_SQL_PLAN_BASELINE將原來的執行計划所對應的SQL PLAN BASELINE的enabled的值設為no。
exec :temp :=DBMS_SPM.ALTER_SQL_PLAN_BASELINE(sql_handle=>'',plan_name=>'',attribute_name='ENABLED',attribute_values => 'NO');
實用二.當執行計划突然改變,如果重新綁定為之前的執行計划
(前提是之前的執行計划還在內存中)
-- 1、查看plan_hash_value是否在內存中存在:
select plan_hash_value from v$sql where sql_id = '&sql_id' group by plan_hash_value;
-- 2、plan_hash_value存在則固定
DECLARE
my_plans PLS_INTEGER;
BEGIN
my_plans:=DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE(SQL_ID=>'69ux4s03mvr53',PLAN_HASH_VALUE=>3236094840,FIXED=>'YES',ENABLED=>'YES');
END;
/
select sql_id,plan_hash_value,SQL_PLAN_BASELINE,child_number from v$sql where sql_id='&sql_id';
實用三.手工生成SQL Plan Baseline
還是調用DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE
3.3.1.針對目標sql使用DBMS_SPM_LOAD_SPM.LOAD_PLANS_FROM_CURSOR_CACHE手工生成其初始化執行計划所對應的sql plan baseline。
dbms_spm.load_plans_cursor_cache
(
sql_id => '原目標sql的sql文本'
plan_hash_value => 原目標sql的plan hash value
)
3.3.2.改寫目標sql的sql文本,在其中加入合適的hint,直到加入hint后所改寫的sql能走出我們想要的執行計划,然后對改寫的sql使用包手工生成新的執行計划所對應的sql plan baseline。
dbms_spm.load_plans_from_cursor_cache
(
sql_id => '加入合適的hint后改寫的sql id',
plan_hash_value => '加入合適hint后改寫sql的plan hash value',
sql_handle => '原目標sql在步驟1中所產生的sql plan baseline的sql_handle'
)
3.3.3.使用DBMS_SPM.DROP_SQL_PLAN_BASELINE刪除在步驟1中手工生成的原目標sql的初始執行計划所對應的SQL plan baseline。
dbms_spm.drop_sql_plan_baseline
(
sql_handle => '原目標sql在3.3.1中所產生的sql plan baseline的sql_handle'
plan_hash_value => ''加入合適hint后改寫的sql的plan hash value
sql_handle => '原目標sql在步驟1中所產生的sql plan baseline的sql_handle'
)
下面自己做一個手動生成sql plan baseline的示例:
第一步.先手工生成初始執行計划
SQL> select /*+ no_index(dept PK_DEPT) */ * from dept where deptno='10';
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING NEW YORK
SQL> select * from table(dbms_xplan.display_cursor(null,null,'ALL'));
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
SQL_ID 5ph4uwu6m55aw, child number 0
-------------------------------------
select /*+ no_index(dept PK_DEPT) */ * from dept where deptno='10'
Plan hash value: 3383998547
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 3 (100)| |
|* 1 | TABLE ACCESS FULL| DEPT | 4 | 120 | 3 (0)| 00:00:01 |
================================================
SQL_ID 5ph4uwu6m55aw
Plan hash value: 3383998547
SQL> var temp number
SQL> exec :temp := dbms_spm.load_plans_from_cursor_cache(sql_id => '5ph4uwu6m55aw',plan_hash_value => 3383998547);
PL/SQL procedure successfully completed.
select sql_handle,plan_name,origin,enabled,accepted,sql_text from dba_sql_plan_baselines where sql_text like 'select /*+ no_index(dept PK_DEPT) */ * from dept%';
SQL_eb3a5ffc38442091 SQL_PLAN_fqfkzzhw4884j0e23be79 MANUAL-LOAD YES YES select /*+ no_index(dept PK_DEPT) */ * from dept where deptno='10'
我們看到,目標sql的初始執行計划所對應的sql plan baseline已經成功生成,其對應的sql_handle為SQL_eb3a5ffc38442091,plan_name為SQL_PLAN_fqfkzzhw4884j0e23be79
第二步.改寫目標sql
SQL> select /*+ index(dept PK_DEPT) */ * from dept where deptno='10';
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING NEW YORK
SQL> select * from table(dbms_xplan.display_cursor(null,null,'ALL'));
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID 0mpwgns2uh7uh, child number 0
-------------------------------------
select /*+ index(dept PK_DEPT) */ * from dept where deptno='10'
Plan hash value: 2852011669
---------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 2 (100)| |
| 1 | TABLE ACCESS BY INDEX ROWID| DEPT | 1 | 30 | 2 (0)| 00:00:01 |
|* 2 | INDEX UNIQUE SCAN | PK_DEPT | 1 | | 1 (0)| 00:00:01 |
---------------------------------------------------------------------------------------
===========================================================
SQL_ID 0mpwgns2uh7uh
Plan hash value: 2852011669
初始執行計划對飲的sql handel:SQL_eb3a5ffc38442091
SQL> exec :temp := dbms_spm.load_plans_from_cursor_cache(sql_id => '0mpwgns2uh7uh',plan_hash_value => 2852011669,sql_handle => 'SQL_eb3a5ffc38442091');
PL/SQL procedure successfully completed.
SQL> select sql_handle,plan_name,origin,enabled,accepted,sql_text from dba_sql_plan_baselines where sql_text like 'select /*+ no_index(dept PK_DEPT) */ * from dept%';
SQL_HANDLE PLAN_NAME ORIGIN ENA ACC SQL_TEXT
------------------------------ ------------------------------ -------------- --- --- --------------------------------------------------------------------------------
SQL_eb3a5ffc38442091 SQL_PLAN_fqfkzzhw4884j0348d329 MANUAL-LOAD YES YES select /*+ no_index(dept PK_DEPT) */ * from dept where deptno='10'
SQL_eb3a5ffc38442091 SQL_PLAN_fqfkzzhw4884j0e23be79 MANUAL-LOAD YES YES select /*+ no_index(dept PK_DEPT) */ * from dept where deptno='10'
我們現在看到,改寫過的sql的新執行計划所對應的的sql plan baseline已經成功生成。
第三部:DROP掉原執行計划
SQL> var temp number
SQL> exec :temp := dbms_spm.drop_sql_plan_baseline(sql_handle => 'SQL_eb3a5ffc38442091',plan_name => 'SQL_PLAN_fqfkzzhw4884j0e23be79');
PL/SQL procedure successfully completed.
SQL> select sql_handle,plan_name,origin,enabled,accepted,sql_text from dba_sql_plan_baselines where sql_text like 'select /*+ no_index(dept PK_DEPT) */ * from dept%';
SQL_HANDLE PLAN_NAME ORIGIN ENA ACC SQL_TEXT
------------------------------ ------------------------------ -------------- --- --- --------------------------------------------------------------------------------
SQL_eb3a5ffc38442091 SQL_PLAN_fqfkzzhw4884j0e23be79 MANUAL-LOAD YES YES select /*+ no_index(dept PK_DEPT) */ * from dept where deptno='10'
可以看到,現在只剩下改寫過的sql對應的執行計划對應的sql plan baseline。
重新執行sql
select /*+ index(dept PK_DEPT) */ * from dept where deptno='10';
現在就是走索引。