Gebaseerd op de EXPLAIN
uitvoer in uw vraag, heeft u al alle indexen die de zoekopdracht moet gebruiken, namelijk:
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);
(Ik weet op basis van uw indexnamen niet zeker of idx_zip_from_distance
bevat echt de zipcode_to
kolom. Als dat niet het geval is, moet u het toevoegen om er een dekkingsindex van te maken
. Ik heb ook de venues.id
kolom in idx_zipcode
voor de volledigheid, maar ervan uitgaande dat dit de primaire sleutel voor de tabel is en dat u InnoDB gebruikt, wordt deze hoe dan ook automatisch opgenomen.)
Het lijkt er echter op dat MySQL een ander en mogelijk suboptimaal zoekplan kiest, waarbij het alle evenementen doorzoekt, hun locaties en postcodes vindt en pas dan de resultaten op afstand filtert. Dit zou wees het optimale zoekplan, als de kardinaliteit van de gebeurtenissentabel laag genoeg was, maar gezien het feit dat je deze vraag stelt, neem ik aan dat dit niet het geval is.
Een reden voor het suboptimale zoekplan zou het feit zijn dat je te veel hebt indexen die de planner verwarren. Denk je bijvoorbeeld echt alle drie die indexen in de postcodetabel nodig hebben, aangezien de gegevens die het opslaat vermoedelijk symmetrisch zijn? Persoonlijk zou ik alleen de index voorstellen die ik hierboven heb beschreven, plus een unieke index (die ook de primaire sleutel kan zijn, als je geen kunstmatige sleutel hebt) op (zipcode_to, zipcode_from)
(bij voorkeur in die volgorde, zodat eventuele vragen op zipcode_to=?
kan er gebruik van maken).
Echter, op basis van enkele tests die ik heb gedaan, vermoed ik dat het belangrijkste probleem waarom MySQL het verkeerde queryplan kiest, simpelweg te maken heeft met de relatieve kardinaliteiten van uw tabellen. Vermoedelijk uw werkelijke zipcode_distances
tafel is groot , en MySQL is niet slim genoeg om te beseffen hoeveel de voorwaarden in de WHERE
clausule verkleint het echt.
Als dat zo is, is de beste en eenvoudigste oplossing misschien om gewoon forceren MySQL om de gewenste indexen te gebruiken :
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
Met die query zou u inderdaad het gewenste queryplan moeten krijgen. (Je hebt wel FORCE INDEX
nodig hier, want met slechts USE INDEX
de queryplanner zou nog steeds kunnen besluiten om een tabelscan te gebruiken in plaats van de voorgestelde index, waardoor het doel teniet wordt gedaan. Ik had dit toen ik dit voor het eerst testte.)
Ps. Hier is een demo over SQLize, beide met
en zonder
FORCE INDEX
, waarmee het probleem wordt aangetoond.