我有一系列極其相似的查詢,我對14億條記錄(帶有索引)的表執行,唯一的問題是至少有10%的查詢比其他查詢執行時間多100倍以上。如何強制oracle使用索引範圍掃描?
我運行了一個解釋計劃,並注意到對於快速查詢(大約90%),Oracle正在使用索引範圍掃描;在慢速的情況下,它使用完整的索引掃描。
有沒有辦法強制Oracle進行索引範圍掃描?
我有一系列極其相似的查詢,我對14億條記錄(帶有索引)的表執行,唯一的問題是至少有10%的查詢比其他查詢執行時間多100倍以上。如何強制oracle使用索引範圍掃描?
我運行了一個解釋計劃,並注意到對於快速查詢(大約90%),Oracle正在使用索引範圍掃描;在慢速的情況下,它使用完整的索引掃描。
有沒有辦法強制Oracle進行索引範圍掃描?
我建議以下辦法: -
你注意INDEX計劃的成本更高。這就是爲什麼Oracle不選擇索引計劃。成本是Oracle根據其具有的統計數據和各種假設估計的。
如果計劃的估計成本更大,但實際運行速度更快,那麼估算是錯誤的。你的工作是找出爲什麼估計是錯誤的,並糾正這一點。然後,甲骨文將選擇適合本聲明的正確計劃和其他人自己的計劃。
要找出錯誤原因,請查看計劃中預期行數。你可能會發現其中之一是一個數量級。這可能是由於不均勻分佈的列值,舊統計信息,彼此互相牽連的列等。
要解決此問題,您可以讓Oracle收集更好的統計信息並使用更好的開始假設來提示它。然後,它會估計準確的成本,並提出最快的計劃。
如果您發佈更多信息,我可以進一步評論。
我看到提示被Oracle忽略。
最近,我們的DBA使用「optimizer_index_cost_adj」並使用索引。 這是Oracle參數,但您可以將其用作會話級別。
100是默認值,我們使用10作爲參數。
optimizer_index_cost_adj會影響優化程序在考慮系統中的任何索引時所做的所有計算 - 考慮到它在整個系統中的一般影響,必須謹慎地進行更改。 – 2010-03-19 04:04:36
這是我的觀點。當它被使用時,它應該被會話級別參數使用,以便它不會影響任何其他查詢。 – exiter2000 2010-03-19 14:33:53
請記住提示是提示,優化程序可能會決定不使用它。 – DJPeter 2012-12-11 05:22:32
如果你想知道爲什麼優化器需要做出決定,你需要使用10053跟蹤。
SQL> alter session set events '10053 trace name context forever, level 1';
然後運行示例快速查詢和示例慢速查詢的解釋計劃。在用戶轉儲目錄中,您將獲得詳細描述CBO經歷的決策樹的跟蹤文件。在這些文件中的某處,您可以找到選擇索引範圍掃描的完整索引掃描的原因。
我不是說跟蹤文件是一個容易閱讀。瞭解它們的最佳資源是Wolfgang Breitling出色的白皮書"A look under the hood of CBO" (PDF)
您可以使用oracle sql提示。您可以強制使用特定指數或排除指數 查看文檔
http://psoug.org/reference/hints.html http://www.adp-gmbh.ch/ora/sql/hints/index.html
像 選擇/ * +索引(scott.emp ix_emp)*/from scott.emp emp_alias
這將使Oracle使用索引,但不一定會強制索引範圍掃描而不是完整索引掃描。 – 2012-12-13 16:48:48
如果您的索引基於非唯一列值,它肯定只適用於索引範圍掃描。要按提示操作,使用提示INDEX,它應該沒問題。否則全索引掃描要求 – mermerkaya 2012-12-13 17:05:24
可能99.9%的時間它會正確選擇索引範圍掃描。但有時Oracle會選擇索引全掃描。有跡象表明,Oracle不要使用* fast * full索引掃描,但據我所知,沒有辦法告訴Oracle不要使用完整索引掃描。 – 2012-12-13 20:40:19
要「強制」Oracle使用索引範圍掃描,只需使用優化程序提示INDEX_RS_ASC
。例如:
CREATE TABLE mytable (a NUMBER NOT NULL, b NUMBER NOT NULL, c CHAR(10)) NOLOGGING;
INSERT /*+ APPEND */ INTO mytable(a,b,c)
SELECT level, mod(level,100)+1, 'a' FROM dual CONNECT BY level <= 1E6;
CREATE INDEX myindex_ba ON mytable(b, a);
EXECUTE dbms_stats.gather_table_stats(NULL,'mytable');
SELECT /*+ FULL(m) */ b FROM mytable m WHERE b=10; -- full table scan
SELECT /*+ INDEX_RS_ASC(m) */ b FROM mytable m WHERE b=10; -- index range scan
SELECT /*+ INDEX_FFS(m) */ b FROM mytable m WHERE b=10; -- index fast full scan
這是否會使你的查詢實際運行速度取決於許多因素,如索引值的選擇性或表中的行的物理順序。舉例來說,如果你改變了查詢WHERE b BETWEEN 10 AND <xxx>
,下列費用出現在我的機器上執行計劃:
b BETWEEN 10 AND 10 20 40 80
FULL 749 750 751 752
INDEX_RS_ASC 29 325 865 1943
INDEX_FFS 597 598 599 601
如果更改了查詢非常輕微不但選擇索引列b
,還包括其他,非索引列,成本變化很大:
b BETWEEN 10 AND 10 20 40 80
FULL 749 750 751 754
INDEX_RS_ASC 3352 40540 108215 243563
INDEX_FFS 3352 40540 108215 243563
我將INDEX_RS更改爲INDEX_RS_ASC。兩者都工作,但基於V $ SQL_HINT和[本文](http://jonathanlewis.wordpress.com/2007/06/17/hints-again/),INDEX_RS_ASC似乎是官方暗示,儘管兩者都是真的無證。不幸的是,這個提示仍然不能解決我的具體問題,所以它並不總是奏效。但至少有時候,至少你的腳本清楚地表明瞭這一點。 – 2012-12-14 20:17:54
鑑於這是一個範圍掃描,我不知道'慢'查詢是否處理更大的範圍(例如一年而不是一週)。在這種情況下,你會期望它們變得相當慢。在某一點上,隨着範圍變得足夠大,從索引切換到全面掃描將是有意義的。甲骨文是否正確猜測了這一點是另一回事。 – 2010-03-18 22:58:26
此外,如果它正在做一個完整的索引掃描,並且你的查詢連接到另一個表,我猜它不是通過嵌套循環連接 - 它可能會切換到散列或合併連接,或者以不同的順序連接表。如果緩慢查詢「應該」更快(即它們不是IRL查詢表中的相當大的百分比),則應該通過暗示嵌套循環(具有適當的'USE_NL'提示)返回到範圍掃描。 – 2010-03-19 04:08:08