Domanda

Abbiamo due tabelle in questo modo:

Event
    id
    type
    ... a bunch of other columns

ProcessedEvent
    event_id
    process

Ci sono indici definiti per

  • Event (id) (PK)
  • ProcessedEvent (event_id, di processo)

Il primo rappresenta gli eventi in un'applicazione.

Il secondo rappresenta il fatto che un certo evento schiera processi da un determinato processo. Ci sono molti processi che hanno bisogno di elaborare un determinato evento, quindi non ci sono più voci nella seconda tabella per ogni voce nel primo.

Al fine di trovare tutti gli eventi che hanno bisogno di elaborazione eseguiamo la seguente query:

select * // of course we do name the columns in the production code
from Event
where type in ( 'typeA', 'typeB', 'typeC')
and id not in (
    select event_id
    from ProcessedEvent
    where process = :1  
)

Le statistiche sono aggiornate

Dal momento che la maggior parte degli eventi vengono elaborati, penso che il miglior piano di esecuzione dovrebbe essere simile a questo

  • indice di scansione completa sull'Indice ProcessedEvent
  • indice di scansione completa sull'Indice evento
  • contro uniscono tra i due
  • accesso tavola con il resto
  • Filtro

Invece Oracle fa il seguente

  • indice di scansione completa sull'Indice ProcessedEvent
  • scansione completa della tabella sul tavolo evento
  • filtrare la tabella eventi
  • contro uniscono tra i due set

Con un hint di indice ottengo Oracle per effettuare le seguenti operazioni:

  • indice di scansione completa sull'Indice ProcessedEvent
  • indice di scansione completa sull'Indice evento
  • accesso tavolo sul tavolo evento
  • filtrare la tabella eventi
  • contro uniscono tra i due set

che è veramente stupido IMHO.

Quindi la mia domanda è: quale potrebbe essere il motivo per Oracle per insistere sulla presto accesso alle tabelle


Aggiunta: La performance è male. Stiamo correggendo il problema di prestazioni selezionando solo le Event.IDs e poi recupero le righe necessarie '' manualmente'. Ma, naturalmente, che è solo un lavoro in giro.

È stato utile?

Soluzione

Il tuo indice FULL SCAN sarà probabilmente più veloce di una scansione completa della tabella in quanto l'indice è probabile "più sottile" rispetto alla tabella. Ancora, la piena INDEX SCAN è una lettura completa del segmento e sarà circa lo stesso costo come scansione completa della tabella.

Tuttavia, si sta anche aggiungendo un ACCESSO TABELLA ROWID passo. Si tratta di un passo costoso: una logica IO per riga per l'accesso ROWID, mentre si ottiene uno IO logica per ogni Multi blocchi (a seconda del vostro db_file_multiblock_read_count parameter) per la scansione completa della tabella.

In conclusione, l'ottimizzatore calcola che:

cost(FULL TABLE SCAN) < cost(FULL INDEX SCAN) + cost(TABLE ACCESS BY ROWID)

Aggiorna : La TABELLA FULL SCAN consente anche il filtro del tipo prima di quanto il percorso completo INDICE DI SCANSIONE (poiché l'indice non conosce il tipo di evento), riducendo pertanto la dimensione il set che sarà anti-incollati (ancora un altro vantaggio della scansione completa della tabella).

Altri suggerimenti

L'ottimizzatore fa molte cose che non hanno senso in un primo momento, ma ha è ragioni. Essi non possono sempre essere destro , ma sono comprensibili.

La tabella eventi può essere più facile da full-scan anziché per l'accesso identificativo causa delle sue dimensioni. Può essere che ci sono significativamente meno operazioni di IO coinvolti per leggere l'intera sequenza tabella che leggere pezzi.

è la performance male, o stai solo chiedendo il motivo per cui l'ottimizzatore ha fatto?

Non riesco a spiegare il comportamento del ottimizzatore, ma la mia esperienza è stata quella di evitare di "NOT IN" a tutti i costi, sostituendolo invece con MINUS, in questo modo:

select * from Event
where id in (
  select id from Event where type in ( 'typeA', 'typeB', 'typeC')
 minus
  select id from ProcessedEvent
)

Ho visto ordini di grandezza in termini di prestazioni di query con le trasformazioni simili.

Qualcosa di simile:

WITH
  PROCEEDED AS
  (
    SELECT
      event_id
    FROM
      ProcessedEvent
    WHERE
      PROCESS = :1
  )
SELECT
  * // of course we do name the columns in the production code
FROM
  EVENT
LEFT JOIN PROCEEDED P
ON
  p.event_id = EVENT.event_id
WHERE
  type           IN ( 'typeA', 'typeB', 'typeC')
  AND p.event_id IS NULL; -- exclude already proceeded

potrebbe funzionare abbastanza veloce (almeno molto più veloce rispetto NOT IN).

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top