2012-01-04 6 views
7

Sto passando attraverso il log delle query lente per cercare di determinare il motivo per cui alcune query si comportano in modo errato. Per motivi di coerenza, le query non sono state memorizzate nella cache e lo svuotamento è stato effettuato per cancellare la cache di sistema prima di eseguire il test. La query più o meno così:Perché aumentare il tempo di query nonostante un numero simile di righe esaminate?

SELECT P.id, P.name, P.lat, P.lng, P.price * E.rate AS 'ask' FROM Property P 
INNER JOIN Exchange E ON E.currency = P.currency 
WHERE P.floor_area >= k? 
    AND P.closing_date >= CURDATE() // this and key_buffer_size=0 prevents caching 
    AND P.type ='c' 
    AND P.lat BETWEEN v? AND v? 
    AND P.lng BETWEEN v? AND v? 
    AND P.price * E.rate BETWEEN k? AND k? 
ORDER BY P.floor_area DESC LIMIT 100; 

L'k? sono definiti dall'utente costanti valori; v? sono variabili che cambiano mentre l'utente trascina o ingrandisce una mappa. 100 risultati vengono estratti dal tavolo e ordinati in base all'area del piano in ordine decrescente.

Un tasto PRIMARIO su id e un INDICE su floor_area è impostato solo. Nessun altro indice viene creato in modo che MySQL utilizzi costantemente floor_area come unica chiave. I tempi di query e righe esaminati sono rilevati come segue:

