Basato su EXPLAIN
output nella tua domanda, hai già tutti gli indici che la query dovrebbe utilizzare, ovvero:
CREATE INDEX idx_zip_from_distance
ON zipcode_distances (zipcode_from, distance, zipcode_to);
CREATE INDEX idx_zipcode ON venues (zipcode, id);
CREATE INDEX idx_venue_id ON events (venue_id);
(Non sono sicuro dai nomi dei tuoi indici se idx_zip_from_distance
include davvero il zipcode_to
colonna. In caso contrario, dovresti aggiungerlo per renderlo un indice di copertura
. Inoltre, ho incluso venues.id
colonna in idx_zipcode
per completezza, ma, supponendo che sia la chiave primaria per la tabella e che tu stia utilizzando InnoDB, verrà comunque inclusa automaticamente.)
Tuttavia, sembra che MySQL stia scegliendo un piano di query diverso, e forse non ottimale, in cui esegue la scansione di tutti gli eventi, trova le sedi e i codici postali e solo successivamente filtra i risultati in base alla distanza. Questo potrebbe essere il piano di query ottimale, se la cardinalità della tabella degli eventi fosse sufficientemente bassa, ma dal fatto che stai ponendo questa domanda suppongo che non lo sia.
Uno dei motivi per cui il piano di query non ottimale potrebbe sia il fatto che ne hai troppi indici che confondono il pianificatore. Ad esempio, davvero hai bisogno di tutti e tre questi indici sulla tabella del codice postale, dato che i dati che memorizza sono presumibilmente simmetrici? Personalmente, suggerirei solo l'indice che ho descritto sopra, più un indice univoco (che può anche essere la chiave primaria, se non ne hai una artificiale) su (zipcode_to, zipcode_from)
(preferibilmente in quest'ordine, in modo che eventuali query occasionali su zipcode_to=?
può farne uso).
Tuttavia, sulla base di alcuni test che ho eseguito, sospetto che il problema principale per cui MySQL sta scegliendo il piano di query sbagliato dipenda semplicemente dalle cardinalità relative delle tabelle. Presumibilmente, le tue attuali zipcode_distances
la tabella è enorme e MySQL non è abbastanza intelligente da rendersi conto di quanto siano le condizioni nel WHERE
clausola davvero restringere il campo.
In tal caso, la soluzione migliore e più semplice potrebbe essere semplicemente forzare MySQL per utilizzare gli indici desiderati :
select
*
from
zipcode_distances z
FORCE INDEX (idx_zip_from_distance)
inner join
venues v
FORCE INDEX (idx_zipcode)
on z.zipcode_to=v.zipcode
inner join
events e
FORCE INDEX (idx_venue_id)
on v.id=e.venue_id
where
z.zipcode_from='92108' and
z.distance <= 5
Con quella query, dovresti effettivamente ottenere il piano di query desiderato. (Hai bisogno di FORCE INDEX
qui, poiché con solo USE INDEX
il pianificatore di query potrebbe comunque decidere di utilizzare una scansione della tabella invece dell'indice suggerito, vanificando lo scopo. Mi è successo quando l'ho testato per la prima volta.)
Sal. Ecco una demo su SQLize, entrambi con
e senza
FORCE INDEX
, a dimostrazione del problema.