2011-10-06 11 views
5

Ho testato diverse idee per ottimizzare alcuni dei tavoli che abbiamo nel nostro sistema al lavoro. Oggi mi sono imbattuto in un tavolo che traccia ogni vista su ogni veicolo nel nostro sistema. Crea la tabella qui sotto.MySQL EXPLAIN "type" cambia da "range" a "ref" quando la data nell'istruzione where viene modificata?

SHOW CREATE TABLE vehicle_view_tracking; 

CREATE TABLE `vehicle_view_tracking` (
    `vehicle_view_tracking_id` int(10) unsigned NOT NULL AUTO_INCREMENT, 
    `public_key` varchar(45) NOT NULL, 
    `vehicle_id` int(10) unsigned NOT NULL, 
    `landing_url` longtext NOT NULL, 
    `landing_port` int(11) NOT NULL, 
    `http_referrer` longtext, 
    `created_on` datetime NOT NULL, 
    `created_on_date` date NOT NULL, 
    `server_host` longtext, 
    `server_uri` longtext, 
    `referrer_host` longtext, 
    `referrer_uri` longtext, 
    PRIMARY KEY (`vehicle_view_tracking_id`), 
    KEY `vehicleViewTrackingKeyCreatedIndex` (`public_key`,`created_on_date`), 
    KEY `vehicleViewTrackingKeyIndex` (`public_key`) 
) ENGINE=InnoDB AUTO_INCREMENT=363439 DEFAULT CHARSET=latin1; 

Mi stavo giocando con indici a colonne multiple e singole colonne. Ho eseguito la seguente query:

EXPLAIN EXTENDED SELECT dealership_vehicles.vehicle_make, dealership_vehicles.vehicle_model, vehicle_view_tracking.referrer_host, count(*) AS count 
FROM vehicle_view_tracking 
LEFT JOIN dealership_vehicles 
ON dealership_vehicles.dealership_vehicle_id = vehicle_view_tracking.vehicle_id 
WHERE vehicle_view_tracking.created_on_date >= '2011-09-07' AND vehicle_view_tracking.public_key IN ('ab12c3') 
GROUP BY (dealership_vehicles.vehicle_make) ASC , dealership_vehicles.vehicle_model, referrer_host 

+----+-------------+-----------------------+--------+----------------------------------------------------------------+------------------------------------+---------+----------------------------------------------+-------+----------+----------------------------------------------+ 
| id | select_type | table     | type | possible_keys             | key        | key_len | ref           | rows | filtered | Extra          | 
+----+-------------+-----------------------+--------+----------------------------------------------------------------+------------------------------------+---------+----------------------------------------------+-------+----------+----------------------------------------------+ 
| 1 | SIMPLE  | vehicle_view_tracking | range | vehicleViewTrackingKeyCreatedIndex,vehicleViewTrackingKeyIndex | vehicleViewTrackingKeyCreatedIndex | 50  | NULL           | 23086 | 100.00 | Using where; Using temporary; Using filesort | 
| 1 | SIMPLE  | dealership_vehicles | eq_ref | PRIMARY              | PRIMARY       | 8  | vehicle_view_tracking.vehicle_id |  1 | 100.00 |            | 
+----+-------------+-----------------------+--------+----------------------------------------------------------------+------------------------------------+---------+----------------------------------------------+-------+----------+----------------------------------------------+ 

(tempo di esecuzione per la effettiva query di selezione è stato .309 secondi)

poi a cambiare la data nella clausola WHERE da '2011-09-07' a '2011- 07-07' e preso i seguenti spiegare i risultati

EXPLAIN EXTENDED SELECT dealership_vehicles.vehicle_make, dealership_vehicles.vehicle_model, vehicle_view_tracking.referrer_host, count(*) AS count 
FROM vehicle_view_tracking 
LEFT JOIN dealership_vehicles 
ON dealership_vehicles.dealership_vehicle_id = vehicle_view_tracking.vehicle_id 
WHERE vehicle_view_tracking.created_on_date >= '2011-07-07' AND vehicle_view_tracking.public_key IN ('ab12c3') 
GROUP BY (dealership_vehicles.vehicle_make) ASC , dealership_vehicles.vehicle_model, referrer_host 


+----+-------------+-----------------------+--------+----------------------------------------------------------------+-----------------------------+---------+----------------------------------------------+-------+----------+----------------------------------------------+ 
| id | select_type | table     | type | possible_keys             | key       | key_len | ref           | rows | filtered | Extra          | 
+----+-------------+-----------------------+--------+----------------------------------------------------------------+-----------------------------+---------+----------------------------------------------+-------+----------+----------------------------------------------+ 
| 1 | SIMPLE  | vehicle_view_tracking | ref | vehicleViewTrackingKeyCreatedIndex,vehicleViewTrackingKeyIndex | vehicleViewTrackingKeyIndex | 47  | const          | 53676 | 100.00 | Using where; Using temporary; Using filesort | 
| 1 | SIMPLE  | dealership_vehicles | eq_ref | PRIMARY              | PRIMARY      | 8  | vehicle_view_tracking.vehicle_id |  1 | 100.00 |            | 
+----+-------------+-----------------------+--------+----------------------------------------------------------------+-----------------------------+---------+----------------------------------------------+-------+----------+----------------------------------------------+ 