query number    1 2 3 4 5 6 7 8 9 10 
user action on map  start > + + < ^ + > v + 
time in seconds   138 0.21 0.43 32.3 0.12 0.12 36.3 4.33 0.33 2.00 
rows examined ('000)  43 43 43 60 43 43 111 139 133 176 

La query piano di esecuzione è il seguente:

+----+-------------+-------+--------+---------------+---------+---------+--------------------+---------+-------------+ 
| id | select_type | table | type | possible_keys | key  | key_len | ref    | rows | Extra  | 
+----+-------------+-------+--------+---------------+---------+---------+--------------------+---------+-------------+ 
| 1 | SIMPLE  | P  | range | id_flA  | id_flA | 3  | NULL    | 4223660 | Using where | 
| 1 | SIMPLE  | E  | eq_ref | PRIMARY  | PRIMARY | 3  | BuySell.P.currency |  1 | Using where | 
+----+-------------+-------+--------+---------------+---------+---------+--------------------+---------+-------------+ 

La prova viene eseguita diverse volte ed i risultati sono abbastanza coerenti con quanto sopra . Quale potrebbe essere il motivo/i per il picco nei tempi di interrogazione nella query numero 4 e numero 7 e come posso farlo?

UPDATE:

Risultati di rimozione ORDER BY come suggerito da Digital Precision:

query number    1 2 3 4 5 6 7 8 9 10 
user action on map  start > + + < ^ + > v + 
time in seconds   255 3.10 3.16 3.08 3.18 3.21 3.32 3.18 3.17 3.80 
rows examined ('000)  131 131 131 131 136 136 136 136 136 157 

La query piano di esecuzione è la stessa di cui sopra anche se sembra più simile a una scansione di tabella. Si noti che sto usando il motore MyISAM, versione 5.5.14.

come richiesto, sotto è lo schema:

| Property | CREATE TABLE `Property` (
    `id` int(10) unsigned NOT NULL AUTO_INCREMENT, 
    `type` char(1) NOT NULL DEFAULT '', 
    `lat` decimal(6,4) NOT NULL DEFAULT '0.0000', 
    `lng` decimal(7,4) NOT NULL DEFAULT '0.0000', 
    `floor_area` mediumint(8) unsigned NOT NULL DEFAULT '0', 
    `currency` char(3) NOT NULL DEFAULT '', 
    `price` int(10) unsigned NOT NULL DEFAULT '0', 
    `closing_date` date NOT NULL DEFAULT '0000-00-00', 
    `name` char(25) NOT NULL DEFAULT '', 
    PRIMARY KEY (`id`), 
    KEY `id_flA` (`floor_area`) 
) ENGINE=MyISAM AUTO_INCREMENT=5000000 DEFAULT CHARSET=latin1 

| Exchange | CREATE TABLE `Exchange` (
    `currency` char(3) NOT NULL, 
    `rate` decimal(11,10) NOT NULL DEFAULT '0.0000000000', 
    PRIMARY KEY (`currency`) 
) ENGINE=MyISAM DEFAULT CHARSET=latin1 

2 ° UPDATE:

ho pensato che sarebbe stato opportuno inserire i parametri non predefiniti nel file di configurazione my.cnf poiché due dei answerers menzionano i parametri:

max_heap_table_size = 1300M 
key_buffer_size = 0 
read_buffer_size = 1300M 
read_rnd_buffer_size = 1024M 
sort_buffer_size = 1300M 

Ho 2 GB di RAM sul mio server di prova.

+0

Puoi provare ad aggiungere un indice su '(type, closing_date)' o '(type, floor_area)' - assumendo che siano nella stessa tabella (non è ovvio senza i dettagli delle tabelle - per favore aggiungili). Questo può aiutare la query in generale, non i picchi. –

+0

@ypercude: ci sono molte altre condizioni nelle colonne 'WHERE'. Sono d'accordo che sarebbe d'aiuto, ma in piccola parte a causa del problema di intervallo e della bassa cardinalità della colonna 'type'. È necessario risolvere il problema dell'indice semplice prima di provare l'indice composito. –

+0

'type' può avere una bassa cardinalità ma l'indice per aiutarlo dipenderà dalla cardinalità composta di' (type, floor_area) '. Se quasi tutte le tue file che sono state controllate ogni volta hanno 'type = 'condominium' allora non sarà di grande aiuto. Ma aiuterà occasionalmente. –

risposta

3

paio di cose:

  1. Perché sono a calcolare il prodotto di P.price e E.rate nella SELECT e aliasing come 'chiedere', quindi fare di nuovo il calcolo nella clausola dove? Dovrebbe essere in grado di fare AND ask BETWEEN k? and k? - Modifica: Questo non funzionerà a causa del modo in cui MySQL funziona. Apparentemente MySQL valuta la clausola WHERE prima di qualsiasi alias (sourced).

  2. Che tipo di indice avete su Exchange.currency e Property.currency?Se lo scambio è una tabella di ricerca, forse sarebbe meglio aggiungere una tabella di pivot (collegamento) con Property.Id e Exchange.Id

  3. L'ordine per floor_area forza MySQL a creare una tabella temporanea per eseguire l'ordinamento correttamente, c'è qualche possibilità che tu possa fare l'ordinamento a livello di app?

  4. L'aggiunta di un indice sulla colonna del tipo aiuterà anche.

- Modifica

Non sei sicuro di cosa si intende per il commento // this and key_buffer_size=0 prevents caching sul CURDATE dove condizionale, è possibile forzare alcuna memorizzazione nella cache SQL utilizzando il flag 'SQL_NO_CACHE' sul tuo select dichiarazione.

Quello che mi sento di raccomandare la società che è stata rimossa l'ORDER BY, è quello di aggiornare la dichiarazione di domanda come segue (Aggiunto P alias alle colonne per ridurre qualsiasi confusione):

WHERE P.type ='condominium' 
    AND P.floor_area >= k? 
    AND P.closing_date >= CURDATE() // No longer necessary with SQL_NO_CACHE 
    AND P.lat BETWEEN v? AND v? 
    AND P.lng BETWEEN v? AND v? 
    AND P.price * E.rate BETWEEN k? AND k? 

quindi aggiungere un indice del colonna 'type' e un indice composito nelle colonne 'type' e 'floor_area'. Come hai detto, la colonna type è una colonna di cardinalità bassa, ma la tabella è grande e dovrebbe essere d'aiuto. E anche se floor_area sembra essere una colonna di cardinalità elevata, l'indice composito contribuirà ad accelerare i tempi delle query.

Si consiglia inoltre alla ricerca se c'è una pena utilizzando TRA piuttosto che gli operatori gamma (>, <, < = etc.)

+0

1. Dice colonna sconosciuta 'ask' in 'where clausola' 2. nessun indice sulla valuta per entrambe le tabelle. Sto usando myISAM, non posso usare il vincolo di chiave esterna 3. Secondo il piano di esecuzione della query, non ci sono filesort, non suppongo alcuna tabella temporanea 4. Nessun altro indice è coinvolto in questa fase mentre sto testando l'indice floor_area. –

+0

Ho bisogno di conoscere i motivi dei due picchi e risolverlo. Altri tempi di interrogazione sono più o meno accettabili per una tabella di 5 milioni di righe. –

+0

@BenHuh: Spiacenti, avrei dovuto chiedere quale tipo di motore stavi utilizzando. Forse lo schema di post aiuterà. Nessun filesort è buono, tuttavia la tabella temporanea continuerà ad accadere dietro le quinte. Prova a prendere l'ORDER BY per ora e vedi se ottieni metriche più coerenti. –

3

Prova un indice sul tipo e floor_area (e possibilmente closing_date troppo).

di modificare i costanti dal tasso di cambio al posto della colonna prezzo:

P.price between (k?/E.rate) and (k?/E.rate) 

quindi provare un indice sul prezzo.

+0

Il tuo suggerimento sull'utilizzo di un indice di prezzo aiuta solo nel processo di filtraggio, ma non aiuta nel processo di ordinazione, anche se faccio un ordine per chiedere non è d'aiuto nell'ordinazione poiché il tasso non viene preso in considerazione . Dopo aver adottato il tuo suggerimento, l'ottimizzatore preferisce ancora utilizzare l'indice fla. Tenderei a pensare che l'indice sia più efficiente da usare per ordinare nel mio caso. Per la query specifica sopra, si tratta di un ordinamento per area di piano.Come discusso in precedenza, un indice composito sarà considerato in una fase successiva. Sono più interessato a risolvere il picco nei tempi di interrogazione. –

2

Sono diventato un po 'ossessionato da questa domanda; il picco è difficile da spiegare.

Ecco quello che ho fatto:

ho ricreato lo schema, e popolato la tabella di proprietà con 4,5 milioni di dischi, con valori casuali per le colonne numeriche e data. Questo quasi certamente non combacia con i tuoi dati - immagino che lat/long tendano a raggrupparsi in aree demografiche, i prezzi intorno a multipli di 10K, e lo spazio del pavimento sarà deviato verso valori di fascia bassa.

Ho eseguito la query con un intervallo di valori per lat, long, floorpace e prezzo. Con solo l'indice sulla superficie, ho visto che il piano di query ignorerebbe l'indice per alcuni valori dell'area del pavimento. Ciò è stato presumibilmente perché l'analizzatore di query ha deciso che il numero di record esclusi utilizzando l'indice era troppo piccolo. Tuttavia, nel rieseguire la query per una varietà di scenari diversi, ho notato che il piano di query ignorava l'indice ogni tanto - non può spiegarlo.

Vale sempre la pena eseguire ANALYZE TABLE quando si ha a che fare con questo tipo di stranezze.

Ho ottenuto risultati leggermente "esplicativi": in particolare, la tabella delle proprietà select ha fornito 'Using where; Uso temporaneo; Usando filesort '. Ciò suggerisce che l'indice è utilizzato solo per la clausola where e non per ordinare i risultati.

Ciò conferma che la spiegazione più probabile dei picchi di prestazioni non è tanto correlata al motore di query, quanto al modo in cui viene gestita la tabella temporanea e all'esigenza di eseguire un fileort. Nel tentativo di riprodurre questo problema, ho notato che i tempi di risposta sono aumentati drasticamente quando il numero di record restituiti dalla clausola "where" è aumentato, anche se non ho visto i picchi che hai notato.

Ho provato una varietà di indici diversi; l'utilizzo di tutte le chiavi nella clausola where velocizza il tempo di recupero dei record corrispondenti alla clausola where, ma non esegue nulla per l'ordine successivo.

Questo, ancora una volta, suggerisce che le prestazioni del tavolo temporaneo sono la causa degli spike. read_rnd_buffer_size sarebbe la cosa ovvia da guardare.

+0

Scusate la mia risposta in ritardo, cercherò di leggere read_rnd_buffer_size e vi aggiornerò presto. –

+0

Ho sollevato read_rnd_buffer_size da 1024M a 1300M. Non vi è alcuna riduzione osservabile dei tempi di interrogazione o alcuna riduzione dei picchi. Sembra che questo parametro sia già al valore ottimale. –

4

Credo di capire il motivo delle punte. Ecco come va:

Per prima cosa ho creato i tavoli e caricare alcuni dati generati in modo casuale su di esso:

Ecco la mia domanda:

SELECT SQL_NO_CACHE P.id, P.name, P.lat, P.lng, P.price * E.rate AS 'ask' 
FROM Property P 
INNER JOIN Exchange E ON E.currency = P.currency 
WHERE P.floor_area >= 2000 
    AND P.closing_date >= CURDATE() 
    AND P.type ='c' 
    AND P.lat BETWEEN 12.00 AND 22.00 
    AND P.lng BETWEEN 10.00 AND 20.00 
    AND P.price BETWEEN 100/E.rate AND 10000/E.rate 
ORDER BY P.floor_area DESC LIMIT 100; 

E qui è la descrizione:

+----+-------------+-------+-------+---------------+--------+---------+------+---------+----------------------------------------------+ 
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra          | 
+----+-------------+-------+-------+---------------+--------+---------+------+---------+----------------------------------------------+ 
| 1 | SIMPLE  | P  | range | id_flA  | id_flA | 3  | NULL | 4559537 | Using where; Using temporary; Using filesort | 
| 1 | SIMPLE  | E  | ALL | PRIMARY  | NULL | NULL | NULL |  6 | Using where; Using join buffer    | 
+----+-------------+-------+-------+---------------+--------+---------+------+---------+----------------------------------------------+ 

impiegato tra 3.5 ~ 3.9 sec ogni volta che interrogo i dati (non ho fatto alcuna differenza quali parametri utilizzo). Non aveva senso quindi ho ricercato Using join buffer

Poi ho voluto provare questa query senza "unire il buffer" così ho inserito 1 altro dato casuale nella tabella di Exchange.

INSERT INTO Exchange(currency, rate) VALUES('JJ', 1); 

Ora uso la stessa SQL e ci sono voluti 0,3 ~ 0,5 secondo per la risposta. E qui è la descrizione:

+----+-------------+-------+--------+---------------+---------+---------+-----------------+---------+-------------+ 
| id | select_type | table | type | possible_keys | key  | key_len | ref    | rows | Extra  | 
+----+-------------+-------+--------+---------------+---------+---------+-----------------+---------+-------------+ 
| 1 | SIMPLE  | P  | range | id_flA  | id_flA | 3  | NULL   | 4559537 | Using where | 
| 1 | SIMPLE  | E  | eq_ref | PRIMARY  | PRIMARY | 3  | test.P.currency |  1 | Using where | 
+----+-------------+-------+--------+---------------+---------+---------+-----------------+---------+-------------+ 

Quindi il problema (per quanto vedo), l'ottimizzatore cercando di usare "join cuscinetto". La soluzione ottimale di questo problema sarebbe forzare l'ottimizzatore a non utilizzare "unire il buffer". (che non ho potuto trovare come fare) o modificare il valore "join_buffer_size". Lo risolvo aggiungendo valori "fittizi" alla tabella di Exchange (quindi l'ottimizzatore non userebbe il buffer di join) ma non è una soluzione esatta, è solo uno stupido trucco per ingannare mysql.

Modifica: Ho ricercato in forum mysql/bug su questo comportamento di "join buffer"; poi ha chiesto informazioni su di esso in official forums. Sto andando a compilare un bug report su questo comportamento irrazionale dell'ottimizzatore.

+0

Ho letto il link sul buffer di join. Dice che ulteriori informazioni sul buffer di join sono state implementate dalla versione 5.1.18 in poi. Sto usando la versione 5.5.14, quindi presumo che sarebbe mostrato. Ma da tutte le prove di cui sopra, sotto la colonna in più, ho solo "Usare dove". Il picco che ho riscontrato è superiore a 30 secondi per le query 4 e 7. Penso che ci sia un altro motivo, altrimenti sarebbe circa 3 secondi rispetto alla prova. Grazie per lo sforzo però :) –

+0

hai provato a cambiare join_buffer_size nella configurazione? la mia ipotesi era alcune delle vostre domande (non tutte) "usando un buffer di join" invece di "using where". nel mio set di dati i dati sono stati distribuiti in modo uniforme (grazie a random) quindi tutte le mie query utilizzate da Eighter o dove, ma poiché i dati non sono equamente distribuiti, il problema può essere costituito da alcune query utilizzando "join buffer". – frail

+0

Ci scusiamo per la mia risposta in ritardo, esaminerò il join_buffer_size e ti aggiornerò presto. –

Problemi correlati