oracle 11g 變數窺視和acs最佳實踐
select t.column_name,
t.num_distinct,
t.num_buckets,
t.last_analyzed,
t.HISTOGRAM
from dba_tab_col_statistics t
where table_name = 'TI_O_YWTOCREDIT'
關於histogram總共有兩種,一種是HEIGHT BALANCED,另外一種是FREQUENCY,這個是oracle是根據NDV來進行選擇
如果有254個以下的非空的不同值,就是FREQUENCY的柱狀圖
如果有254個以上的非空的不同值,就是HEIGHT BALANCED的柱狀圖
HEIGHT BALANCED 高度平衡直方圖(a.ENDPOINT_NUMBER(代表桶號),a.ENDPOINT_VALUE )
FREQUENCY 頻率直方圖(a.ENDPOINT_NUMBER(代表桶號),a.ENDPOINT_VALUE )
今天談談下面這幾個引數對資料庫效能和穩定性的影響:
cursor_sharing:遊標共享
_optim_peek_user_binds:繫結變數窺視
_optimizer_adaptive_cursor_sharing:自適應遊標共享(簡稱ACS),一般還包括另外兩個_optimizer_extended_cursor_sharing和_optimizer_extended_cursor_sharing_rel 引數)
_optim_peek_user_binds和ACS:
在10g,因為沒有ACS,一般建議客戶關閉繫結變數窺視功能的情況多一些。
在11g,很多客戶還是將繫結變數窺視和ACS都關閉了,原因有的是資料庫從1g升級而來,升級後沒有改,還有就是因為ACS早期版本有一些bug。
其實這是兩個很好的引數,可以在程式碼寫的不是太好的情況下,也能獲得比較好的效能。雖然ACS可能還有一些小bug沒有解決(有的bug是在很特殊的情況下才會觸發),
到了11204版本應該都不是大問題了。老虎劉建議還是都開啟比較好。
最重要的引數還在下面,如果做到了下面這兩點,上面兩個引數就顯得不是那麼重要了:
首先,cursor_sharing這個引數對系統效能和穩定性都非常重要,可惜經常被忽略,建議使用該引數的預設值:
即 cursor_sharing=EXACT (而不是FORCE或similar)
這要求應該使用繫結變數的地方,必須使用繫結變數。這個對於OLTP系統來說是鐵律,不容置疑,
cursor_sharing=FORCE通常就是為了解決該使用繫結變數而沒有使用繫結變數的情況。
前年在網上看到一個廣為流傳的某水果公司的AWR報告,居然設定 cursor_sharing= FORCE ,令人感嘆啊。
其次,還有一個重要的補充條件:
不該使用繫結變數的地方,不用繫結變數:對那些唯一值較少的欄位,特別是資料分佈不均的情況,
不建議使用繫結變數。如type、status等欄位,我們建議使用常量:where type=1 and status=2。
這種情況如果使用了繫結變數,就是繫結變數窺視和ACS發揮作用的時候。
如果cursor_sharing=FORCE;或者cursor_sharing=EXACT,但是在資料分佈不均的欄位上也使用了繫結變數(兩者基本上是等同的,雖然後一種略好於前一種情況),
那麼就要考慮“繫結變數窺視”和“自適應遊標”兩個引數的影響了。
看下面幾種情況:
1、關閉“繫結變數窺視”(預設是開啟):
ACS同時失效,這時系統的穩定性好(不會因為繫結變數的不同,發生執行計劃改變),
但是整體效能會下降:因為不能窺視繫結變數,只能按照欄位是資料分佈均勻的情況來計算,
在能否使用索引,返回行源的估值上,都會出現較大的偏差,有時可能會配合使用hint來提高SQL效能。
2、如果開啟了“繫結變數窺視”而不開啟ACS(預設是開啟):
那麼系統就會極不穩定:比如硬解析窺視到一個繫結變數適合全表掃描的執行計劃,不管接下來的繫結變數是否能使用索引,
都會一直全表掃描下去,直到下次硬解析時再次窺視繫結變數才可能重新生成新的執行計劃。
3、如果開啟“繫結變數窺視”,同時開啟ACS:
這種情況在解決了一部分穩定性的同時,兼顧了效能。也是11g新增的ACS比10g沒有ACS進步的地方:
執行計劃不再從一而終,而是會根據繫結變數的不同,不是很及時的做出調整:
比如第一次窺視到的繫結變數適合全表掃描,那麼第二次即使使用的繫結變數適合走索引,也還是會使用全表掃描的執行計劃,
下一次再次執行就會糾正為使用索引的執行計劃(具體請參考ACS的實現原理)。
繫結變數窺視和ACS這兩個引數是與直方圖資訊緊密聯絡在一起的,關閉直方圖收集,也就相當於關閉了繫結變數窺視和ACS,即使開啟了這兩個引數。
直方圖能較為準確的反映資料分佈不均欄位的資料分佈情況,一般使用預設選項(auto),
某些特殊情況可以補充或去掉某些欄位的直方圖資訊。一些客戶在資料庫級關閉收集直方圖的做法是不建議的。
總結:
最佳實踐:
cursor_sharing=EXACT + 合理使用繫結變數(合理就是:類似ID、account_no等唯一值等於或接近錶行數的欄位,必須使用繫結變數;
而type、status等唯一值少且數分別不均的欄位,不使用繫結變數)。
繫結變數窺視和ACS保持預設開啟狀態。
特殊情況:
1、欄位唯一值有一定的數量(介於少與多之間),比如1000個,如果資料分佈均勻,則可以使用繫結變數。
如果欄位分佈不均,則把佔比多的幾個值,使用常量,其他值使用繫結變數。
2、欄位唯一值少,還有經常互相轉變的情況,比如常見的工單處理表:沒有處理的狀態是0,處理後的狀態是1,
夜間統計資訊收集後,由於欄位值的不穩定,統計資訊經常不能反映表的實時資料分佈情況,這種情況談是否使用繫結變數已沒有意義,
涉及這類表的SQL,可以關閉欄位上的直方圖收集,再配合rownum和hint 來提高SQL效率和穩定性,
必要時還可以使用dynamic_sampling(動態取樣)來輔助最佳化器做出正確的執行計劃。
最差組合:
cursor_sharing=FORCE
_optim_peek_user_binds=TRUE(開啟繫結變數窺視)
_optimizer_adaptive_cursor_sharing=FALSE(關閉ACS,還有其他兩個引數也要一起設定)
執行計劃不穩定的同時還會帶來低效能。
使用ACS的前提條件:
1.繫結變數使用變數窺視;
2.繫結變數的列上使用直方圖;
關閉acs步驟:
我們先來看看跟ACS相關的三個隱藏引數,是用來控制是否啟用ACS
col ksppinm for a30
col ksppstvl for a20
col ksppdesc for a35
SELECT ksppinm, ksppstvl, ksppdesc
FROM x$ksppi x, x$ksppcv y
WHERE x.indx = y.indx AND ksppinm = '_optimizer_adaptive_cursor_sharing';
KSPPINM KSPPSTVL KSPPDESC
------------------------------ -------------------- -----------------------------------
_optimizer_adaptive_cursor_sha TRUE optimizer adaptive cursor sharing
ring
SELECT ksppinm, ksppstvl, ksppdesc
FROM x$ksppi x, x$ksppcv y
WHERE x.indx = y.indx AND ksppinm = '_optimizer_extended_cursor_sharing';
KSPPINM KSPPSTVL KSPPDESC
------------------------------ -------------------- -----------------------------------
_optimizer_extended_cursor_sha UDO optimizer extended cursor sharing
ring
SELECT ksppinm, ksppstvl, ksppdesc
FROM x$ksppi x, x$ksppcv y
WHERE x.indx = y.indx AND ksppinm = '_optimizer_extended_cursor_sharing_rel';
KSPPINM KSPPSTVL KSPPDESC
------------------------------ -------------------- -----------------------------------
_optimizer_extended_cursor_sha SIMPLE optimizer extended cursor sharing f
ring_rel
所以如果我們要關閉ACS,使用如下的命令
alter system set "_optimizer_extended_cursor_sharing_rel"=none;
alter system set "_optimizer_extended_cursor_sharing"=none;
alter system set "_optimizer_adaptive_cursor_sharing"=false;
關於直方圖案例:
下面我們就來進行實驗
準備一張測試表,資料分佈極不均勻
20:10:16 scott@prod> select count(*) from test where object_id=1;
COUNT(*)
-------------
2400000
Elapsed: 00:00:00.60
20:11:41 scott@prod> select count(*) from test;
COUNT(*)
-------------
2406496
20:07:06 scott@prod> create index idx_test on test(object_id);
Index created.
收集柱狀圖
exec dbms_stats.gather_table_stats('SCOTT','TEST',method_opt=>'for all columns size skewonly');
20:14:41 scott@prod> select COLUMN_NAME,HISTOGRAM from user_tab_columns where TABLE_NAME='TEST' and column_name='OBJECT_ID';
COLUMN_NAME HISTOGRAM
------------------------------ ---------------
OBJECT_ID HEIGHT BALANCED
關於histogram總共有兩種,一種是HEIGHT BALANCED,另外一種是FREQUENCY,這個是oracle是根據NDV來進行選擇
如果有254個以下的非空的不同值,就是FREQUENCY的柱狀圖
如果有254個以上的非空的不同值,就是HEIGHT BALANCED的柱狀圖
關於柱狀圖的其他的特性,我會在另外的文章中再進行總結
由於set autotrace on 不是顯示的實際的執行計劃,是根據統計資訊進行估算的,並不一定能夠反應真是的執行計劃,我們使用
select * from table(dbms_xplan.display_cursor('sql_id',null,'advanced'));來顯示正確的執行計劃
20:26:37 scott@prod> var x number
20:26:51 scott@prod> exec :x:=48056
PL/SQL procedure successfully completed.
20:27:03 scott@prod> select * from test where object_id=:x;
20:35:50 sys@prod> select sql_id,sql_text,EXECUTIONS from v$sql where sql_text ='select * from test where object_id=:x';
SQL_ID SQL_TEXT EXECUTIONS
------------- ---------------------------------------- -------------
fk1y97mvmdu8f select * from test where object_id=:x 1
20:33:22 sys@prod> select * from table(dbms_xplan.display_cursor('fk1y97mvmdu8f',null,'advanced'));
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------------------------------------
SQL_ID fk1y97mvmdu8f, child number 0
-------------------------------------
select * from test where object_id=:x
Plan hash value: 2473784974
----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 4 (100)| |
| 1 | TABLE ACCESS BY INDEX ROWID| TEST | 3 | 285 | 4 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | IDX_TEST | 3 | | 3 (0)| 00:00:01 |
----------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
1 - SEL$1 / TEST@SEL$1
2 - SEL$1 / TEST@SEL$1
Outline Data
-------------
/*+
BEGIN_OUTLINE_DATA
IGNORE_OPTIM_EMBEDDED_HINTS
OPTIMIZER_FEATURES_ENABLE('11.2.0.3')
DB_VERSION('11.2.0.3')
ALL_ROWS
OUTLINE_LEAF(@"SEL$1")
INDEX_RS_ASC(@"SEL$1" "TEST"@"SEL$1" ("TEST"."OBJECT_ID"))
END_OUTLINE_DATA
*/
Peeked Binds (identified by position):
--------------------------------------
1 - :X (NUMBER): 48056
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("OBJECT_ID"=:X)
Column Projection Information (identified by operation id):
-----------------------------------------------------------
1 - "TEST"."OWNER"[VARCHAR2,30], "TEST"."OBJECT_NAME"[VARCHAR2,128],
"TEST"."SUBOBJECT_NAME"[VARCHAR2,30], "OBJECT_ID"[NUMBER,22],
"TEST"."DATA_OBJECT_ID"[NUMBER,22], "TEST"."OBJECT_TYPE"[VARCHAR2,19],
"TEST"."CREATED"[DATE,7], "TEST"."LAST_DDL_TIME"[DATE,7],
"TEST"."TIMESTAMP"[VARCHAR2,19], "TEST"."STATUS"[VARCHAR2,7],
"TEST"."TEMPORARY"[VARCHAR2,1], "TEST"."GENERATED"[VARCHAR2,1],
"TEST"."SECONDARY"[VARCHAR2,1], "TEST"."NAMESPACE"[NUMBER,22],
"TEST"."EDITION_NAME"[VARCHAR2,30]
2 - "TEST".ROWID[ROWID,10], "OBJECT_ID"[NUMBER,22]
可以看到此時走的是索引的範圍掃描
我們換object_id=1的,可以看到,總共240多萬的表,object_id=1的就有240萬,按道理這種肯定是應該走全表掃描的
exec :x:=1
select * from test where object_id=:x;
20:36:23 sys@prod> select sql_id,sql_text,EXECUTIONS from v$sql where sql_text ='select * from test where object_id=:x';
SQL_ID SQL_TEXT EXECUTIONS
------------- ---------------------------------------- -------------
fk1y97mvmdu8f select * from test where object_id=:x 2
可以看到這個子游標的執行次數已經變成了2次
在來看此時的執行計劃
20:38:39 sys@prod> select * from table(dbms_xplan.display_cursor('fk1y97mvmdu8f',null,'advanced'));
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------------------------------------
SQL_ID fk1y97mvmdu8f, child number 0
-------------------------------------
select * from test where object_id=:x
Plan hash value: 2473784974
----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 4 (100)| |
| 1 | TABLE ACCESS BY INDEX ROWID| TEST | 3 | 285 | 4 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | IDX_TEST | 3 | | 3 (0)| 00:00:01 |
----------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
1 - SEL$1 / TEST@SEL$1
2 - SEL$1 / TEST@SEL$1
Outline Data
-------------
/*+
BEGIN_OUTLINE_DATA
IGNORE_OPTIM_EMBEDDED_HINTS
OPTIMIZER_FEATURES_ENABLE('11.2.0.3')
DB_VERSION('11.2.0.3')
ALL_ROWS
OUTLINE_LEAF(@"SEL$1")
INDEX_RS_ASC(@"SEL$1" "TEST"@"SEL$1" ("TEST"."OBJECT_ID"))
END_OUTLINE_DATA
*/
Peeked Binds (identified by position):
--------------------------------------
1 - :X (NUMBER): 48056
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("OBJECT_ID"=:X)
Column Projection Information (identified by operation id):
-----------------------------------------------------------
1 - "TEST"."OWNER"[VARCHAR2,30], "TEST"."OBJECT_NAME"[VARCHAR2,128],
"TEST"."SUBOBJECT_NAME"[VARCHAR2,30], "OBJECT_ID"[NUMBER,22],
"TEST"."DATA_OBJECT_ID"[NUMBER,22], "TEST"."OBJECT_TYPE"[VARCHAR2,19],
"TEST"."CREATED"[DATE,7], "TEST"."LAST_DDL_TIME"[DATE,7],
"TEST"."TIMESTAMP"[VARCHAR2,19], "TEST"."STATUS"[VARCHAR2,7],
"TEST"."TEMPORARY"[VARCHAR2,1], "TEST"."GENERATED"[VARCHAR2,1],
"TEST"."SECONDARY"[VARCHAR2,1], "TEST"."NAMESPACE"[NUMBER,22],
"TEST"."EDITION_NAME"[VARCHAR2,30]
2 - "TEST".ROWID[ROWID,10], "OBJECT_ID"[NUMBER,22]
還是索引的範圍的掃描,現在現在走這個執行計劃是不合適的,由於繫結變數窺視,oracle走錯了執行計劃
select CHILD_NUMBER,PLAN_HASH_VALUE,EXECUTIONS,
BUFFER_GETS/EXECUTIONS BG_PER_EX,
IS_BIND_SENSITIVE BS,IS_BIND_AWARE BA,IS_SHAREABLE S
from v$sql where sql_id='fk1y97mvmdu8f';
select CHILD_NUMBER,PLAN_HASH_VALUE,EXECUTIONS,
BUFFER_GETS/EXECUTIONS BG_PER_EX,
IS_BIND_SENSITIVE BS,IS_BIND_AWARE BA,IS_SHAREABLE S
from v$sql where sql_id='fk1y97mvmdu8f';
CHILD_NUMBER PLAN_HASH_VALUE EXECUTIONS BG_PER_EX B B S
------------- --------------- ------------- ------------- - - -
0 2473784974 2 3913 Y N Y
從v$SQL中,可以看到這個cursor的資料,其中
IS_BIND_SENSITIVE=Y,表明使用繫結變數窺視來生成這次執行計劃,這次執行計劃是取決於這個繫結變數的,如果Oracle發現有其他的繫結變數出現,是可能生成其他的執行計劃的。
IS_BIND_AWARE=N,表明Oracle還沒有使用extended cursor sharing。
IS_SHAREABLE=Y,表明這個cursor可以被再次使用,即能夠共享;反之,設為N代表著這個cursor已經過時了,不會被再用了,這個cursor將會等待被age out出shared pool。
此時我們在執行這條sql語句
select sql_id,sql_text,child_number,executions,is_bind_sensitive,is_bind_aware,is_shareable,plan_hash_value from v$sql where sql_text ='select * from test where object_id=:x';
SQL_ID SQL_TEXT CHILD_NUMBER EXECUTIONS I I I PLAN_HASH_VALUE
------------- ---------------------------------------- ------------- ------------- - - - ---------------
fk1y97mvmdu8f select * from test where object_id=:x 0 2 Y N N 2473784974
fk1y97mvmdu8f select * from test where object_id=:x 1 1 Y Y Y 1357081020
可以看到重新生成了新的子游標
檢視新遊標的執行計劃
Elapsed: 00:00:00.13
06:14:18 sys@orcl> select * from table(dbms_xplan.display_cursor('fk1y97mvmdu8f',1,'advanced'));
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------------------------------------
SQL_ID fk1y97mvmdu8f, child number 1
-------------------------------------
select * from test where object_id=:x
Plan hash value: 1357081020
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 5528 (100)| |
|* 1 | TABLE ACCESS FULL| TEST | 1369K| 125M| 5528 (1)| 00:01:07 |
--------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
1 - SEL$1 / TEST@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")
FULL(@"SEL$1" "TEST"@"SEL$1")
END_OUTLINE_DATA
*/
Peeked Binds (identified by position):
--------------------------------------
1 - :X (NUMBER): 100
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("OBJECT_ID"=:X)
Column Projection Information (identified by operation id):
-----------------------------------------------------------
1 - "TEST"."OWNER"[VARCHAR2,30], "TEST"."OBJECT_NAME"[VARCHAR2,128],
"TEST"."SUBOBJECT_NAME"[VARCHAR2,30], "OBJECT_ID"[NUMBER,22],
"TEST"."DATA_OBJECT_ID"[NUMBER,22], "TEST"."OBJECT_TYPE"[VARCHAR2,19],
"TEST"."CREATED"[DATE,7], "TEST"."LAST_DDL_TIME"[DATE,7],
"TEST"."TIMESTAMP"[VARCHAR2,19], "TEST"."STATUS"[VARCHAR2,7],
"TEST"."TEMPORARY"[VARCHAR2,1], "TEST"."GENERATED"[VARCHAR2,1],
"TEST"."SECONDARY"[VARCHAR2,1], "TEST"."NAMESPACE"[NUMBER,22],
"TEST"."EDITION_NAME"[VARCHAR2,30]
如果此時我們賦予一個另外的值,我們再重新執行以下這個sql語句
06:18:00 kiwi@orcl> exec :x:=57603
PL/SQL procedure successfully completed.
Elapsed: 00:00:00.01
06:19:14 kiwi@orcl> select * from test where object_id=:x;
SQL_ID SQL_TEXT CHILD_NUMBER EXECUTIONS I I I PLAN_HASH_VALUE
------------- ---------------------------------------- ------------- ------------- - - - ---------------
fk1y97mvmdu8f select * from test where object_id=:x 0 2 Y N N 2473784974
fk1y97mvmdu8f select * from test where object_id=:x 1 4 Y N N 1357081020
fk1y97mvmdu8f select * from test where object_id=:x 2 1 Y Y Y 2473784974
我們此時如果在反覆的執行
06:26:39 kiwi@orcl> exec :x:=100
PL/SQL procedure successfully completed.
Elapsed: 00:00:00.00
06:27:05 kiwi@orcl> select * from test where object_id=:x;
SQL_ID SQL_TEXT CHILD_NUMBER EXECUTIONS I I I PLAN_HASH_VALUE
------------- ---------------------------------------- ------------- ------------- - - - ---------------
fk1y97mvmdu8f select * from test where object_id=:x 0 2 Y N N 2473784974
fk1y97mvmdu8f select * from test where object_id=:x 1 4 Y N N 1357081020
fk1y97mvmdu8f select * from test where object_id=:x 2 4 Y Y Y 2473784974
fk1y97mvmdu8f select * from test where object_id=:x 3 1 Y Y Y 1357081020
這樣重複的執行下來,oracle就會找到可以被重複使用是幾種執行計劃,並把他們共享出去
ACS相關的檢視
select child_number,predicate,range_id,low,high from v$sql_cs_selectivity
where sql_id='fk1y97mvmdu8f' order by 1;
CHILD_NUMBER PREDICATE RANGE_ID LOW HIGH
------------- ---------------------------------------- ------------- ---------- ----------
2 =X 0 0.000018 0.000022
3 =X 0 0.892941 1.091373
統計的各個子游標的選擇性範圍
select child_number,bind_set_hash_value,peeked,executions,rows_processed,buffer_gets
from v$sql_cs_statistics where sql_id='fk1y97mvmdu8f'
order by 1;
CHILD_NUMBER BIND_SET_HASH_VALUE P EXECUTIONS ROWS_PROCESSED BUFFER_GETS
------------- ------------------- - ------------- -------------- -------------
0 2251692860 Y 1 2 5
1 336594526 Y 1 1380000 110576
2 1059637539 Y 1 2 5
3 336594526 Y 1 1411 111
統計的各個子游標的執行情況
關於oracle收集直方圖時 method_opt中引數 auto跟skewonly的區別:
我們先來看官方文件中對這兩個引數的解釋
- AUTO : Oracle determines the columns to collect histograms based on data distribution and the workload of the columns.
- SKEWONLY : Oracle determines the columns to collect histograms based on the data distribution of the columns
從字面上解讀,AUTO和SKEWONLY的區別就在於收集histograms時ORACLE的選擇條件不同。
AUTO會根據column資料分佈情況以及column的負載情況進行判斷是不是收集這個column的histogram.
而SKEWONLY只會根據column的資料分佈情況決定是否收集histogram。
這裡,我會強調這個“只”字,因為在SKEWONLY時,只要Oracle覺得資料分佈可能不均,就會收集列的柱狀圖。
而AUTO時,即使某一個column它的資料分佈不均,但只要這個column的workload很低,或者說沒有workload,那麼Oracle就不會收集這個column的histogram
Oracle自行判斷的依據就來源於col_usage$字典基表,若表上的某一列曾在硬解析(hard parse)過的SQL語句中充當過predicate(通俗的說就是where後的condition)的話,我們認為此列上有收集柱狀圖的必要,那麼col_usage$上就會被加入該列曾充當predicate的記錄。當DBMS_STATS.GATHER_TABLE_STATS儲存過程以’SIZE AUTO’模式執行時,收集程式會檢查col_usage$基表以判斷哪些列之前曾充當過predicate,若充當過則說明該列有收集柱狀圖的價值。
t.num_distinct,
t.num_buckets,
t.last_analyzed,
t.HISTOGRAM
from dba_tab_col_statistics t
where table_name = 'TI_O_YWTOCREDIT'
關於histogram總共有兩種,一種是HEIGHT BALANCED,另外一種是FREQUENCY,這個是oracle是根據NDV來進行選擇
如果有254個以下的非空的不同值,就是FREQUENCY的柱狀圖
如果有254個以上的非空的不同值,就是HEIGHT BALANCED的柱狀圖
HEIGHT BALANCED 高度平衡直方圖(a.ENDPOINT_NUMBER(代表桶號),a.ENDPOINT_VALUE )
FREQUENCY 頻率直方圖(a.ENDPOINT_NUMBER(代表桶號),a.ENDPOINT_VALUE )
今天談談下面這幾個引數對資料庫效能和穩定性的影響:
cursor_sharing:遊標共享
_optim_peek_user_binds:繫結變數窺視
_optimizer_adaptive_cursor_sharing:自適應遊標共享(簡稱ACS),一般還包括另外兩個_optimizer_extended_cursor_sharing和_optimizer_extended_cursor_sharing_rel 引數)
_optim_peek_user_binds和ACS:
在10g,因為沒有ACS,一般建議客戶關閉繫結變數窺視功能的情況多一些。
在11g,很多客戶還是將繫結變數窺視和ACS都關閉了,原因有的是資料庫從1g升級而來,升級後沒有改,還有就是因為ACS早期版本有一些bug。
其實這是兩個很好的引數,可以在程式碼寫的不是太好的情況下,也能獲得比較好的效能。雖然ACS可能還有一些小bug沒有解決(有的bug是在很特殊的情況下才會觸發),
到了11204版本應該都不是大問題了。老虎劉建議還是都開啟比較好。
最重要的引數還在下面,如果做到了下面這兩點,上面兩個引數就顯得不是那麼重要了:
首先,cursor_sharing這個引數對系統效能和穩定性都非常重要,可惜經常被忽略,建議使用該引數的預設值:
即 cursor_sharing=EXACT (而不是FORCE或similar)
這要求應該使用繫結變數的地方,必須使用繫結變數。這個對於OLTP系統來說是鐵律,不容置疑,
cursor_sharing=FORCE通常就是為了解決該使用繫結變數而沒有使用繫結變數的情況。
前年在網上看到一個廣為流傳的某水果公司的AWR報告,居然設定 cursor_sharing= FORCE ,令人感嘆啊。
其次,還有一個重要的補充條件:
不該使用繫結變數的地方,不用繫結變數:對那些唯一值較少的欄位,特別是資料分佈不均的情況,
不建議使用繫結變數。如type、status等欄位,我們建議使用常量:where type=1 and status=2。
這種情況如果使用了繫結變數,就是繫結變數窺視和ACS發揮作用的時候。
如果cursor_sharing=FORCE;或者cursor_sharing=EXACT,但是在資料分佈不均的欄位上也使用了繫結變數(兩者基本上是等同的,雖然後一種略好於前一種情況),
那麼就要考慮“繫結變數窺視”和“自適應遊標”兩個引數的影響了。
看下面幾種情況:
1、關閉“繫結變數窺視”(預設是開啟):
ACS同時失效,這時系統的穩定性好(不會因為繫結變數的不同,發生執行計劃改變),
但是整體效能會下降:因為不能窺視繫結變數,只能按照欄位是資料分佈均勻的情況來計算,
在能否使用索引,返回行源的估值上,都會出現較大的偏差,有時可能會配合使用hint來提高SQL效能。
2、如果開啟了“繫結變數窺視”而不開啟ACS(預設是開啟):
那麼系統就會極不穩定:比如硬解析窺視到一個繫結變數適合全表掃描的執行計劃,不管接下來的繫結變數是否能使用索引,
都會一直全表掃描下去,直到下次硬解析時再次窺視繫結變數才可能重新生成新的執行計劃。
3、如果開啟“繫結變數窺視”,同時開啟ACS:
這種情況在解決了一部分穩定性的同時,兼顧了效能。也是11g新增的ACS比10g沒有ACS進步的地方:
執行計劃不再從一而終,而是會根據繫結變數的不同,不是很及時的做出調整:
比如第一次窺視到的繫結變數適合全表掃描,那麼第二次即使使用的繫結變數適合走索引,也還是會使用全表掃描的執行計劃,
下一次再次執行就會糾正為使用索引的執行計劃(具體請參考ACS的實現原理)。
繫結變數窺視和ACS這兩個引數是與直方圖資訊緊密聯絡在一起的,關閉直方圖收集,也就相當於關閉了繫結變數窺視和ACS,即使開啟了這兩個引數。
直方圖能較為準確的反映資料分佈不均欄位的資料分佈情況,一般使用預設選項(auto),
某些特殊情況可以補充或去掉某些欄位的直方圖資訊。一些客戶在資料庫級關閉收集直方圖的做法是不建議的。
總結:
最佳實踐:
cursor_sharing=EXACT + 合理使用繫結變數(合理就是:類似ID、account_no等唯一值等於或接近錶行數的欄位,必須使用繫結變數;
而type、status等唯一值少且數分別不均的欄位,不使用繫結變數)。
繫結變數窺視和ACS保持預設開啟狀態。
特殊情況:
1、欄位唯一值有一定的數量(介於少與多之間),比如1000個,如果資料分佈均勻,則可以使用繫結變數。
如果欄位分佈不均,則把佔比多的幾個值,使用常量,其他值使用繫結變數。
2、欄位唯一值少,還有經常互相轉變的情況,比如常見的工單處理表:沒有處理的狀態是0,處理後的狀態是1,
夜間統計資訊收集後,由於欄位值的不穩定,統計資訊經常不能反映表的實時資料分佈情況,這種情況談是否使用繫結變數已沒有意義,
涉及這類表的SQL,可以關閉欄位上的直方圖收集,再配合rownum和hint 來提高SQL效率和穩定性,
必要時還可以使用dynamic_sampling(動態取樣)來輔助最佳化器做出正確的執行計劃。
最差組合:
cursor_sharing=FORCE
_optim_peek_user_binds=TRUE(開啟繫結變數窺視)
_optimizer_adaptive_cursor_sharing=FALSE(關閉ACS,還有其他兩個引數也要一起設定)
執行計劃不穩定的同時還會帶來低效能。
使用ACS的前提條件:
1.繫結變數使用變數窺視;
2.繫結變數的列上使用直方圖;
關閉acs步驟:
我們先來看看跟ACS相關的三個隱藏引數,是用來控制是否啟用ACS
col ksppinm for a30
col ksppstvl for a20
col ksppdesc for a35
SELECT ksppinm, ksppstvl, ksppdesc
FROM x$ksppi x, x$ksppcv y
WHERE x.indx = y.indx AND ksppinm = '_optimizer_adaptive_cursor_sharing';
KSPPINM KSPPSTVL KSPPDESC
------------------------------ -------------------- -----------------------------------
_optimizer_adaptive_cursor_sha TRUE optimizer adaptive cursor sharing
ring
SELECT ksppinm, ksppstvl, ksppdesc
FROM x$ksppi x, x$ksppcv y
WHERE x.indx = y.indx AND ksppinm = '_optimizer_extended_cursor_sharing';
KSPPINM KSPPSTVL KSPPDESC
------------------------------ -------------------- -----------------------------------
_optimizer_extended_cursor_sha UDO optimizer extended cursor sharing
ring
SELECT ksppinm, ksppstvl, ksppdesc
FROM x$ksppi x, x$ksppcv y
WHERE x.indx = y.indx AND ksppinm = '_optimizer_extended_cursor_sharing_rel';
KSPPINM KSPPSTVL KSPPDESC
------------------------------ -------------------- -----------------------------------
_optimizer_extended_cursor_sha SIMPLE optimizer extended cursor sharing f
ring_rel
所以如果我們要關閉ACS,使用如下的命令
alter system set "_optimizer_extended_cursor_sharing_rel"=none;
alter system set "_optimizer_extended_cursor_sharing"=none;
alter system set "_optimizer_adaptive_cursor_sharing"=false;
關於直方圖案例:
下面我們就來進行實驗
準備一張測試表,資料分佈極不均勻
20:10:16 scott@prod> select count(*) from test where object_id=1;
COUNT(*)
-------------
2400000
Elapsed: 00:00:00.60
20:11:41 scott@prod> select count(*) from test;
COUNT(*)
-------------
2406496
20:07:06 scott@prod> create index idx_test on test(object_id);
Index created.
收集柱狀圖
exec dbms_stats.gather_table_stats('SCOTT','TEST',method_opt=>'for all columns size skewonly');
20:14:41 scott@prod> select COLUMN_NAME,HISTOGRAM from user_tab_columns where TABLE_NAME='TEST' and column_name='OBJECT_ID';
COLUMN_NAME HISTOGRAM
------------------------------ ---------------
OBJECT_ID HEIGHT BALANCED
關於histogram總共有兩種,一種是HEIGHT BALANCED,另外一種是FREQUENCY,這個是oracle是根據NDV來進行選擇
如果有254個以下的非空的不同值,就是FREQUENCY的柱狀圖
如果有254個以上的非空的不同值,就是HEIGHT BALANCED的柱狀圖
關於柱狀圖的其他的特性,我會在另外的文章中再進行總結
由於set autotrace on 不是顯示的實際的執行計劃,是根據統計資訊進行估算的,並不一定能夠反應真是的執行計劃,我們使用
select * from table(dbms_xplan.display_cursor('sql_id',null,'advanced'));來顯示正確的執行計劃
20:26:37 scott@prod> var x number
20:26:51 scott@prod> exec :x:=48056
PL/SQL procedure successfully completed.
20:27:03 scott@prod> select * from test where object_id=:x;
20:35:50 sys@prod> select sql_id,sql_text,EXECUTIONS from v$sql where sql_text ='select * from test where object_id=:x';
SQL_ID SQL_TEXT EXECUTIONS
------------- ---------------------------------------- -------------
fk1y97mvmdu8f select * from test where object_id=:x 1
20:33:22 sys@prod> select * from table(dbms_xplan.display_cursor('fk1y97mvmdu8f',null,'advanced'));
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------------------------------------
SQL_ID fk1y97mvmdu8f, child number 0
-------------------------------------
select * from test where object_id=:x
Plan hash value: 2473784974
----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 4 (100)| |
| 1 | TABLE ACCESS BY INDEX ROWID| TEST | 3 | 285 | 4 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | IDX_TEST | 3 | | 3 (0)| 00:00:01 |
----------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
1 - SEL$1 / TEST@SEL$1
2 - SEL$1 / TEST@SEL$1
Outline Data
-------------
/*+
BEGIN_OUTLINE_DATA
IGNORE_OPTIM_EMBEDDED_HINTS
OPTIMIZER_FEATURES_ENABLE('11.2.0.3')
DB_VERSION('11.2.0.3')
ALL_ROWS
OUTLINE_LEAF(@"SEL$1")
INDEX_RS_ASC(@"SEL$1" "TEST"@"SEL$1" ("TEST"."OBJECT_ID"))
END_OUTLINE_DATA
*/
Peeked Binds (identified by position):
--------------------------------------
1 - :X (NUMBER): 48056
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("OBJECT_ID"=:X)
Column Projection Information (identified by operation id):
-----------------------------------------------------------
1 - "TEST"."OWNER"[VARCHAR2,30], "TEST"."OBJECT_NAME"[VARCHAR2,128],
"TEST"."SUBOBJECT_NAME"[VARCHAR2,30], "OBJECT_ID"[NUMBER,22],
"TEST"."DATA_OBJECT_ID"[NUMBER,22], "TEST"."OBJECT_TYPE"[VARCHAR2,19],
"TEST"."CREATED"[DATE,7], "TEST"."LAST_DDL_TIME"[DATE,7],
"TEST"."TIMESTAMP"[VARCHAR2,19], "TEST"."STATUS"[VARCHAR2,7],
"TEST"."TEMPORARY"[VARCHAR2,1], "TEST"."GENERATED"[VARCHAR2,1],
"TEST"."SECONDARY"[VARCHAR2,1], "TEST"."NAMESPACE"[NUMBER,22],
"TEST"."EDITION_NAME"[VARCHAR2,30]
2 - "TEST".ROWID[ROWID,10], "OBJECT_ID"[NUMBER,22]
可以看到此時走的是索引的範圍掃描
我們換object_id=1的,可以看到,總共240多萬的表,object_id=1的就有240萬,按道理這種肯定是應該走全表掃描的
exec :x:=1
select * from test where object_id=:x;
20:36:23 sys@prod> select sql_id,sql_text,EXECUTIONS from v$sql where sql_text ='select * from test where object_id=:x';
SQL_ID SQL_TEXT EXECUTIONS
------------- ---------------------------------------- -------------
fk1y97mvmdu8f select * from test where object_id=:x 2
可以看到這個子游標的執行次數已經變成了2次
在來看此時的執行計劃
20:38:39 sys@prod> select * from table(dbms_xplan.display_cursor('fk1y97mvmdu8f',null,'advanced'));
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------------------------------------
SQL_ID fk1y97mvmdu8f, child number 0
-------------------------------------
select * from test where object_id=:x
Plan hash value: 2473784974
----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 4 (100)| |
| 1 | TABLE ACCESS BY INDEX ROWID| TEST | 3 | 285 | 4 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | IDX_TEST | 3 | | 3 (0)| 00:00:01 |
----------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
1 - SEL$1 / TEST@SEL$1
2 - SEL$1 / TEST@SEL$1
Outline Data
-------------
/*+
BEGIN_OUTLINE_DATA
IGNORE_OPTIM_EMBEDDED_HINTS
OPTIMIZER_FEATURES_ENABLE('11.2.0.3')
DB_VERSION('11.2.0.3')
ALL_ROWS
OUTLINE_LEAF(@"SEL$1")
INDEX_RS_ASC(@"SEL$1" "TEST"@"SEL$1" ("TEST"."OBJECT_ID"))
END_OUTLINE_DATA
*/
Peeked Binds (identified by position):
--------------------------------------
1 - :X (NUMBER): 48056
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("OBJECT_ID"=:X)
Column Projection Information (identified by operation id):
-----------------------------------------------------------
1 - "TEST"."OWNER"[VARCHAR2,30], "TEST"."OBJECT_NAME"[VARCHAR2,128],
"TEST"."SUBOBJECT_NAME"[VARCHAR2,30], "OBJECT_ID"[NUMBER,22],
"TEST"."DATA_OBJECT_ID"[NUMBER,22], "TEST"."OBJECT_TYPE"[VARCHAR2,19],
"TEST"."CREATED"[DATE,7], "TEST"."LAST_DDL_TIME"[DATE,7],
"TEST"."TIMESTAMP"[VARCHAR2,19], "TEST"."STATUS"[VARCHAR2,7],
"TEST"."TEMPORARY"[VARCHAR2,1], "TEST"."GENERATED"[VARCHAR2,1],
"TEST"."SECONDARY"[VARCHAR2,1], "TEST"."NAMESPACE"[NUMBER,22],
"TEST"."EDITION_NAME"[VARCHAR2,30]
2 - "TEST".ROWID[ROWID,10], "OBJECT_ID"[NUMBER,22]
還是索引的範圍的掃描,現在現在走這個執行計劃是不合適的,由於繫結變數窺視,oracle走錯了執行計劃
select CHILD_NUMBER,PLAN_HASH_VALUE,EXECUTIONS,
BUFFER_GETS/EXECUTIONS BG_PER_EX,
IS_BIND_SENSITIVE BS,IS_BIND_AWARE BA,IS_SHAREABLE S
from v$sql where sql_id='fk1y97mvmdu8f';
select CHILD_NUMBER,PLAN_HASH_VALUE,EXECUTIONS,
BUFFER_GETS/EXECUTIONS BG_PER_EX,
IS_BIND_SENSITIVE BS,IS_BIND_AWARE BA,IS_SHAREABLE S
from v$sql where sql_id='fk1y97mvmdu8f';
CHILD_NUMBER PLAN_HASH_VALUE EXECUTIONS BG_PER_EX B B S
------------- --------------- ------------- ------------- - - -
0 2473784974 2 3913 Y N Y
從v$SQL中,可以看到這個cursor的資料,其中
IS_BIND_SENSITIVE=Y,表明使用繫結變數窺視來生成這次執行計劃,這次執行計劃是取決於這個繫結變數的,如果Oracle發現有其他的繫結變數出現,是可能生成其他的執行計劃的。
IS_BIND_AWARE=N,表明Oracle還沒有使用extended cursor sharing。
IS_SHAREABLE=Y,表明這個cursor可以被再次使用,即能夠共享;反之,設為N代表著這個cursor已經過時了,不會被再用了,這個cursor將會等待被age out出shared pool。
此時我們在執行這條sql語句
select sql_id,sql_text,child_number,executions,is_bind_sensitive,is_bind_aware,is_shareable,plan_hash_value from v$sql where sql_text ='select * from test where object_id=:x';
SQL_ID SQL_TEXT CHILD_NUMBER EXECUTIONS I I I PLAN_HASH_VALUE
------------- ---------------------------------------- ------------- ------------- - - - ---------------
fk1y97mvmdu8f select * from test where object_id=:x 0 2 Y N N 2473784974
fk1y97mvmdu8f select * from test where object_id=:x 1 1 Y Y Y 1357081020
可以看到重新生成了新的子游標
檢視新遊標的執行計劃
Elapsed: 00:00:00.13
06:14:18 sys@orcl> select * from table(dbms_xplan.display_cursor('fk1y97mvmdu8f',1,'advanced'));
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------------------------------------
SQL_ID fk1y97mvmdu8f, child number 1
-------------------------------------
select * from test where object_id=:x
Plan hash value: 1357081020
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 5528 (100)| |
|* 1 | TABLE ACCESS FULL| TEST | 1369K| 125M| 5528 (1)| 00:01:07 |
--------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
1 - SEL$1 / TEST@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")
FULL(@"SEL$1" "TEST"@"SEL$1")
END_OUTLINE_DATA
*/
Peeked Binds (identified by position):
--------------------------------------
1 - :X (NUMBER): 100
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("OBJECT_ID"=:X)
Column Projection Information (identified by operation id):
-----------------------------------------------------------
1 - "TEST"."OWNER"[VARCHAR2,30], "TEST"."OBJECT_NAME"[VARCHAR2,128],
"TEST"."SUBOBJECT_NAME"[VARCHAR2,30], "OBJECT_ID"[NUMBER,22],
"TEST"."DATA_OBJECT_ID"[NUMBER,22], "TEST"."OBJECT_TYPE"[VARCHAR2,19],
"TEST"."CREATED"[DATE,7], "TEST"."LAST_DDL_TIME"[DATE,7],
"TEST"."TIMESTAMP"[VARCHAR2,19], "TEST"."STATUS"[VARCHAR2,7],
"TEST"."TEMPORARY"[VARCHAR2,1], "TEST"."GENERATED"[VARCHAR2,1],
"TEST"."SECONDARY"[VARCHAR2,1], "TEST"."NAMESPACE"[NUMBER,22],
"TEST"."EDITION_NAME"[VARCHAR2,30]
如果此時我們賦予一個另外的值,我們再重新執行以下這個sql語句
06:18:00 kiwi@orcl> exec :x:=57603
PL/SQL procedure successfully completed.
Elapsed: 00:00:00.01
06:19:14 kiwi@orcl> select * from test where object_id=:x;
SQL_ID SQL_TEXT CHILD_NUMBER EXECUTIONS I I I PLAN_HASH_VALUE
------------- ---------------------------------------- ------------- ------------- - - - ---------------
fk1y97mvmdu8f select * from test where object_id=:x 0 2 Y N N 2473784974
fk1y97mvmdu8f select * from test where object_id=:x 1 4 Y N N 1357081020
fk1y97mvmdu8f select * from test where object_id=:x 2 1 Y Y Y 2473784974
我們此時如果在反覆的執行
06:26:39 kiwi@orcl> exec :x:=100
PL/SQL procedure successfully completed.
Elapsed: 00:00:00.00
06:27:05 kiwi@orcl> select * from test where object_id=:x;
SQL_ID SQL_TEXT CHILD_NUMBER EXECUTIONS I I I PLAN_HASH_VALUE
------------- ---------------------------------------- ------------- ------------- - - - ---------------
fk1y97mvmdu8f select * from test where object_id=:x 0 2 Y N N 2473784974
fk1y97mvmdu8f select * from test where object_id=:x 1 4 Y N N 1357081020
fk1y97mvmdu8f select * from test where object_id=:x 2 4 Y Y Y 2473784974
fk1y97mvmdu8f select * from test where object_id=:x 3 1 Y Y Y 1357081020
這樣重複的執行下來,oracle就會找到可以被重複使用是幾種執行計劃,並把他們共享出去
ACS相關的檢視
select child_number,predicate,range_id,low,high from v$sql_cs_selectivity
where sql_id='fk1y97mvmdu8f' order by 1;
CHILD_NUMBER PREDICATE RANGE_ID LOW HIGH
------------- ---------------------------------------- ------------- ---------- ----------
2 =X 0 0.000018 0.000022
3 =X 0 0.892941 1.091373
統計的各個子游標的選擇性範圍
select child_number,bind_set_hash_value,peeked,executions,rows_processed,buffer_gets
from v$sql_cs_statistics where sql_id='fk1y97mvmdu8f'
order by 1;
CHILD_NUMBER BIND_SET_HASH_VALUE P EXECUTIONS ROWS_PROCESSED BUFFER_GETS
------------- ------------------- - ------------- -------------- -------------
0 2251692860 Y 1 2 5
1 336594526 Y 1 1380000 110576
2 1059637539 Y 1 2 5
3 336594526 Y 1 1411 111
統計的各個子游標的執行情況
關於oracle收集直方圖時 method_opt中引數 auto跟skewonly的區別:
我們先來看官方文件中對這兩個引數的解釋
- AUTO : Oracle determines the columns to collect histograms based on data distribution and the workload of the columns.
- SKEWONLY : Oracle determines the columns to collect histograms based on the data distribution of the columns
從字面上解讀,AUTO和SKEWONLY的區別就在於收集histograms時ORACLE的選擇條件不同。
AUTO會根據column資料分佈情況以及column的負載情況進行判斷是不是收集這個column的histogram.
而SKEWONLY只會根據column的資料分佈情況決定是否收集histogram。
這裡,我會強調這個“只”字,因為在SKEWONLY時,只要Oracle覺得資料分佈可能不均,就會收集列的柱狀圖。
而AUTO時,即使某一個column它的資料分佈不均,但只要這個column的workload很低,或者說沒有workload,那麼Oracle就不會收集這個column的histogram
Oracle自行判斷的依據就來源於col_usage$字典基表,若表上的某一列曾在硬解析(hard parse)過的SQL語句中充當過predicate(通俗的說就是where後的condition)的話,我們認為此列上有收集柱狀圖的必要,那麼col_usage$上就會被加入該列曾充當predicate的記錄。當DBMS_STATS.GATHER_TABLE_STATS儲存過程以’SIZE AUTO’模式執行時,收集程式會檢查col_usage$基表以判斷哪些列之前曾充當過predicate,若充當過則說明該列有收集柱狀圖的價值。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/25462274/viewspace-2131162/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Oracle 變數繫結與變數窺視合集Oracle變數
- oracle繫結變數窺視(zt)Oracle變數
- oracle 11g 新特性之動態繫結變數窺視(二)Oracle變數
- oracle 11g 新特性之動態繫結變數窺視(一)Oracle變數
- 【原創】Oracle 變數繫結與變數窺視合集Oracle變數
- oracle bind value peeking繫結變數窺視Oracle變數
- 15 個變數和方法命名的最佳實踐變數
- 變數(函式)命名最佳實踐變數函式
- Oracle 繫結變數窺探Oracle變數
- 繫結變數窺視測試案例變數
- Oracle 變數窺視引起執行計劃異常故障分析Oracle變數
- 轉METALINK一篇文章(變數窺視)變數
- 繫結變數窺測變數
- 關於oracle 11g acs的一點總結:Oracle
- Oracle 9i變數窺視引起執行計劃異常故障報告Oracle變數
- Java環境變數配置的最佳實踐和常見問題解決方案Java變數
- 繫結變數窺測的演變變數
- Kotlin 變數詳解:宣告、賦值與最佳實踐指南Kotlin變數賦值
- Dreyfus模型和最佳實踐模型
- PHP實踐之路(四)PHP中常量和變數PHP變數
- Vue最佳實踐和實用技巧Vue
- MySQL引數調優最佳實踐MySql
- 【TABLESPACE】Oracle表空間最佳實踐Oracle
- Oracle RAT介紹及最佳實踐Oracle
- Oracle高可用最佳實踐總結Oracle
- RAC 和 Oracle Clusterware 最佳實踐和初學者指南 (AIX)1526555.1OracleAI
- 繫結變數優缺點、使用、繫結變數窺探、 Oracle自適應共享遊標變數Oracle
- 繫結變數之繫結變數窺探(Bind Peeking)變數
- 微信ClickHouse實時數倉的最佳實踐
- 10g繫結變數窺探變數
- 【SQL 調優】繫結變數窺測SQL變數
- RDS MySQL引數調優最佳實踐MySql
- RAC 和 Oracle Clusterware 最佳實踐和初學者指南(平臺無關部分)Oracle
- 使用Java Optional類的最佳實踐 - oracleJavaOracle
- 最佳實踐(保持、清理ORACLE alert日誌)Oracle
- MVVM 最佳解讀和實踐MVVM
- RAC 和 Oracle Clusterware 最佳實踐和初學者指南 (AIX) (文件 ID 1526555.1)OracleAI
- 函數語言程式設計最佳實踐函數程式設計