sql >> Database >  >> RDS >> Mysql

Hoe kan ik een volledige tabelscan op deze mysql-query vermijden?

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.



  1. MYSQL - Query verwijderen met Join

  2. Failover en failback op Amazon RDS

  3. Rails Resque-werknemers mislukken met PGError:server heeft de verbinding onverwacht gesloten

  4. Opties voor cloudback-up voor PostgreSQL