MySql query benchmark
webmaster: 13/02/09 @ 13:18Come ho già scritto, SELECT DISTINCT e GROUP BY sono istruzioni che rallentano l'esecuzione di una query in modo notevole. Vorrei agginugere a questa lista l'istruzione ORDER BY, che ho scoperto essere un'altra grossa causa di query lente.
Dove possible cerco di delegare i compiti di queste istruzioni al PHP, ma alcune volte questo non è proprio possibile. In questi casi il trucco è dividere la query in modo che sia più semplice possibile e non includa JOIN di alcun tipo.
Prendiamo ad esempio questa query che fa un JOIN su varie tabelle, deve leggersi un sacco di row e poi le deve ordinare. Su una tabella con tante row, questa query potrebbe diventare lentissima lentissima.
Visto che l'ORDER BY era molto importante la soluzione è stata di dividere la query in due parti. Nella prima si esegue solo una ricerca per gli id delle news e li si ordina (si potrebbe far fare questo ordinamento al PHP, ma a questo punto penso sia inutile). Poi si mette la seconda query nel loop dei risultati della prima query eseguendo quindi una query che, benché ricca di JOIN è molto veloce grazie all'assenza di elementi come GROUP BY e ORDER BY.
SELECT * FROM news_table, users_table, categories_table WHERE id = $row['id'] AND...
In realtà ho trovato un modo per ulteriormente velocizzare questa coppia di query usando l'istruzione IN. In pratica gli id sono infilati in un loop e costruiscono una stringa come $id1, $id2, $id3... che chiamo $ids e che poi viene usata nella seguente query. Il tempo d'esecuzione di questa query è meno del 10% della somma delle 20 query che servivano prima.
Un'altra piccola scoperta che ho fatto è l'uso dell'istruzione MAX. Per vari motivi ho bisogno di sapere qual'è l'id dell'ultima news. Per molto tempo ho utilizzato la seguente query - che fa uso del famigerato ORDER BY.
Da quando ho capito quanto fosse importante evitare di usare l'ORDER BY è diventata una missione trovare alternative dove possibile. In questo caso MAX ottiene gli stessi risultati, ma in modo molto più efficiente.
Devo ammettere che arrivare a queste conclusioni sarebbe stato molto difficile senza uno strumento di analisi. Il mio piccolo segreto è stato quello di sviluppare ulteriormente lo script di cui parlavo nel precedente post trasformandolo in uno script per paragonare query, o grupppi di query, tra di loro. L'idea era di avere uno strumento che facilitasse il lavoro di dare una risposta a domande come "Se divido questa query in due, è meglio?", oppure "È meglio una query più complessa o tante query semplici?", oppure "Meglio questa query o quella?"
Lo script esegue i due gruppi di query n volte (almeno un centinaio per avere dei numeri statisticamente rilevanti) in modo da evitare errori dovuti alla misurazione del tempo o a tempi di reazione diversi del DB. Se qualcuno fosse interessato può trovare qui sotto lo script. Se qualcuno lo migliorasse mi faccia sapere, lo aggiornerò anch'io.
Test MySql
Commenti: 18
Post precedente
Search Engine Optimisation - Internal Linking
Post successivo
URL canonico per informare Google dei duplicati
I commenti dei lettori:
Non conosco MySql, ma mi pare bizzarro essere costretti a trovare alternative a
operazioni che per un db sono (o dovrebbero essere) il pane quotidiano.
Capisco (e approvo) MAX al posto di ORDER BY + LIMIT, ma smettere di usare
clausole tipo GROUP BY o DISTINCT (quando sono usate a proposito) non dovrebbe
essere una pratica corretta.
Hai considerato la possibilità di aggiungere degli indici per vedere se le
prestazioni migliorano?
Alessandro
grazie archimede, ma senza indici penso che questo db sarebbe inchidato da anni. ormai indicizzo tutto quello che è possibile indicizzare :)
Mah, confesso che la cosa mi sorprende un po': ritenevo che ormai anche MySQL
avesse un motore sufficientemente ottimizzato.
Inoltre non sono del tutto sicuro che i tuoi test siano conclusivi; quando dici
"Lo script esegue i due gruppi di query n volte" cosa
intendi? Esecuzioni consecutive o concorrenti? In altre parole, la query più
veloce in termini assoluti non necessariamente è quella maggiormente
scalabile.
Alessandro
Inoltre non sono del tutto sicuro che i tuoi test siano
conclusivi; quando dici "Lo script esegue i due gruppi di query n volte" cosa
intendi? Esecuzioni consecutive o concorrenti? In altre parole, la query più
veloce in termini assoluti non necessariamente è quella maggiormente
scalabile. intendo che il primo gruppo di query viene eseguito n volte e poi il secondo gruppo di query viene eseguito
n volte. il gruppo di query con il tempo di esecuzione
medio inferiore è il gruppo migliore.
ritengo che il mio codice possa essere migliorato molto, quindi se hai idee sei
il benvenuto
Non sono un esperto di benchmarking nè, come dicevo, di MySql: temo quindi di
non poterti aiutare concretamente.
Quel che posso dire è che non credo sia sufficiente confrontare i tempi medi di
esecuzione di due query per un singolo utente: molto
più significativo sarebbe creare una pagina per ciascuna query e simulare
l'accesso contemporaneo a queste da parte di n
utenti.
So che esistono tools che eseguono questi tipi di test ma, non avendone mai
utilizzato alcuno, non saprei quale suggerire.
Alessandro
alessandro, non penso di seguirti. in questo caso l'utnete non ha alcuna
influenza sulla query. la query è eseguita dal server ed è indipendente dal
browser, dalla banda o dal sistema operativo dell'utente. concordo con te che il
tempo medio non tiene conto di alcune variabili, ma coinvolgere l'utente non
aiuterebbe.
se c'è un problema con il mio script è il fatto che mysql tende a essere più
veloce quando le query vengono ripetute a breve distanza di tempo, ma questo non
so come includerlo nel mio script.
webmaster, lavorare sul server cambia poco il discorso: vuoi ottimizzare le
query per un unico utente/client/script/quelchetipare che interroga il db 1000
volte di seguito oppure per 1000 utenti/clients/scripts/quelchetipare che
interrogano il db una volta sola ma tutti nello stesso
momento?
A me la differenza fra i due scenari pare evidente e prescinde dalle tecniche
usate per accedere al db.
Alessandro
A me la differenza fra i due scenari pare evidente e
prescinde dalle tecniche usate per accedere al db.
archimede, in questo caso mi aiuteresti molto se me la spiegassi, perché non
sono sicuro di vedere la differenza, almeno per quanto riguarda la domanda:
meglio la query x o la query y?
Se parliamo di MySQL 5penso che questo possa esserti utile:
http://dev.mysql.com/doc/mysqltest/en/index.html
mi aiuteresti molto se me la spiegassiIn un
determinato momento al database può arrivare la richiesta di eseguire una certa
query da parte di 1 o di n utenti.
Se tu fai girare la query una volta supponiamo alle ore 09:00 e poi un'altra
volta alle ore 09:01 stai misurando le prestazioni del sistema con un singolo utente. Se invece apri due sessioni (due
finestre, due script, ...) e contemporaneamente alle
ore 09:00 lanci la stessa query stai misurando le prestazioni del sistema con
accessi concorrenti (in altri termini, stai verificando la
[url=../../glossario/scalabilita/]scalabilità[/url] di quella query).
Meglio di così temo di non riuscire a spiegarmi.
Alessandro
archimede, il ragionamento che stai facendo non ti porterà a capire se la query
x è meglio della query y
oppure no.
per essere più chiari è come se cercassimo di misurare la velocità massima di
una macchina nel traffico di milano. certo ci darebbe un'idea della velocità
massima a cui potremmo usare la macchina, ma non risponderebbe alla domanda: è
più veloce la macchina x o la macchina y?
quello che serve a noi è conoscere la velocità assoluta delle query. che poi ci
siano 1 o 1000 utenti collegati contemporaneamente questo non influirà sul
risultato finale in quanto possiamo correttamente presumere che entrambe
sarebbero penalizzate allo stesso modo dai 1000 utenti.
Se parliamo di MySQL 5penso che questo possa esserti
utile:
http://dev.mysql.com/doc/mysqltest/en/index.html
molto interessante, non lo conoscevo. purtroppo non ho accesso alle cartelle di
test fornite da mysql, ma vedo se riesco a ottenere qualcosa lo stesso.
che poi ci siano 1 o 1000 utenti collegati
contemporaneamente questo non influirà sul risultato finale
Ah sì? Allora supponiamo di avere due query che tirano fuori lo stesso
risultato: la prima impega mediamente 5 secondi e l'altra 3. Secondo il tuo
ragionamento preferiremo automaticamente la seconda, ovviamente.
Supponiamo però che per far girare la seconda query il sistema necessiti del
triplo di RAM e/o CPU rispetto alla prima: ogni utente
che farà girare la query in un determinato momento avrà bisogno di quelle
risorse.
Personalmente preferisco una query che impiega 2 secondi in più ma non mi mette
in ginocchio il server al quinto accesso concorrente, poi è pur sempre vero che
non sono un esperto di queste cose.
Alessandro
Caro webmaster dai retta a archimede che è uno che ha dimostrato di sapere
molto, ma veramente molto in materia di DB ;)
E io pure, per quel pochissimo che ne so, devo dargli ragione.
Il test sulle query va fatto simulando più accessi concorrenti...
Una query non deve essere solo "veloce" ma anche utilizzare una porzione
accettabile di risorse.
Con un paragone un po' forzato capirai bene anche tu che una cosa è aprire una
file 100 volte in un'ora, un'altra è aprirne 100 identici nello stesso identico
secondo...
EDIT: miglioriamo un attimo il paragone di prima, altrimenti credo che lo
fraintenderesti...
Mettiamo che il file (cioè la risposta che ti serve dal DB) possa essere aperto
da due programmi diversi (che sarebbero le query).
Il primo lo apre in 4 secondi, utilizzando il 10% del processore e una modesta
quantità di RAM
Il secondo apre lo stesso file in 1 secondo, utilizzando l'80% del processore e
molta più RAM
Con i tuoi test quale riterresti il vincitore?
E se invece dovessi aprire contemporaneamente più file su quale faresti cadere
la scelta?
Come vedi la rsposta cambia di netto...
alessandro, finalmente ci siamo capiti.
che la quantità di ram sia importante sono d'accordo con te e il mio script non
ne tiene conto al momento, ma provvederò ad aggiungere l'informazione.
continuno però a sostenere che simulare più utenti collegati contemporaneamente
non aiuterebbe, basta sapere quanta ram una query consuma.
Come dicevano su, non devi controllare solo quanta RAM consuma, ma anche quante
risorse del processore.
Fino a quando gli accessi contemporanei sono relativamente pochi, il tuo metodo
di benchmarking potrebbe anche essere il più efficace. Ma se, come speriamo
tutti, gli accessi aumenteranno esponenzialmente, probabilmente dovresti
riguardare il benchmark.
Comunque per me è vergognoso che un DB sia battuto nel suo campo da uno script
php.
Mich
Comunque per me è vergognoso che un DB sia battuto nel
suo campo da uno script php.
concordo con te, ma purtroppo è così!