執行計劃的成本變化很大,為什麼?
由於未知原因,我有一個查詢更改執行計劃。統計數據是在更改前大約 24 小時收集的,並且數據增長率在相關期間沒有改變。這是原始的 GOOD 執行計劃: GOOD return about 428 row per run
SELECT STATEMENT Optimizer=ALL_ROWS (Cost=9) SORT (ORDER BY) (Cost=9 Cardinality=1 Bytes=82 Time=1) TABLE ACCESS (BY INDEX ROWID) OF DE.MY_TABLE (TABLE) (Cost=8 Cardinality=1 Bytes=82 Time=1) INDEX (RANGE SCAN) OF DE.MY_TABLE_MY_COL_01_DATE (INDEX) (Cost=3 Cardinality=6 Time=1)
這是 UGLY 計劃,7300 萬行上的 FTS 使 CPU 接近 100% 旋轉仍返回約 400 行
SELECT STATEMENT Optimizer=ALL_ROWS (Cost=4911) SORT (ORDER BY) (Cost=4911 Cardinality=32894 Bytes=2697308 Time=59) TABLE ACCESS (FULL) OF DE.MY_TABLE (TABLE) (Cost=4278 Cardinality=32894 Bytes=2697308 Time=52)
在重建索引、創建覆蓋索引並且不起作用之後,我們最終添加了使用原始索引的提示,並且效果很好。這恢復了以前的時間。執行良好,查詢在 22 毫秒內執行。醜陋的計劃執行了大約 14 秒。
在查詢提示之後,這是查詢計劃:
SELECT STATEMENT Optimizer=ALL_ROWS (Cost=56571 Cardinality=13697 Bytes=1164245) SORT (ORDER BY) (Cost=56571 Cardinality=13697 Bytes=1164245) TABLE ACCESS (BY INDEX ROWID) OF MY_TABLE (TABLE) (Cost=56297 Cardinality=13697 Bytes=1164245) INDEX (RANGE SCAN) OF MY_TABLE_MY_COL_01_DATE (INDEX) (Cost=521 Cardinality=62447)
注意第一個和最後一個計劃的成本和基數都使用相同的索引。在第一個基數是錯誤的(1)。該查詢返回相當一致的 400 行。在上一個計劃中,基數是 13697。這也是錯誤的。但是看看上一個方案56571的Cost,難怪它帶了一個提示來強制使用工作索引。
我的研究指向一個統計問題。我驗證了 GATHER_STATS_JOB 正在執行。我已經使用 dba_tab_stats_history 表來驗證統計資訊實際上是在我感興趣的表上更新的。
所以我的問題是:為什麼查詢的基數和成本會發生如此巨大的變化?如果是收集統計資訊的方式(我正在使用gather_database_stats(auto)),我應該從哪裡開始閱讀要使用的統計資訊收集選項?
如果不太可能統計我應該去哪裡看?
我正在執行 Oracle 數據庫 10g 版本 10.2.0.4.0 - 64 位生產
編輯新計劃 03/21/2018 最新狀態是系統不再認為提示索引昂貴。這是最新的計劃細節:
----------------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ----------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 41 | 3444 | 998 (1)| 00:00:12 | | 1 | SORT ORDER BY | | 41 | 3444 | 998 (1)| 00:00:12 | |* 2 | TABLE ACCESS BY INDEX ROWID| MY_TABLE | 41 | 3444 | 997 (1)| 00:00:12 | |* 3 | INDEX RANGE SCAN | MY_TABLE_MY_COL_01_DATE| 1128 | | 12 (0)| 00:00:01 | ----------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - filter("FO"."STATUSID"<6800 AND "FO"."PARENTID" IS NULL AND "FO"."DELETED"<>1) 3 - access("FO"."MY_COL_01"=:AGENCY AND "FO"."EVENTDATE">SYSDATE@!-:TIMENUMBER AND "FO"."EVENTDATE" IS NOT NULL)
據我記得,10g 存在綁定窺視問題,並且症狀與您的症狀非常相似。我懷疑
fo.eventdate>sysdate - :2
當變數的值很小或者根據統計數據滿足該條件的行很少時,好的計劃會選擇索引(MY_TABLE_MY_COL_01_DATE)。然後由於統計數據的變化,或者你使用不同的變數值執行它,或者 sql 從 SGA 中被逐出,或者它生成了一個新的計劃。您可能會嘗試檢查V$SQL_SHARED_CURSOR
,它顯示了生成新游標的原因。理論上,優化器應該在 FTS 和索引掃描之間切換,但在我的記憶中,它從未在 10g 中發生過——一旦它選擇了 FTS,它就會繼續使用它。如果升級 Oracle 不是一個選項,我會嘗試在這種特殊情況下使用文字而不是綁定變數,即fo.eventdate>sysdate - 1
. (或者可能結合文字和綁定變數來幫助優化器,比如如果它永遠不會超過 10 天,你可以做fo.eventdate>sysdate - 10 and fo.eventdate>sysdate - :2