Oracle

執行計劃的成本變化很大,為什麼?

  • March 21, 2018

由於未知原因,我有一個查詢更改執行計劃。統計數據是在更改前大約 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

引用自:https://dba.stackexchange.com/questions/201826