2012-10-17 12 views
6

Ho un tavolo foo con (tra le altre 20) colonne bar, baz e quux con indici su baz e quux. La tabella ha ~ 500k righe.Perché MAX() 100 volte più lento di ORDER BY ... LIMIT 1?

Perché quanto segue per le query differisce tanto nella velocità? La query A richiede 0,3 secondi, mentre la query B richiede 28 secondi.

Query Un

select baz from foo 
    where bar = :bar 
    and quux = (select quux from foo where bar = :bar order by quux desc limit 1) 

Spiegare

id select_type table type possible_keys key  key_len ref  rows Extra 
1 PRIMARY  foo  ref  quuxIdx   quuxIdx 9  const 2  "Using where" 
2 SUBQUERY foo  index NULL   quuxIdx 9  NULL 1  "Using where" 

Query B

select baz from foo 
    where bar = :bar 
    and quux = (select MAX(quux) from foo where bar = :bar) 

Spiegate

id select_type table type possible_keys key  key_len ref  rows Extra 
1 PRIMARY  foo  ref  quuxIdx   quuxIdx 9  const 2  "Using where" 
2 SUBQUERY foo  ALL  NULL   NULL NULL NULL 448060 "Using where" 

Io uso MySQL 5.1.34.

+0

'LiMIT 1' significa prendere 1 riga e fermarsi, non è vero?la query B è O (n * m) – jondinham

+2

@PaulDinh sembra che entrambe le query producano lo stesso risultato, molto probabilmente è legato all'ordine delle operazioni, nel primo caso ordina per quux e barra di ricerca dal risultato (veloce) nella seconda query barra di ricerca (necessario per controllare l'intera tabella) da unsorted e quindi ordinare per trovare max –

+0

@Viktor, puoi per favore mostrare 'spiegare select baz da foo dove bar =: bar e quux = (seleziona quux da foo dove quux = MAX (quux) e bar =: bar) ' ' spiega select baz da foo dove bar =: bar e quux = (seleziona quux da foo dove quux = MAX (quux) e bar =: bar limite 1) ' –

risposta

6

È necessario aggiungere un indice su (bar, quux).

Senza questo indice, MySQL non può vedere come eseguire la query in modo efficiente, quindi deve scegliere tra vari piani di query inefficienti.

Nel primo esempio esegue la scansione dell'indice quux e per ogni riga trovata, cerca il valore corrispondente di bar nella tabella originale. Questo richiede il doppio del tempo per controllare ogni riga, ma è una fortuna che una riga con il valore corretto di bar si avvicini all'inizio della scansione e quindi possa fermarsi. Ciò potrebbe essere dovuto al fatto che il valore di bar che stai cercando si verifica frequentemente, quindi la possibilità di essere fortunati è molto alta. Di conseguenza, potrebbe essere necessario esaminare solo una manciata di righe prima di trovare una corrispondenza, quindi anche se ci vuole il doppio del tempo per controllare ogni riga, il fatto che siano controllate solo poche righe dà un massiccio risparmio complessivo. Dato che non hai un indice su bar, MySQL non sa in anticipo che il valore :bar si verifica frequentemente in modo che non possa sapere che questa query sarà veloce.

Nel secondo esempio viene utilizzato un piano diverso in cui esegue sempre la scansione dell'intera tabella. Ogni riga viene letta direttamente dalla tabella, senza l'uso di un indice. Ciò significa che ogni riga di lettura è veloce, ma poiché hai un sacco di righe, è complessivamente lenta. Se nessuna delle righe corrisponde a :bar, questo sarebbe il piano di query più veloce. Ma se circa l'1% delle righe ha il valore desiderato di bar, sarà (molto) circa 100 volte più lento utilizzare questo piano di query rispetto al piano precedente. Poiché non hai un indice su bar, MySQL non lo sa in anticipo.

Si potrebbe anche solo aggiungere l'indice mancante e quindi entrambe le query andranno molto più veloce.

+1

Sembra che OP abbia chiesto perché lo stesso risultato abbia una differenza così drammatica –

+0

quindi 'seleziona quux da foo dove quux = MAX (quux) e bar =: bar' è più veloce di 'seleziona MAX (quux) da foo dove bar =: bar' se quux è indicizzato int e bar è testo? –

+0

nvm dà risultati diversi :) –

Problemi correlati