2010-03-18 85 views
12

我有一系列極其相似的查詢,我對14億條記錄(帶有索引)的表執行,唯一的問題是至少有10%的查詢比其他查詢執行時間多100倍以上。如何強制oracle使用索引範圍掃描?

我運行了一個解釋計劃,並注意到對於快速查詢(大約90%),Oracle正在使用索引範圍掃描;在慢速的情況下,它使用完整的索引掃描。

有沒有辦法強制Oracle進行索引範圍掃描?

回答

12

我建議以下辦法: -

  • 獲取慢速聲明
  • 使用索引提示的解釋計劃,得到一個解釋計劃使用索引

你注意INDEX計劃的成本更高。這就是爲什麼Oracle不選擇索引計劃。成本是Oracle根據其具有的統計數據和各種假設估計的。

如果計劃的估計成本更大,但實際運行速度更快,那麼估算是錯誤的。你的工作是找出爲什麼估計是錯誤的,並糾正這一點。然後,甲骨文將選擇適合本聲明的正確計劃和其他人自己的計劃。

要找出錯誤原因,請查看計劃中預期行數。你可能會發現其中之一是一個數量級。這可能是由於不均勻分佈的列值,舊統計信息,彼此互相牽連的列等。

要解決此問題,您可以讓Oracle收集更好的統計信息並使用更好的開始假設來提示它。然後,它會估計準確的成本,並提出最快的計劃。

如果您發佈更多信息,我可以進一步評論。

+3

鑑於這是一個範圍掃描,我不知道'慢'查詢是否處理更大的範圍(例如一年而不是一週)。在這種情況下,你會期望它們變得相當慢。在某一點上,隨着範圍變得足夠大,從索引切換到全面掃描將是有意義的。甲骨文是否正確猜測了這一點是另一回事。 – 2010-03-18 22:58:26

+0

此外,如果它正在做一個完整的索引掃描,並且你的查詢連接到另一個表,我猜它不是通過嵌套循環連接 - 它可能會切換到散列或合併連接,或者以不同的順序連接表。如果緩慢查詢「應該」更快(即它們不是IRL查詢表中的相當大的百分比),則應該通過暗示嵌套循環(具有適當的'USE_NL'提示)返回到範圍掃描。 – 2010-03-19 04:08:08

-2

我看到提示被Oracle忽略。

最近,我們的DBA使用「optimizer_index_cost_adj」並使用索引。 這是Oracle參數,但您可以將其用作會話級別。

100是默認值,我們使用10作爲參數。

+0

optimizer_index_cost_adj會影響優化程序在考慮系統中的任何索引時所做的所有計算 - 考慮到它在整個系統中的一般影響,必須謹慎地進行更改。 – 2010-03-19 04:04:36

+0

這是我的觀點。當它被使用時,它應該被會話級別參數使用,以便它不會影響任何其他查詢。 – exiter2000 2010-03-19 14:33:53

+0

請記住提示是提示,優化程序可能會決定不使用它。 – DJPeter 2012-12-11 05:22:32

2

如果你想知道爲什麼優化器需要做出決定,你需要使用10053跟蹤。

SQL> alter session set events '10053 trace name context forever, level 1'; 

然後運行示例快速查詢和示例慢速查詢的解釋計劃。在用戶轉儲目錄中,您將獲得詳細描述CBO經歷的決策樹的跟蹤文件。在這些文件中的某處,您可以找到選擇索引範圍掃描的完整索引掃描的原因。

我不是說跟蹤文件是一個容易閱讀。瞭解它們的最佳資源是Wolfgang Breitling出色的白皮書"A look under the hood of CBO" (PDF)

-1

您可以使用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

+1

這將使Oracle使用索引,但不一定會強制索引範圍掃描而不是完整索引掃描。 – 2012-12-13 16:48:48

+0

如果您的索引基於非唯一列值,它肯定只適用於索引範圍掃描。要按提示操作,使用提示INDEX,它應該沒問題。否則全索引掃描要求 – mermerkaya 2012-12-13 17:05:24

+0

可能99.9%的時間它會正確選擇索引範圍掃描。但有時Oracle會選擇索引全掃描。有跡象表明,Oracle不要使用* fast * full索引掃描,但據我所知,沒有辦法告訴Oracle不要使用完整索引掃描。 – 2012-12-13 20:40:19

7

要「強制」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 
+0

我將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