2010-08-24 5 views
6

Una tabella con circa 70.000 di record viene visualizzata su un sito, con 50 record per pagina. L'impaginazione viene eseguita con limit offset,50 nella query e i record possono essere ordinati su colonne diverse.La selezione è molto più lenta quando si selezionano gli ultimi record

Navigando ultime pagine (quindi l'offset è di circa 60.000) rende le query molto più lento rispetto a quando sfogliando le prime pagine (circa 10 volte)

questo è un problema di usare il comando limit? Ci sono altri modi per ottenere gli stessi risultati?

+0

Buona domanda, io sono non ha familiarità con il funzionamento di MySQL in questo senso. – RedFilter

+0

Grazie per le risposte, lo testeremo più tardi nella speranza di guadagnare un po 'di velocità. – Omiod

risposta

7

Con offset grandi, MySQL deve cercare più record.

Anche se il piano utilizza filesort (il che significa che tutti i record devono essere passati in rassegna), MySQL ottimizza in modo che solo $offset + $limit record superiori sono ordinati, che lo rende molto più efficiente per i valori più bassi di $offset.

La soluzione tipica è per indicizzare le colonne che stai ordinando sul, registrare l'ultimo valore delle colonne e riutilizzarla nelle query successive, in questo modo:

SELECT * 
FROM mytable 
ORDER BY 
     value, id 
LIMIT 0, 10 

cui uscite:

value id 

1  234 
3  57 
4  186 
5  457 
6  367 
8  681 
10  366 
13  26 
15  765 
17  345 -- this is the last one 

per arrivare alla pagina successiva, si può usare:

SELECT * 
FROM mytable 
WHERE (value, id) > (17, 345) 
ORDER BY 
     value, id 
LIMIT 0, 10 

, che utilizza l'in dex su (value, id).

Naturalmente questo non è di aiuto con le pagine di accesso arbitrario, ma aiuta con la navigazione sequenziale.

Inoltre, MySQL ha alcuni problemi con la ricerca della riga in ritardo. Se le colonne sono indicizzate, può essere la pena di provare a riscrivere la query come questa:

SELECT * 
FROM (
     SELECT id 
     FROM mytable 
     ORDER BY 
       value, id 
     LIMIT $offset, $limit 
     ) q 
JOIN mytable m 
ON  m.id = q.id 

consulta questo articolo per spiegazioni più dettagliate:

+0

MySQL supporta quella sintassi in stile tupla ('(val1, val2)> (1, 2)')? Non l'ho mai visto prima. Qual è il combinatore (E, sto assumendo)? Immagino che impari qualcosa di nuovo ogni giorno ... – ircmaxell

+0

@ircmaxell: sì, sì. È un ordine lessicografico, lo stesso di 'val1> 1 OR (val1 = 1 AND val2> 2)' – Quassnoi

+0

Buona risposta. Ecco una presentazione che ritengo abbia qualche buona informazione che è sulla stessa linea: http://www.slideshare.net/Eweaver/efficient-pagination-using-mysql – nathan

2

È come MySQL gestisce i limiti. Se può ordinare su un indice (e la query è abbastanza semplice) può interrompere la ricerca dopo aver trovato le prime righe offset + limit. Quindi, LIMIT 0,10 significa che se la query è abbastanza semplice, potrebbe essere necessario solo eseguire la scansione di 10 righe. Ma LIMIT 1000,10 significa che al minimo ha bisogno di scansionare 1010 righe. Ovviamente, il numero effettivo di righe che devono essere scansionate dipende da una serie di altri fattori. Ma il punto qui è che più basso è il limite inferiore sul numero di righe che devono essere scansionate ...

Per quanto riguarda i rimedi alternativi, vorrei ottimizzare le query in modo che la query stessa senza la clausola LIMIT è il più efficiente possibile. EXPLAIN è un amico in questo caso ...