Oracle 統計量NO_INVALIDATE引數配置(上)
Oracle統計量對於CBO執行是至關重要的。RBO是建立在資料結構的基礎上的,DDL結構、約束會將SQL語句分為不同的成本結構等級。而CBO是在資料結構的基礎上,加入資料表細粒度資訊,將成本結構細化為成本cost值。
相對於資料表的DDL結構,統計量反映了當下資料表資料分佈情況,可變性更強。我們經常遇到這樣的場景,資料匯入操作之後,原有一個執行良好的作業突然效率低下。當我們手工收集一下統計量之後,作業效率提升。這種現象也就是反映了統計量和執行計劃的關係。
SGA中的shared pool是進行執行計劃快取的位置。Shared Cursor是SQL語句共享的主要物件。一句SQL語句,如果在Shared Pool中有快取的執行計劃。這個時候,有新的統計量收集動作,有新統計量收集到資料字典中,進而以為了新的執行計劃需求。那麼,Oracle是如何進行抉擇呢?
答案就是dbms_stats的no_invalidate引數。透過不同的引數配置,可以實現對Oracle失效共享遊標行為的控制。
1、no_invalidate引數
No_invalidate引數從字面上比較糾結。No和in都是否定含義,“負負得正”。引數含義就是validate,也就是是否有效。它決定了新統計量生成之後,如何處理此時已經生成的執行計劃,也就是在Shared Pool中的執行計劃。
統計量決定SQL執行計劃,是CBO的一個特徵。但是這個過程是針對新生成的執行計劃,也就是新的Parse過程。對於已經生成的執行計劃,Oracle是透過no_invalidate引數來處理shared cursor的失效過程。
一個物件(資料表、索引)新統計量生成之後,最簡單的方法是一次性將在Shared Pool中有依賴關係的shared cursor失效。下一次再進行SQL執行的時候,必然會用新的執行計劃Parse解析過程。另一個極端是無視新統計量的差異,維持現有的Shared Cursor,不會去讓其失效。
從效能角度看,兩個極端都是有其問題的。如果是一次性將其全部失效,會引起後續作業過程的“解析峰值”。因為,如果系統負載比較高,突然間快取的執行計劃全部被失效,Oracle作業必然要進行一些額外的成本進行執行計劃重新生成。這個會體現在系統執行有一個峰值。
如果不將共享遊標失效,那麼新的統計量不會很快體現在更好執行計劃生成的過程。效能提升無從談起。
所以,是否將遊標失效,是一個“左右為難”的問題。
在Oracle中,no_invalidate引數包括三個取值。
SQL> select * from v$version;
BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - Production
PL/SQL Release 11.2.0.1.0 - Production
CORE 11.2.0.1.0 Production
TNS for Linux: Version 11.2.0.1.0 - Production
NLSRTL Version 11.2.0.1.0 - Production
-- no_invalidate - Do not invalide the dependent cursors if set to TRUE.
-- The procedure invalidates the dependent cursors immediately
-- if set to FALSE.
-- Use DBMS_STATS.AUTO_INVALIDATE to have oracle decide when to
-- invalidate dependend cursors. This is the default. The default
-- can be changed using set_param procedure.
-- When the 'cascade' argument is specified, not pertinent with certain
-- types of indexes described in the gather_index_stats section.
Oracle支援true、false和dbms_stats.auto_invalidate取值。如果取值為true,表示不進行遊標失效動作,原有的shared cursor保持原有狀態。如果取值為false,表示將統計量物件相關的所有cursor全部失效。如果設定為auto_invalidate,根據官方文件,Oracle自己決定shared cursor失效動作。
從10G開始,Oracle就將auto_invalidate作為預設的統計量收集行為。
SQL> select dbms_stats.get_param(pname => 'no_invalidate') from dual;
DBMS_STATS.GET_PARAM(PNAME=>'N
--------------------------------------------------------------------------------
DBMS_STATS.AUTO_INVALIDATE
下面,筆者將透過一系列的實驗,來證明no_invalidate引數取值的效果。
2、no_invalidate取值為YES
取值為YES,表示不經心共享遊標失效動作,即使這個過程中,共享的遊標已經不是最優的執行計劃。
我們建立實驗資料表。
SQL> create table t as select * from dba_objects;
Table created
SQL> create index idx_t_id on t(object_id);
Index created
--第一次統計量收集
SQL> exec dbms_stats.gather_table_stats(user,'T',cascade => true);
PL/SQL procedure successfully completed
目標SQL語句,注意:出於篇幅原因,筆者將結果遮蔽。
SQL> select /*+demo*/object_id, owner from t where object_id=1000;
統計資訊
----------------------------------
164 recursive calls
0 db block gets
23 consistent gets
0 physical reads
(有省略……)
1 rows processed
此時shared pool中情況如下,出現第一個執行計劃快取物件。
SQL> select sql_id, executions, version_count from v$sqlarea where sql_text like 'select /*+demo*/%';
SQL_ID EXECUTIONS VERSION_COUNT
------------- ---------- -------------
cnb0ktgvms6vq 1 1
SQL> select * from table(dbms_xplan.display_cursor(sql_id=>'cnb0ktgvms6vq'));
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
SQL_ID cnb0ktgvms6vq, child number 0
-------------------------------------
select /*+demo*/object_id, owner from t where object_id=1000
Plan hash value: 514881935
--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Ti
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 2 (100)|
| 1 | TABLE ACCESS BY INDEX ROWID| T | 1 | 11 | 2 (0)| 00
|* 2 | INDEX RANGE SCAN | IDX_T_ID | 1 | | 1 (0)| 00
--------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("OBJECT_ID"=1000)
19 rows selected
此時,最優的執行計劃是索引路徑。在shared pool中有一個父遊標和子游標(version count=1),執行次數為1。
第二次執行之後,Shared Pool中有共享現象。相同的共享遊標執行兩次。
SQL> select sql_id, executions, version_count from v$sqlarea where sql_text like 'select /*+demo*/%';
SQL_ID EXECUTIONS VERSION_COUNT
------------- ---------- -------------
cnb0ktgvms6vq 2 1
之後,我們更新資料,修改資料分佈結構。
SQL> update t set object_id=1000;
72729 rows updated
SQL> commit;
Commit complete
此時,如果執行SQL語句,我們發現依然是使用原有的索引路徑。此時全部T表中object_id都是1000,走索引不是好的選擇。
SQL> select /*+demo*/object_id, owner from t where object_id=1000;
已選擇72729行。
統計資訊
------------------------------------------
0 recursive calls
0 db block gets
11157 consistent gets
0 physical reads
72729 rows processed
SQL> select sql_id, executions, version_count from v$sqlarea where sql_text like 'select /*+demo*/%';
SQL_ID EXECUTIONS VERSION_COUNT
------------- ---------- -------------
cnb0ktgvms6vq 3 1
此時的路徑依然是Index Range Scan。這個明顯是由於統計量的過時,外加遊標共享,引起的錯誤路徑。下面我們重新收集一下統計量,採用no_invaliate為true的情況。
SQL> exec dbms_stats.flush_database_monitoring_info;
PL/SQL procedure successfully completed
SQL> exec dbms_stats.gather_table_stats(user,'T',cascade => true,no_invalidate => true,method_opt => 'for columns size 10 object_id');
PL/SQL procedure successfully completed
新統計量生成,我們使用explain plan檢視一下,此時SQL應該採用的執行計劃是什麼?
SQL> explain plan for select /*+demo*/object_id, owner from t where object_id=1000;
Explained
SQL> select * from table(dbms_xplan.display);
PLAN_TABLE_OUTPUT
--------------------------------------------------------
Plan hash value: 1601196873
---------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 72722 | 639K| 266 (1)| 00:00:04 |
|* 1 | TABLE ACCESS FULL| T | 72722 | 639K| 266 (1)| 00:00:04 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("OBJECT_ID"=1000)
13 rows selected
此時,FTS全表掃描是更好的選擇。但是我們檢視一下實際執行時候,路徑情況。
SQL> select /*+demo*/object_id, owner from t where object_id=1000;
已選擇72729行。
統計資訊
----------------------------------------------------------
0 recursive calls
0 db block gets
10907 consistent gets
0 physical reads
0 redo size
72729 rows processed
SQL> select sql_id, executions, version_count from v$sqlarea where sql_text like 'select /*+demo*/%';
SQL_ID EXECUTIONS VERSION_COUNT
------------- ---------- -------------
cnb0ktgvms6vq 4 1
此時,Oracle依然選擇了原來的Index路徑,原有的shared cursor沒有失效!!如果我們此時將shared pool清空,新的FTS執行計劃也就生成。
SQL> alter system flush shared_pool;
System altered
SQL> select sql_id, executions, version_count from v$sqlarea where sql_text like 'select /*+demo*/%';
SQL_ID EXECUTIONS VERSION_COUNT
------------- ---------- -------------
SQL> select /*+demo*/object_id, owner from t where object_id=1000;
已選擇72729行。
統計資訊
----------------------------------------------------------
243 recursive calls
0 db block gets
5855 consistent gets
0 physical reads
72729 rows processed
--新的shared cursor形成
SQL> select sql_id, executions, version_count from v$sqlarea where sql_text like 'select /*+demo*/%';
SQL_ID EXECUTIONS VERSION_COUNT
------------- ---------- -------------
cnb0ktgvms6vq 1 1
--FTS執行計劃
SQL> select * from table(dbms_xplan.display_cursor(sql_id => 'cnb0ktgvms6vq'));
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
SQL_ID cnb0ktgvms6vq, child number 0
-------------------------------------
select /*+demo*/object_id, owner from t where object_id=1000
Plan hash value: 1601196873
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 266 (100)| |
|* 1 | TABLE ACCESS FULL| T | 72722 | 639K| 266 (1)| 00:00:04 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("OBJECT_ID"=1000)
18 rows selected
結論:當我們使用no_invalidate為true的時候,原有的shared cursor不會被失效,可以支援共享。只有當被age out或者flush out出shared pool之後,新執行計劃才能生成。
3、no_invalidate=false
下面我們看看取值為false的情況,實驗場景相同。為避免影響,我們重新構建資料表。
SQL> drop table t purge;
Table dropped
SQL> alter system flush shared_pool;
System altered
SQL> create table t as select * from dba_objects;
Table created
SQL> create index idx_t_id on t(object_id);
Index created
SQL> exec dbms_stats.gather_table_stats(user,'T',cascade => true);
PL/SQL procedure successfully completed
第一次執行SQL語句,形成Index路徑執行計劃。
SQL> select /*+demo*/object_id, owner from t where object_id=1000;
統計資訊
----------------------------------------------------------
164 recursive calls
0 db block gets
23 consistent gets
1 rows processed
SQL> select sql_id, executions, version_count from v$sqlarea where sql_text like 'select /*+demo*/%';
SQL_ID EXECUTIONS VERSION_COUNT
------------- ---------- -------------
cnb0ktgvms6vq 1 1
SQL> select * from table(dbms_xplan.display_cursor(sql_id=>'cnb0ktgvms6vq'));
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
SQL_ID cnb0ktgvms6vq, child number 0
-------------------------------------
select /*+demo*/object_id, owner from t where object_id=1000
Plan hash value: 514881935
--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Ti
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 2 (100)|
| 1 | TABLE ACCESS BY INDEX ROWID| T | 1 | 11 | 2 (0)| 00
|* 2 | INDEX RANGE SCAN | IDX_T_ID | 1 | | 1 (0)| 00
--------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("OBJECT_ID"=1000)
19 rows selected
第二次執行相同SQL,我們可以看到生成的shared cursor進行共享。
SQL> select sql_id, executions, version_count, first_load_time from v$sqlarea where sql_text like 'select /*+demo*/%';
SQL_ID EXECUTIONS VERSION_COUNT FIRST_LOAD_TIME
------------- ---------- ------------- ----------------------------------------------------------------------------
cnb0ktgvms6vq 2 1 2014-01-06/00:04:29
修改資料object_id取值,改變資料分佈。
SQL> update t set object_id=1000;
72729 rows updated
SQL> commit;
Commit complete
第三次執行。
SQL> select /*+demo*/object_id, owner from t where object_id=1000;
已選擇72729行。
統計資訊
--------------------------------------
0 recursive calls
0 db block gets
11157 consistent gets
72729 rows processed
此時shared cursor狀態如下:
SQL> select sql_id, executions, version_count, first_load_time from v$sqlarea where sql_text like 'select /*+demo*/%';
SQL_ID EXECUTIONS VERSION_COUNT FIRST_LOAD_TIME
------------- ---------- ------------- ----------------------------------------------------------------------------
cnb0ktgvms6vq 3 1 2014-01-06/00:04:29
執行計劃是進行Index Range Scan動作。
SQL> select * from table(dbms_xplan.display_cursor(sql_id=>'cnb0ktgvms6vq'));
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
SQL_ID cnb0ktgvms6vq, child number 0
-------------------------------------
select /*+demo*/object_id, owner from t where object_id=1000
Plan hash value: 514881935
--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Ti
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 2 (100)|
| 1 | TABLE ACCESS BY INDEX ROWID| T | 1 | 11 | 2 (0)| 00
|* 2 | INDEX RANGE SCAN | IDX_T_ID | 1 | | 1 (0)| 00
--------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("OBJECT_ID"=1000)
19 rows selected
收集統計量,使用no_invalidate為false取值。
SQL> exec dbms_stats.flush_database_monitoring_info;
PL/SQL procedure successfully completed
SQL> exec dbms_stats.gather_table_stats(user,'T',cascade => true,no_invalidate => false,method_opt => 'for columns size 10 object_id');
PL/SQL procedure successfully completed
第四次執行過程。
SQL> select /*+demo*/object_id, owner from t where object_id=1000;
已選擇72729行。
統計資訊
----------------------------
141 recursive calls
0 db block gets
5835 consistent gets
72729 rows processed
SQL> select sql_id, executions, version_count, first_load_time from v$sqlarea where sql_text like 'select /*+demo*/%';
SQL_ID EXECUTIONS VERSION_COUNT FIRST_LOAD_TIME
------------- ---------- ------------- -------------------
cnb0ktgvms6vq 1 1 2014-01-06/00:04:29
注意:在相同的sql_id情況下,version_count和executions都為1。Executions是不可能減少的。所以,這個父遊標是新生成的!
此時,執行計劃如下:
SQL> select * from table(dbms_xplan.display_cursor(sql_id=>'cnb0ktgvms6vq'));
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
SQL_ID cnb0ktgvms6vq, child number 0
-------------------------------------
select /*+demo*/object_id, owner from t where object_id=1000
Plan hash value: 1601196873
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 266 (100)| |
|* 1 | TABLE ACCESS FULL| T | 72722 | 639K| 266 (1)| 00:00:04 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("OBJECT_ID"=1000)
18 rows selected
這也就是說明了,新的執行計劃已經生成了!也就是原有的遊標被廢棄。
結論:當我們收集統計量使用no_invalidate為false的時候,原有的共享遊標被失效,下一次在執行SQL的時候,Oracle會重新為其生成執行計劃,也就是一次hard parse過程。
True和false取值是比較簡單的。我們接下來討論dbms_stats.auto_invalidate取值情況。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/25462274/viewspace-2122401/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Oracle 統計量NO_INVALIDATE引數配置(下)Oracle
- Oracle收集統計資訊之NO_INVALIDATE引數Oracle
- oracle dbms_stats(no_invalidate引數)Oracle
- oracle引數配置Oracle
- Oracle rman 配置引數Oracle
- ORACLE 配置event引數Oracle
- SAP配置系統引數
- AIX 系統引數配置AI
- oracle RMAN引數配置詳解Oracle
- MySQL 配置InnoDB配置非持久優化器統計資訊引數MySql優化
- AIX 系統引數配置 -- 轉AI
- ORACLE初始化引數的配置Oracle
- 在Solaris10上配置核心引數
- linux系統基於oracle的核心引數配置說明LinuxOracle
- SciTech-Mathmatics-Probability+Statistics-Population:Region-統計量與引數估計
- oracle11g引數檔案配置Oracle
- 部分Oracle 配置檔案引數含義Oracle
- 更改oracle系統引數例子Oracle
- 數理統計基礎 統計量
- Oracle group by與case when統一單位後統計數量Oracle
- 常用的jvm配置引數 :永久區引數配置JVM
- Oracle:歸檔量統計Oracle
- AIX作業系統核心引數配置AI作業系統
- oracle sga配置相關的os 核心引數Oracle
- 幾個引數配置的計算公式公式
- Oracle引數-隱藏引數Oracle
- jvm引數配置JVM
- JavaWeb引數配置JavaWeb
- Laravel 數量統計優化Laravel優化
- ArcGIS工具 - 統計工具數量
- oracle 引數Oracle
- 卷積神經網路的引數量和計算量卷積神經網路
- 【統計】能夠在session級別和system級別修改的oracle引數統計SessionOracle
- ORACLE安裝核心引數配置_linux平臺OracleLinux
- ORACLE RAC GUARD配置引數——RAC GUARD概念和管理Oracle
- 留存統計 引數聚合函式函式
- Solaris10的上安裝Oracle時需要調整的系統引數Oracle
- 如何計算PHP函式中傳遞的引數數量PHP函式