sql >> Database teknologi >  >> RDS >> Mysql

Hvordan kan jeg undgå en fuld tabelscanning på denne mysql-forespørgsel?

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.



  1. rake db:create - sorteringsproblemer

  2. Hvordan ville jeg implementere en simpel webstedssøgning med php og mySQL?

  3. Hvordan hæver man undtagelser i PL/SQL?

  4. PDO forbindelsesfejl ved brug af symfony og MAMP