(tempo di esecuzione per la query di selezione effettivo era .670 secondi)

vedo 4 cambiamenti principali:

0.123.
  1. tipo cambiato da gamma al rif
  2. chiave cambiato da vehicleViewTrackingKeyCreatedIndex a vehicleViewTrackingKeyIndex
  3. key_len passa da 50 47 (causata dal cambiamento di chiave)
  4. righe modificate da 23086 a 53676 (causato dal cambio di chiave)

A questo punto, il tempo di esecuzione è di soli 6 secondi per la query lenta, tuttavia nel nostro database abbiamo solo il 10% circa dei nostri veicoli.

Si sta facendo tardi e potrei aver trascurato qualcosa nei documenti mysql ma non riesco a trovare il motivo per cui la chiave (ea sua volta il tipo e le righe) cambiano quando la data viene modificata nella clausola where.

L'aiuto è molto apprezzato. Ho cercato qualcuno che avesse lo stesso problema/simile a una data che causava questo cambiamento e non è stato in grado di trovare nulla. Se ho perso un post precedente, per favore collegami :-)

risposta

7

Diverse strategie di ricerca hanno senso per dati diversi. In particolare, le scansioni di indice (come l'intervallo) devono spesso cercare di leggere effettivamente la riga. Ad un certo punto, fare tutte quelle ricerche è più lento che non usare l'indice.

Prendi un esempio banale, una tabella con tre colonne: id (chiave primaria), nome (indicizzato), compleanno. Dì che ha molti dati. Se chiedi a MySQL di cercare il compleanno di Bob, può farlo abbastanza velocemente: in primo luogo, trova Bob nell'indice del nome (questo richiede un paio di ricerche, log (n) dove n è il numero di righe), quindi un ulteriore tentativo di leggere la riga effettiva nel file di dati e leggere il compleanno da esso. È molto veloce e molto più rapido rispetto alla scansione dell'intero tavolo.

Successivamente, prendere in considerazione un name like 'Z%'. Questa è probabilmente una porzione piuttosto piccola del tavolo. Quindi è ancora più veloce trovare dove iniziano gli Z nell'indice del nome, quindi per ognuno cercare il file di dati per leggere la riga. (Questa è una scansione dell'intervallo).

Infine, valutare la possibilità di richiedere tutti i nomi che iniziano con M-Z. Probabilmente è circa la metà dei dati.Potrebbe eseguire una scansione dell'intervallo e quindi un lotto di ricerche, ma cercare casualmente il file di dati con l'obiettivo finale di leggere metà delle righe non è ottimale: sarebbe più veloce fare semplicemente una grande lettura sequenziale sul file di dati. Quindi, in questo caso, l'indice verrà ignorato.

Questo è quello che stai vedendo, tranne nel tuo caso, c'è un'altra chiave su cui può ricadere. (È anche possibile che possa effettivamente usare l'indice della data se non ha l'altro, dovrebbe scegliere quale indice sarà più veloce. Attenzione che l'ottimizzatore di MySQL spesso fa errori in questo.)

Quindi, in breve, questo è previsto. Una query non dice come per recuperare i dati, piuttosto dice quali dati recuperare. L'ottimizzatore del database dovrebbe trovare il modo più veloce per recuperarlo.

È possibile trovare un indice su entrambe le colonne , nell'ordine (public_key, created_on_date) è preferibile in entrambi i casi e accelera la query. Questo perché MySQL può sempre usare solo un indice per tabella (per query). Inoltre, la data finisce alla fine perché una scansione di intervallo può essere eseguita in modo efficiente solo nell'ultima colonna di un indice.

[InnoDB ha effettivamente un altro livello di riferimento indiretto, credo, ma confonderebbe il punto. Non fa differenza per la spiegazione.]

+0

Quindi, in poche parole, l'ottimizzatore mysql ha ritenuto che fosse meglio/più veloce apportare la modifica e utilizzare l'altro indice. Ho eseguito un altro test e rimosso il secondo indice (vehicleViewTrackingKeyIndex) e il tempo di query era compreso tra 0,01 secondi. Sembra che con l'aumentare del set di risultati, si è reso conto che non aveva senso utilizzare l'indice a 2 colonne. – CriticalSpeak

+0

@CriticalSpeak: Sì, in poche parole. Spesso devi giocare con gli indici (e riscrivere le query) molto più di quanto dovresti avere in MySQL perché il suo ottimizzatore ha molte lacune. Se ti senti male, prova PostgreSQL. – derobert

Problemi correlati