Baseret på EXPLAIN
output i dit spørgsmål, har du allerede alle de indekser, som forespørgslen skal bruger, nemlig:
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);
(Jeg er ikke sikker på, ud fra dine indeksnavne, om idx_zip_from_distance
indeholder virkelig zipcode_to
kolonne. Hvis ikke, bør du tilføje det for at gøre det til et dækkende indeks
. Jeg har også inkluderet venues.id
kolonne i idx_zipcode
for fuldstændighedens skyld, men forudsat at det er den primære nøgle til tabellen, og at du bruger InnoDB, vil den blive inkluderet automatisk alligevel.)
Det ser dog ud til, at MySQL vælger en anden, og muligvis suboptimal, forespørgselsplan, hvor den scanner gennem alle begivenheder, finder deres spillesteder og postnumre og først derefter filtrerer resultaterne på distance. Dette kunne være den optimale forespørgselsplan, hvis kardinaliteten af hændelsestabellen var lav nok, men ud fra det faktum, at du stiller dette spørgsmål, antager jeg, at det ikke er det.
En grund til den suboptimale forespørgselsplan kunne være det faktum, at du har for mange indekser, som forvirrer planlæggeren. For eksempel, gør du virkelig har brug for alle tre af disse indekser på postnummertabellen, i betragtning af at de data, den gemmer, formentlig er symmetriske? Personligt vil jeg kun foreslå det indeks, jeg beskrev ovenfor, plus et unikt indeks (som også kan være den primære nøgle, hvis du ikke har et kunstigt et) på (zipcode_to, zipcode_from)
(helst i den rækkefølge, så eventuelle lejlighedsvise forespørgsler på zipcode_to=?
kan gøre brug af det).
Men baseret på nogle tests, jeg foretog, har jeg mistanke om, at hovedproblemet, hvorfor MySQL vælger den forkerte forespørgselsplan, simpelthen skyldes de relative kardinaliteter af dine tabeller. Formentlig dine faktiske zipcode_distances
tabellen er enorm , og MySQL er ikke smart nok til at indse, hvor meget forholdene i WHERE
klausul virkelig indsnævre det.
Hvis det er tilfældet, kan den bedste og enkleste løsning være at tvinge MySQL for at bruge de indekser, du ønsker :
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
Med den forespørgsel burde du faktisk få den ønskede forespørgselsplan. (Du skal bruge FORCE INDEX
her, da med kun USE INDEX
forespørgselsplanlæggeren kunne stadig beslutte at bruge en tabelscanning i stedet for det foreslåede indeks, hvilket besejrede formålet. Det skete, da jeg første gang testede dette.)
Ps. Her er en demo om SQLize, både med
og uden
FORCE INDEX
, der demonstrerer problemet.