轉過來學習:http://daizj.iteye.com/blog/1716867
從11g開始,表與索引的統計信息收集完畢後,可以選擇收集的統信息立即發佈,也可以選擇使新收集的統計信息處於pending狀態,待確定處於pending狀態的統計信息是安全的,再使處於pending狀態的統計信息發佈,這樣就會避免一些因爲收集統計信息立即發佈而導致SQL執行計劃走錯的災難。
在 11g 之前的版本中,DBMS_STATS 自動統計收集(Automatic Statistics Gathering)默認的閥值是 10%, 這個 10% 是不可以修改的。這對千變萬化的企業數據庫來說環境來說,有些死板,如果是個超大的表,默認的 10% 數據也是海量了,會把整個資源佔死。Oracle
11g 中,這個屬性可以通過修改 STALE_PERCENT 屬性來修改, 有全局(DBMS_STATS.SET_GLOBAL_PREFS )和表級別(DBMS_STATS.SET_TABLE_PREFS)兩種。
1 如何判斷是否有pending的統計信息需要生效?
SQL> Select dbms_stats.get_prefs('PUBLISH') publish from dual;
PUBLISH
--------------------------
TRUE
dbms_stats的get_prefs函數返回true,表示對象的統計信息收集後立即生效,如果返回flase,收集的統計信息將處於pending狀態。
2 如果查看相關的視圖
A 立即生效的統計信息可以通過以下字典可以查看
user_tab_stats
user_ind_stats
B pending狀態的統計信息可以通過以下字典可以查看
user_tab_pending_stats
user_ind_pending_stats
3 如何設置表或schema的統計信息的publish狀態
用dbms_stats的set_table_prefs或者set_schema_prefs過程可以在表級或schema表設置它們的統計信息是否立即生效,當我們設置tmp_test表的統計信息收集後處於pending狀態,那該表收集統計信息後,將存放於user_tab_pending_stats字典中。
SQL> Exec dbms_stats.set_table_prefs('yekai','tmp_test','publish','false');
PL/SQL procedure successfully completed.
SQL> select count(*) from user_tab_pending_stats;
COUNT(*)
----------
0
SQL> exec dbms_stats.gather_table_stats('yekai','tmp_test');
PL/SQL procedure successfully completed.
SQL> select count(*) from user_tab_pending_stats;
COUNT(*)
----------
1
4 如何測試並使用處於pending狀態的統計信息
在11g,新的參數optimizer_pending_statistics將可以來解決這個問題,當我們在session級設置optimizer_pending_statistics爲true時,我們就可以使用存放在user_*_pending_stats字典中的統計信息啦,當我們確保該處於pending狀態的統計信息是正確時,我們就可以決定是否使它們立即生效。
SQL> alter session set optimizer_pending_statistics = TRUE;
5 如何發佈處於pending狀態的統計信息
當測試過統計信息有效後,我們可以選擇發佈pending狀態的統計信息
SQL> exec dbms_stats.publish_pending_stats('yekai','tmp_test');
如果我們不需要該處於pending狀態的統計信息,可以選擇刪除這個pending的統計信息
SQL> exec dbms_stats.publish_pending_stats('yekai','tmp_test');
在CBO時代,SQL語句的執行計劃完全依賴於在數據字典中保存的統計量信息和優化器Optimizer的計算公式參數。從9i開始到現在的11gR2,我們說CBO優化器已經很成熟和完善。在通常情況下,我們的SQL都是可以獲取到較好的執行計劃以及執行效率的。
在實際工作中,我們經常會遇到執行計劃低效的情況。但是這種故障根源中,絕大多數的原因在於統計量的錯誤或者失效。錯誤的統計量連帶生成的就是不恰當的執行計劃,以至於低效的執行過程。在9i時代,RBO和CBO混合使用,讓我們經常需要自定義的統計量收集過程。
從10g開始,Oracle引入了自動收集統計量的作業,以保證數據字典中統計量正確反映數據對象狀態。這在很大程度上,緩解了由於數據變化導致的統計量過期問題。但是,我們在實際工作中,還是會發現執行計劃的突然變化。究其原因,就是某個時間點收集的統計量,也許不能反映數據的全貌(如中間表)。
1、統計量Pending
在系統運維中,我們常常希望維持SQL執行計劃的穩定。很多DBA和開發人員對於hint的依賴,很大程度上也是源於對CBO情況下,執行計劃對於統計量過於依賴,容易形成不穩定執行計劃。
那麼,我們SQL語句執行計劃的穩定性,就變成統計量的穩定性問題。更進一步,就是新的統計量更新,無論是否手動收集還是自動收集,能否促進SQL語句生成更高效的執行計劃。
所以,一種思路是:在新的統計量收集生成時,暫時不要生效投入執行計劃生成。等待最後確認統計量正確之後,再投入生產環境。
在Oracle 11g中,推出了統計量管理的一種新技術——Pending Statistic技術,提供了這種功能。
簡單的說,我們可以對一系列的數據表設置pending屬性。設置pending屬性之後,數據的統計量在數據字典中相當於已經鎖定Lock住。但新統計量生成之後,不是直接替換原有的數據,而是存放在pending數據字典中。
在pending字典中的統計量,默認情況下是不會參與SQL執行計劃的生產的。只有在進行SQL測試通過的時候,經過用戶手工的確定,纔會將其Publish出來,替換原有的統計量信息。
這樣,就給我們運維DBA一種維持執行計劃穩定的思路。通過固定統計量,將新統計量pending的方式將原有的統計量固定,從而穩定執行計劃。進而,對pending的統計量進行測試,只有在更好執行計劃的情況下,纔會替換原有的方案。
下面,我們通過實驗來驗證pending統計量的使用。
2、實驗環境構建
我們選擇11gR2進行實驗。
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
構建數據表T,以及對應的索引。注意,我們首先在數據表中不保存任何數據。
SQL> create table t as select * from dba_objects where 1=0;
Table created
SQL> create index idx_t_owner on t(owner);
Index created
SQL> create index idx_t_id on t(object_id);
Index created
在不顯式的收集統計量的情況下,是沒有對應的數據表統計量的。
SQL> select NUM_ROWS, BLOCKS EMPTY_BLOCKS, AVG_SPACE, CHAIN_CNT, AVG_ROW_LEN from user_tab_statistics where table_name='T';
NUM_ROWS EMPTY_BLOCKS AVG_SPACE CHAIN_CNT AVG_ROW_LEN
---------- ------------ ---------- ---------- -----------
SQL> select count(*) from user_tab_col_statistics where table_name='T';
COUNT(*)
----------
0
SQL> select BLEVEL, LEAF_BLOCKS, DISTINCT_KEYS, CLUSTERING_FACTOR NUM_ROWS from user_ind_statistics where index_name='IDX_T_OWNER';
BLEVEL LEAF_BLOCKS DISTINCT_KEYS NUM_ROWS
---------- ----------- ------------- ----------
0 0 0 0
收集統計量,獲取最新的數據分佈狀況。
SQL> exec dbms_stats.gather_table_stats(user,'T',cascade => true);
PL/SQL procedure successfully completed
當我們修改數據內容,沒有收集統計量,會存在新舊差異。
SQL> insert into t select * from dba_objects;
72202 rows inserted
SQL> commit;
Commit complete
SQL> select NUM_ROWS, BLOCKS EMPTY_BLOCKS, AVG_SPACE, CHAIN_CNT, AVG_ROW_LEN from user_tab_statistics where table_name='T';
NUM_ROWS EMPTY_BLOCKS AVG_SPACE CHAIN_CNT AVG_ROW_LEN
---------- ------------ ---------- ---------- -----------
0 0 0 0 0
3、Pending Statistics設置
在11g環境中,數據表、Schema都存在一個統計量相關參數PUBLISH,表示當有新統計量的時候,新統計量是否立即被publish出來,作爲最新的統計信息使用。
該參數的默認值爲TRUE。
SQL> select dbms_stats.get_prefs(pname => 'PUBLISH',ownname => 'SYS',tabname => 'T') from dual;
DBMS_STATS.GET_PREFS(PNAME=>'P
-------------------------------------------------------
TRUE
--設置數據表的publish參數取值;
SQL> exec dbms_stats.set_table_prefs(user,'T','PUBLISH','false');
PL/SQL procedure successfully completed
SQL> select dbms_stats.get_prefs('PUBLISH',ownname => 'SYS',tabname => 'T') from dual;
DBMS_STATS.GET_PREFS('PUBLISH'
--------------------------------------
FALSE
此時,數據表中已經包括了七萬餘條數據,重新收集統計量。
SQL> exec dbms_stats.gather_table_stats(user,'T',cascade => true);
PL/SQL procedure successfully completed
SQL> select NUM_ROWS, BLOCKS EMPTY_BLOCKS, AVG_SPACE, CHAIN_CNT, AVG_ROW_LEN from user_tab_statistics where table_name='T';
NUM_ROWS EMPTY_BLOCKS AVG_SPACE CHAIN_CNT AVG_ROW_LEN
---------- ------------ ---------- ---------- -----------
0 0 0 0 0
當我們將數據表T的PUBLISH參數修改爲false之後,我們重新收集統計量,發現原有統計信息並沒有連帶的更新。
新統計量不是沒有收集,而是被記錄在了pending信息中。我們可以通過user_ind_pending_stats和user_tab_pending_stats兩個視圖查看被pending的統計量信息。
SQL> select NUM_ROWS, BLOCKS, AVG_ROW_LEN, SAMPLE_SIZE, LAST_ANALYZED from user_tab_pending_stats where table_name='T';
NUM_ROWS BLOCKS AVG_ROW_LEN SAMPLE_SIZE LAST_ANALYZED
---------- ---------- ----------- ----------- -------------
72202 1028 97 72202 2012/6/20 20:
SQL> select index_name, LEAF_BLOCKS, DISTINCT_KEYS, CLUSTERING_FACTOR,LAST_ANALYZED from user_ind_pending_stats where table_name='T';
INDEX_NAME LEAF_BLOCKS DISTINCT_KEYS CLUSTERING_FACTOR LAST_ANALYZED
------------------------------ ----------- ------------- ----------------- -------------
IDX_T_OWNER 293 23 1884 2012/6/20 20:
IDX_T_ID 256 72202 1665 2012/6/20 20:
4、Pending和SQL執行計劃
新的統計量沒有被publish出來。那麼,在一般情況下,我們的SQL執行計劃還是依據正式被publish的統計量生成。
SQL> explain plan for select * from t where wner='SYS';
Explained
SQL> select * from table(dbms_xplan.display);
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------
Plan hash value: 1516787156
------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
-------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 207 | 1 (0)|
| 1 | TABLE ACCESS BY INDEX ROWID| T | 1 | 207 | 1 (0)|
|* 2 | INDEX RANGE SCAN | IDX_T_OWNER | 1 | | 1 (0)|
--------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("OWNER"='SYS')
14 rows selected
實際執行情況;
SQL> select * from t where wner='SYS';
已選擇58799行。
已用時間: 00: 00: 06.19
執行計劃
----------------------------------------------------------
Plan hash value: 1516787156
-------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 207 | 1 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| T | 1 | 207 | 1 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | IDX_T_OWNER | 1 | | 1 (0)| 00:00:01 |
-------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("OWNER"='SYS')
統計信息
----------------------------------------------------------
528 recursive calls
0 db block gets
8962 consistent gets
1108 physical reads
0 redo size
6291375 bytes sent via SQL*Net to client
43520 bytes received via SQL*Net from client
3921 SQL*Net roundtrips to/from client
4 sorts (memory)
0 sorts (disk)
58799 rows processed
SQL>
在sys用戶下,行數比例超過了數據表T的絕大多數。按照CBO的原則,走全表掃描可能是較好的方法。但是,由於統計量還是在空表的狀態下,所以,Oracle CBO認爲Index路徑會更好。
在Oracle中,存在一個參數optimizer_use_pending_statistics,用來控制當前是否使用pending的統計量來生成執行計劃。作爲運維DBA,可以通過這個參數暫時性的啓用pending統計量,觀察一下性能狀況。再決定是否啓用publish這些統計量。
默認情況下,該參數取值爲false。我們可以在session級別設置下該參數爲true。
SQL> show parameter optimizer_use_pending
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
optimizer_use_pending_statistics boolean FALSE
修改參數爲true之後,Oracle CBO在生成執行計劃的時候就會使用Pending的統計量。
SQL> alter session set optimizer_use_pending_statistics=true;
Session altered
SQL> select value from v$parameter where name='optimizer_use_pending_statistics';
VALUE
------------------------------------------
TRUE
SQL> explain plan for select * from t where wner='SYS';
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 | | 58274 | 5463K| 281 (1)| 00:00:04 |
|* 1 | TABLE ACCESS FULL| T | 58274 | 5463K| 281 (1)| 00:00:04 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("OWNER"='SYS')
13 rows selected
SQL> select * from t where wner='SYS';
已選擇58799行。
已用時間: 00: 00: 04.68
執行計劃
----------------------------------------------------------
Plan hash value: 1601196873
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 58274 | 5463K| 281 (1)| 00:00:04 |
|* 1 | TABLE ACCESS FULL| T | 58274 | 5463K| 281 (1)| 00:00:04 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("OWNER"='SYS')
統計信息
----------------------------------------------------------
7511 recursive calls
50 db block gets
6599 consistent gets
1118 physical reads
0 redo size
2392962 bytes sent via SQL*Net to client
43520 bytes received via SQL*Net from client
3921 SQL*Net roundtrips to/from client
211 sorts (memory)
0 sorts (disk)
58799 rows processed
果然,設置參數後,Oracle生成了FTS路徑,說明更新的統計量起了作用。同時,執行時間減少了近2秒鐘,說明結果上也確實是生成了更好的執行計劃。
5、Pending統計量的後續處理
在對pending統計量進行合理評估之後,DBA是可以做出刪除還是發佈統計量的決定的。具體操作如下:
--刪除pending信息
SQL> exec dbms_stats.delete_pending_stats(user,'T');
PL/SQL procedure successfully completed
SQL> select count(*) from user_tab_pending_stats;
COUNT(*)
----------
0
--重新收集pending統計量
SQL> exec dbms_stats.gather_table_stats(user,'T',cascade => true);
PL/SQL procedure successfully completed
SQL> select NUM_ROWS, BLOCKS EMPTY_BLOCKS, AVG_SPACE, CHAIN_CNT, AVG_ROW_LEN from user_tab_statistics where table_name='T';
NUM_ROWS EMPTY_BLOCKS AVG_SPACE CHAIN_CNT AVG_ROW_LEN
---------- ------------ ---------- ---------- -----------
0 0 0 0 0
--發佈pending統計量
SQL> exec dbms_stats.publish_pending_stats(user,'T');
PL/SQL procedure successfully completed
SQL> select NUM_ROWS, BLOCKS EMPTY_BLOCKS, AVG_SPACE, CHAIN_CNT, AVG_ROW_LEN from user_tab_statistics where table_name='T';
NUM_ROWS EMPTY_BLOCKS AVG_SPACE CHAIN_CNT AVG_ROW_LEN
---------- ------------ ---------- ---------- -----------
72202 1028 0 0 96
單發佈完統計量之後,就可以在正常的情況下使用統計量生成執行計劃了。
SQL> show parameter optimizer_use_pen
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
optimizer_use_pending_statistics boolean FALSE
SQL> alter session set optimizer_use_pending_statistics=false;
會話已更改。
已用時間: 00: 00: 00.01
SQL> select * from t where wner='SYS';
已選擇58799行。
已用時間: 00: 00: 04.33
執行計劃
----------------------------------------------------------
Plan hash value: 1601196873
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 58794 | 5511K| 281 (1)| 00:00:04 |
|* 1 | TABLE ACCESS FULL| T | 58794 | 5511K| 281 (1)| 00:00:04 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("OWNER"='SYS')
統計信息
----------------------------------------------------------
426 recursive calls
0 db block gets
4975 consistent gets
0 physical reads
0 redo size
2392962 bytes sent via SQL*Net to client
43520 bytes received via SQL*Net from client
3921 SQL*Net roundtrips to/from client
4 sorts (memory)
0 sorts (disk)
58799 rows processed
6、結論
在11g中提出的pending statistic的方法,可以在生產運維和穩定優化執行計劃方面,給我們提供幫助。