Mysql
 sql >> Baza danych >  >> RDS >> Mysql

Jak mogę uniknąć pełnego skanowania tabeli w tym zapytaniu mysql?

Na podstawie EXPLAIN wypisz w swoim pytaniu, masz już wszystkie indeksy, które zapytanie powinno używać, a mianowicie:

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);

(Nie jestem pewien na podstawie nazw indeksów, czy idx_zip_from_distance naprawdę zawiera zipcode_to kolumna. Jeśli nie, dodaj go, aby stał się indeksem pokrycia . Dołączyłem również venues.id kolumna w idx_zipcode dla kompletności, ale zakładając, że jest to klucz podstawowy dla tabeli i używasz InnoDB, i tak zostanie dołączony automatycznie.)

Wygląda jednak na to, że MySQL wybiera inny i prawdopodobnie nieoptymalny plan zapytań, w którym skanuje wszystkie zdarzenia, znajduje ich lokalizacje i kody pocztowe, a dopiero potem filtruje wyniki na odległość. To może być optymalnym planem zapytania, jeśli liczność tabeli zdarzeń była wystarczająco niska, ale z faktu, że zadajesz to pytanie, zakładam, że tak nie jest.

Jednym z powodów nieoptymalnego planu zapytań może bądź faktem, że masz za dużo indeksy, które dezorientują planistę. Na przykład, czy naprawdę? potrzebujesz wszystkich trzech indeksów w tabeli kodów pocztowych, biorąc pod uwagę, że dane, które przechowuje, są prawdopodobnie symetryczne? Osobiście sugerowałbym tylko indeks, który opisałem powyżej, plus unikalny indeks (który może być również kluczem podstawowym, jeśli nie masz sztucznego) na (zipcode_to, zipcode_from) (najlepiej w tej kolejności, aby wszelkie sporadyczne zapytania dotyczące zipcode_to=? może z niego skorzystać).

Jednak na podstawie niektórych testów, które przeprowadziłem, podejrzewam, że główny problem, dlaczego MySQL wybiera zły plan zapytań, sprowadza się po prostu do względnych kardynałów twoich tabel. Przypuszczalnie Twoje rzeczywiste zipcode_distances stół jest ogromny , a MySQL nie jest wystarczająco sprytny, aby zdać sobie sprawę, jak bardzo są warunki w WHERE klauzula naprawdę zawęża to.

Jeśli tak, najlepszym i najprostszym rozwiązaniem może być po prostu wymuszenie MySQL do korzystania z indeksów, które chcesz :

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

Dzięki temu zapytaniu rzeczywiście powinieneś otrzymać pożądany plan zapytań. (Potrzebujesz FORCE INDEX tutaj, ponieważ wystarczy USE INDEX planista zapytań może nadal zdecydować się na użycie skanowania tabeli zamiast sugerowanego indeksu, co jest sprzeczne z celem. Zdarzyło mi się to, kiedy pierwszy raz to przetestowałem.)

Ps. Oto demo SQLize, oba z i bez FORCE INDEX , przedstawiając problem.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Jak połączyć dwie bazy danych mySQL w jedną?

  2. Czy są jakieś dobre powody, dla których nie powinienem używać - (myślnik) w nazwach pól w MySQL?

  3. Zaprojektowanie znormalizowanej bazy danych w celu uzyskania 5 najbliższych sąsiadów przy użyciu indeksu przestrzennego MySQL

  4. Grupa MySQL od kwartałów do okresów

  5. Relacja JPA OneToMany/ManyToOne nie działa - Czego mi brakuje?