索引失效系列——統計量過期引起執行計劃錯誤
索引是我們進行最佳化的一種重要方式。實際工作中,一個簡單的索引,可能就會大大提升提高關鍵業務作業效率,最終提升使用者滿意度。在CBO時代,DBA和開發人員經常為索引為什麼不出現在執行計劃中而困惑。
問題提出
進入CBO時代之後,Oracle最佳化器變得越來越智慧、靈活,生成的SQL執行計劃越來越高效。這一切都是依賴兩個因素:最佳化器引數公式和強大的統計量收集能力。其中,統計量是我們最經常打交道的部分。
所謂統計量,就是針對當前的資料庫物件實際情況,採用一定的抽樣比例,應用一定的統計方法,生成資料的描述資訊。並且最終將其記錄在資料字典中,供下一次生成執行計劃是呼叫使用。
越精確的統計量,也就意味越優質高效的SQL執行計劃。反過來說,過時、錯誤的統計量,也可能會生成錯誤的執行計劃。下面我們來看這個實驗。
環境準備
先準備資料表T和對應索引。
SQL> create table t as select * from dba_objects;
Table created
SQL> create index idx_t_owner on t(owner);
Index created
//收集統計量
SQL> exec dbms_stats.gather_table_stats(user,'T',cascade => true);
PL/SQL procedure successfully completed
正常實驗
此時,我們透過dbms_stats包進行統計量收集。此時,可以說資料表T相關物件的資料字典上統計資訊是正確的。
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 | | 43153 | 3877K| 160 (3)| 00:00:02 |
|* 1 | TABLE ACCESS FULL| T | 43153 | 3877K| 160 (3)| 00:00:02 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("OWNER"='SYS')
13 rows selected
SQL> rollback;
Rollback complete
SQL> alter system flush shared_pool;
System altered
SQL> explain plan for select * from t where wner='SCOTT';
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 | | 54 | 4968 | 2 (0)|
| 1 | TABLE ACCESS BY INDEX ROWID| T | 54 | 4968 | 2 (0)|
|* 2 | INDEX RANGE SCAN | IDX_T_OWNER | 54 | | 1 (0)|
--------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("OWNER"='SCOTT')
14 rows selected
上述實驗證明了兩個有代表性的場景。Owner=’SYS’取值較多、偏移度高,這樣進行全表掃描的成本較低。而owner=’SCOTT’的選擇性好,進行索引路徑成本較低。
統計量失效實驗
我們使用dbms_stats包進行統計量收集。如果我們此時修改更新資料,改變了資料原有的分佈結構和狀態,而不去進行手工的統計量更新。那麼,生成的執行計劃是怎麼樣呢?
//清理shared_pool,避免執行計劃重用;
SQL> alter system flush shared_pool;
System altered
SQL> update t set wner='SCOTT' where wner='SYS';
43037 rows updated
SQL> commit;
Commit complete
此時,owner=’SCOTT’,佔到了超過原有’SYS’的比例。如果正確的執行計劃,應該是進行全表掃描。事實又是如何呢?
SQL> explain plan for select * from t where wner='SCOTT';
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 | | 54 | 4968 | 2 (0)|
| 1 | TABLE ACCESS BY INDEX ROWID| T | 54 | 4968 | 2 (0)|
|* 2 | INDEX RANGE SCAN | IDX_T_OWNER | 54 | | 1 (0)|
--------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("OWNER"='SCOTT')
14 rows selected
在沒有重新收集統計量,使用原有舊的統計量的情況下,Oracle生成了索引路徑執行計劃。當資料發生大面積的更新,修改了原有的資料分佈,統計量是會與實際資料分佈不一致,從而引起執行計劃的生成錯誤。
那麼,修復的方法也很簡單,我們重新收集一下統計量就可以了。
//手工的收集統計量
SQL> exec dbms_stats.gather_table_stats(user,'T',cascade => true);
PL/SQL procedure successfully completed
SQL> alter system flush shared_pool;
System altered
SQL> explain plan for select * from t where wner='SCOTT';
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 | | 43405 | 3984K| 160 (3)| 00:00:02 |
|* 1 | TABLE ACCESS FULL| T | 43405 | 3984K| 160 (3)| 00:00:02 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("OWNER"='SCOTT')
13 rows selected
此時,我們已經獲取到了本篇的結論:當統計量和實際資料情況不一致,CBO生成的執行計劃的確會有問題,生成的路徑的確可能不是最優的。
進一步的思考
我們瞭解了結構,那麼如何避免這種情況發生呢?
回答這個問題,首先我們需要從Oracle統計量收集的方式談起。在RBO時代,最佳化器工作是不需要統計量的。所以雖然比較教條,但也是不需要擔心統計量過時的問題。在CBO時代,我們不得不考慮統計量收集的方式。
在Oracle10g開始,Oracle開始提供自動化統計量收集的作業。當使用預設安裝方式之後,每天晚上10點到凌晨,Oracle都會執行作業,來進行統計量收集。此時收集的範圍定義為發生資料變數達到一定比例的資料表。Oracle希望使用這個機制來保證儘可能有更新的統計量使用。
這種作業機制可以實現自動的統計量收集,但是也會給其他方面帶來困擾。最大的一方面就是影響到系統夜間作業的執行。我們的應用系統中經常會有一些如大運算、報表、資料彙集等大作業。為了避免和白天的業務視窗時間衝突,我們常常將它們排在夜間完成,沒想到會與Oracle統計量自動收集衝突。這也就是為什麼有DBA抱怨這些大作業夜間做的速度甚至沒有白天快的原因。
上述問題的解決其實也比較容易,就是禁止夜間進行統計量收集(具體方法請查閱網路資源)。手工組織統計量作業排程計劃。這樣可以靈活的避免負載衝突的情況。
下面說說發生統計量與實際資料分佈不一致出現的場景,從網路上各同行反映的資訊看,大都有如下情況:
ü 沒有定期收集統計量的方案,使得統計量過舊;
ü 系統升級移植,因為版本不一致使得匯入的對應統計量錯誤;
ü 陰差陽錯,在夜間剛剛收集完統計量之後呼叫大作業,匯入海量資料直接改變了原有的資料分佈情況。第二天直接生成錯誤的執行計劃;
解決的方法相對容易,發現統計量錯誤的物件,手工收集下就可以了。
最後,筆者想談一下統計量收集的頻度問題。一個資料表,是不是每次都需要收集統計量?是不是隻要發生變化就要收集統計量?筆者認為這不是絕對的。
一張資料表,如果資料分佈是有規律的、取值是有範圍的,那麼一份統計量完全可以解決所有的問題,不需要每時每刻進行收集。
CBO是一次時代的飛躍,但是伴隨飛躍必然存在新問題的引入。意識到問題,正視問題和解決問題,是我們正確對待統計量過時問題的根本出發點。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/17203031/viewspace-693944/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- mysql 執行計劃索引分析筆記MySql索引筆記
- 檢視執行計劃出現ORA-22992錯誤
- SQL執行計劃異常引起的效能問題SQL
- SQL執行計劃異常 引起的效能問題SQL
- 檔案格式引起的指令碼執行錯誤指令碼
- 執行計劃-1:獲取執行計劃
- 【PG執行計劃】Postgresql資料庫執行計劃統計資訊簡述SQL資料庫
- 【MySQL】MySQL的執行計劃及索引優化MySql索引優化
- 建了索引執行計劃會有區別了索引
- 理解索引:MySQL執行計劃詳細介紹索引MySql
- Oracle 變數窺視引起執行計劃異常故障分析Oracle變數
- 分析執行計劃優化SQLORACLE的執行計劃(轉)優化SQLOracle
- 透過空間佔用和執行計劃瞭解SQL Server的行儲存索引SQLServer索引
- MySQL執行計劃MySql
- SYBASE執行計劃
- MySQL 執行計劃MySql
- 【執行計劃】Oracle獲取執行計劃的幾種方法Oracle
- Oracle資料遷移後由列的直方圖統計資訊引起的執行計劃異常Oracle直方圖
- 評“MySQL 隱式轉換引起的執行結果錯誤”MySql
- Oracle 通過註釋改變執行計劃Oracle
- MySQL——通過EXPLAIN分析SQL的執行計劃MySqlAI
- oracle執行計劃------未走索引,隱式轉換的坑Oracle索引
- 通過鎖定表的統計資訊來穩定sql的執行計劃SQL
- MySQL:2020 端午節隨筆(索引下探和唯一索引特殊執行計劃)MySql索引
- MySQL執行計劃解析MySql
- mysql explain 執行計劃MySqlAI
- explain執行計劃分析AI
- mysql執行計劃explainMySqlAI
- oracle 固定執行計劃Oracle
- Oracle sql執行計劃OracleSQL
- Oracle當number型別超過一定長度直方圖限制導致SQL執行計劃錯誤Oracle型別直方圖SQL
- MOGDB/openGauss資料庫執行計劃快取/失效機制的測試資料庫快取
- 執行計劃執行步驟原則
- SQLServer統計監控SQL執行計劃突變的方法SQLServer
- Oracle 9i變數窺視引起執行計劃異常故障報告Oracle變數
- mongodb執行計劃解釋MongoDB
- 檢視 OceanBase 執行計劃
- MySQL執行計劃解析(四)MySql
- 讀懂MySQL執行計劃MySql