2010-03-18 29 views
12

Ho una serie di query estremamente simili eseguite su una tabella di 1,4 miliardi di record (con indici), l'unico problema è che almeno il 10% di quelle query richiede più di 100 volte più tempo di altre.Come forzare Oracle per utilizzare la scansione dell'indice?

Ho eseguito un piano di spiegazioni e ho notato che per le query veloci (circa il 90%) Oracle utilizza una scansione dell'indice; su quelli lenti, sta usando una scansione indice completa.

Esiste un modo per forzare Oracle a eseguire una scansione dell'indice?

risposta

12

suggerisco il seguente approccio: -

  • ottenere un piano di spiegare sulla dichiarazione lento
  • Utilizzando un hint di indice, ottenere un piano di spiegare sulla utilizzando l'indice

Avrete notare che il costo del piano INDICE è maggiore. Questo è il motivo per cui Oracle non sta scegliendo il piano dell'indice. Il costo è la stima di Oracle basata sulle statistiche che ha e varie ipotesi.

Se il costo stimato di un piano è maggiore, ma in realtà viene eseguito più rapidamente, la stima è errata. Il tuo compito è capire perché la stima è sbagliata e correggere quello. Quindi Oracle sceglierà il piano giusto per questa affermazione e altri per conto proprio.

Per capire perché è sbagliato, controllare il numero di righe previste nel piano. Probabilmente troverai uno di questi è un ordine di grandezza. Ciò potrebbe essere dovuto a valori di colonna distribuiti in modo non uniforme, vecchie statistiche, colonne che si correlano tra loro ecc.

Per risolvere questo problema, è possibile ottenere Oracle per raccogliere statistiche migliori e suggerirlo con ipotesi di partenza migliori. Quindi valuterà i costi precisi e presenterà il piano più veloce.

Se pubblichi più informazioni potrei essere in grado di commentare ulteriormente.

+3

Dato che si tratta di un intervallo di scansione, mi chiedevo se le query "lente" si occupano di un intervallo più ampio (ad esempio un anno anziché una settimana). Nel qual caso ti aspetteresti che siano notevolmente più lenti. Ad un certo punto, quando l'intervallo diventa abbastanza grande, avrà senso passare dall'indice alla scansione completa. Che Oracle abbia indovinato correttamente quel punto è una questione diversa. –

+0

Inoltre, se si sta eseguendo una scansione dell'indice completa e la tua query si sta aggiungendo a un'altra tabella, suppongo che non si unisca tramite un ciclo annidato: probabilmente sta passando a un hash o a unire join o unendo le tabelle in un ordine diverso. Se le query lente "dovrebbero" essere più rapide (ovvero non eseguono query IRL su una percentuale significativa della tabella), dovresti essere in grado di tornare alle scansioni di intervalli facendo riferimento a cicli annidati (con un suggerimento 'USE_NL' appropriato). –

-2

Ho visto il suggerimento ignorato da Oracle.

Recentemente, il nostro DBA utilizza "optimizer_index_cost_adj" e ha utilizzato l'indice. Questo è il parametro Oracle ma è possibile utilizzarlo come livello di sessione.

100 è il valore predefinito e abbiamo utilizzato 10 come parametro.

+0

optimizer_index_cost_adj ha effetto su TUTTI i calcoli effettuati dall'ottimizzatore quando si considerano ANY tutti gli indici del sistema: la modifica deve essere eseguita con attenzione, tenendo conto del suo effetto generale su un sistema. –

+0

Questo è il mio punto. Quando viene utilizzato, dovrebbe essere utilizzato dal parametro del livello di sessione in modo che non influenzi alcuna altra query. – exiter2000

+0

Si ricorda che i suggerimenti sono suggerimenti e che l'ottimizzatore potrebbe decidere di non utilizzarlo. – DJPeter

2

Se si desidera sapere perché l'ottimizzatore prende le decisioni, è necessario utilizzare la traccia 10053.

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

Quindi eseguire spiegare piani per una query veloce di esempio e una query lenta di esempio. Nella directory di dump dell'utente verranno visualizzati i file di traccia che descrivono in dettaglio gli alberi decisionali attraversati dalla CBO. Da qualche parte in quei file troverai i motivi per cui sceglie una scansione dell'indice completa su una scansione dell'indice.

Non sto dicendo che i file di traccia siano di facile lettura. La migliore risorsa per comprenderli è l'eccellente white paper di Wolfgang Breitling "A look under the hood of CBO" (PDF)

-1

è possibile utilizzare oracle sql hint.si può forzare di utilizzare indice specifico o escludere indice controllare la documentazione

http://psoug.org/reference/hints.html http://www.adp-gmbh.ch/ora/sql/hints/index.html

come select/* + indice (scott.emp ix_emp) */da scott.emp emp_alias

+1

Ciò renderà Oracle utilizzare un indice, ma non forzerà necessariamente una scansione dell'indice anziché una scansione dell'indice completa. –

+0

Se il proprio indice è basato su valori di colonna non univoci, sarà sicuramente disponibile solo per la scansione dell'indice. Per fare questo, usa il suggerimento INDICE e dovrebbe andare bene. in caso contrario, la scansione dell'indice completa richiede – mermerkaya

+0

Probabilmente il 99,9% delle volte sceglierà correttamente la scansione dell'indice. Ma a volte Oracle sceglie invece la scansione completa dell'indice. C'è un suggerimento per dire a Oracle di non usare una scansione completa * veloce *, ma per quanto ne so non c'è modo di dire a Oracle di non usare una scansione indice completa. –

7

Per "forzare" Oracle a utilizzare una scansione dell'indice, è sufficiente utilizzare un suggerimento di ottimizzazione INDEX_RS_ASC. Per esempio:

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 

Se questo renderà la vostra richiesta in realtà correre più veloce dipende da molti fattori come la selettività del valore indicizzato o l'ordine fisico delle righe della tabella. Per esempio, se si modifica la query per WHERE b BETWEEN 10 AND <xxx>, i seguenti costi appaiono nei piani di esecuzione sulla mia macchina:

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 

Se si modifica la query leggermente non solo per selezionare la colonna indicizzata b, ma anche altri, colonne non indicizzate, i costi variano drasticamente:

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

Ho cambiato INDEX_RS in INDEX_RS_ASC. Entrambi funzionano, ma basati su V $ SQL_HINT e [questo articolo] (http://jonathanlewis.wordpress.com/2007/06/17/hints-again/), INDEX_RS_ASC sembra essere il suggerimento ufficiale, anche se entrambi sono realmente non documentato. Sfortunatamente questo suggerimento non risolve il mio problema specifico, quindi non sempre funziona. Ma funziona almeno a volte, come dimostra chiaramente la tua sceneggiatura. –

Problemi correlati