Oracle 11g 数据统计量Pending处理
在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的方法,可以在生產運維和穩定優化執行計劃方面,給我們提供幫助。
?
總結
以上是生活随笔為你收集整理的Oracle 11g 数据统计量Pending处理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 测试工具:Windows下超强日志工具B
- 下一篇: 修改数据文件路径