Kiedy używać STRAIGHT_JOIN z MySQL


88

Właśnie otrzymałem dość złożone zapytanie, nad którym pracowałem, a jego wykonanie zajęło 8 sekund. EXPLAIN pokazywał dziwną kolejność tabel, a moje indeksy nie były używane nawet z podpowiedzią FORCE INDEX. Natknąłem się na słowo kluczowe Join STRAIGHT_JOIN i zacząłem zastępować nim niektóre z moich słów kluczowych INNER JOIN. Zauważyłem znaczną poprawę prędkości. Ostatecznie właśnie zastąpiłem wszystkie moje słowa kluczowe INNER JOIN na STRAIGHT_JOIN dla tego zapytania i teraz trwa ono 0,01 sekundy.

Moje pytanie brzmi: kiedy używasz STRAIGHT_JOIN, a kiedy INNER JOIN? Czy jest jakiś powód, aby nie używać STRAIGHT_JOIN, jeśli piszesz dobre zapytania?

Odpowiedzi:


73

Nie polecałbym używania STRAIGHT_JOIN bez ważnego powodu. Z własnego doświadczenia wiem, że optymalizator zapytań MySQL wybiera kiepski plan zapytań częściej, niż bym chciał, ale nie na tyle często, aby w ogóle go ominąć, co by się stało, gdybyś zawsze używał STRAIGHT_JOIN.

Zalecam pozostawienie wszystkich zapytań jako zwykłych DOŁĄCZ. Jeśli odkryjesz, że jedno zapytanie korzysta z nieoptymalnego planu zapytań, sugerowałbym najpierw spróbować przepisać lub nieco zmienić strukturę zapytania, aby zobaczyć, czy optymalizator wybierze lepszy plan zapytania. Ponadto przynajmniej w przypadku innodb upewnij się, że nie chodzi tylko o to, że statystyki indeksu są nieaktualne ( TABELA ANALIZOWANIA ). Może to spowodować, że optymalizator wybierze kiepski plan zapytań. Wskazówki dotyczące optymalizatora powinny być generalnie ostatnią deską ratunku.

Innym powodem, dla którego nie należy używać wskazówek dotyczących zapytań, jest to, że dystrybucja danych może zmieniać się w czasie lub selektywność indeksu może się zmieniać w miarę wzrostu tabeli. Twoje wskazówki dotyczące zapytań, które są teraz optymalne, mogą z czasem stać się nieoptymalne. Jednak optymalizator nie będzie w stanie dostosować planu zapytań z powodu twoich przestarzałych wskazówek. Pozostajesz bardziej elastyczny, jeśli pozwalasz optymalizatorowi podejmować decyzje.


59
Ta odpowiedź właściwie nie wyjaśnia, kiedy użyć straight_join .
Pacerier

23

Z referencji MySQL JOIN :

„STRAIGHT_JOIN jest podobna do JOIN, z tą różnicą, że lewa tabela jest zawsze czytana przed właściwą tabelą. Można tego użyć w tych (kilku) przypadkach, w których optymalizator łączenia ustawia tabele w niewłaściwej kolejności.”


27
Dzięki, ale przeczytałem już podręcznik MySQL na ten temat. Mam nadzieję na dalsze wyjaśnienia.
Greg

20

Oto scenariusz, który pojawił się niedawno w pracy.

Rozważ trzy tabele, A, B, C.

A ma 3000 wierszy; B ma 300 000 000 wierszy; a C ma 2000 wierszy.

Zdefiniowano klucze obce: B (a_id), B (c_id).

Załóżmy, że masz zapytanie, które wygląda następująco:

select a.id, c.id
from a
join b on b.a_id = a.id
join c on c.id = b.c_id

Z mojego doświadczenia wynika, że ​​MySQL może w tym przypadku wybrać C -> B -> A. C jest mniejsze niż A, a B jest ogromne i wszystkie są równonożne.

Problem polega na tym, że MySQL niekoniecznie bierze pod uwagę rozmiar przecięcia między (C.id i B.c_id) a (A.id i B.a_id). Jeśli sprzężenie między B i C zwraca tyle samo wierszy, co B, to jest to bardzo zły wybór; gdyby zaczynając od A przefiltrował B do tylu wierszy co A, to byłby to znacznie lepszy wybór. straight_joinmożna użyć do wymuszenia tego zamówienia w następujący sposób:

select a.id, c.id
from a
straight_join b on b.a_id = a.id
join c on c.id = b.c_id

Teraz anależy dołączyć wcześniej b.

Ogólnie rzecz biorąc, chcesz wykonywać łączenia w kolejności minimalizującej liczbę wierszy w wynikowym zestawie. Zatem rozpoczęcie od małego stolika i łączenie w taki sposób, aby wynikowe połączenie również było małe, jest idealne. Sprawy przybierają kształt gruszki, jeśli zaczynając od małego stołu i łącząc go z większym stołem, kończy się tak samo, jak duży stół.

Jest to jednak zależne od statystyk. Jeśli zmieni się dystrybucja danych, obliczenia mogą ulec zmianie. Zależy to również od szczegółów implementacji mechanizmu łączenia.

Najgorsze przypadki, jakie widziałem w przypadku MySQL, w których wszystkie oprócz wymaganych straight_joinlub agresywnych podpowiedzi do indeksów, to zapytania, które dzielą się na strony wielu danych w ścisłej kolejności sortowania z lekkim filtrowaniem. MySQL zdecydowanie preferuje używanie indeksów do wszelkich filtrów i łączenia zamiast sortowania; ma to sens, ponieważ większość ludzi nie próbuje sortować całej bazy danych, ale raczej ma ograniczony podzbiór wierszy, które odpowiadają na zapytanie, a sortowanie ograniczonego podzbioru jest znacznie szybsze niż filtrowanie całej tabeli, bez względu na to, czy jest posortowana, czy nie. W tym przypadku, wstawiając proste sprzężenie bezpośrednio po tabeli, która miała zindeksowaną kolumnę, chciałem posortować według ustalonych rzeczy.


Jak użyłbyś sprzężenia bezpośredniego, aby rozwiązać problem?
Hannele

@Hannele straight_joinocenia lewą tabelę przed prawą. Więc jeśli chcesz przejść z A -> B -> Cw moim przykładzie, pierwsze joinsłowo kluczowe można zastąpić straight_join.
Barry Kelly,

Ach fajnie. Przydałoby się to jako przykład w swojej odpowiedzi :)
Hannele

18

MySQL nie jest koniecznie dobry w wyborze kolejności łączenia w złożonych zapytaniach. Określając złożone zapytanie jako straight_join, zapytanie wykonuje sprzężenia w kolejności, w jakiej są one określone. Umieszczając najpierw tabelę jako najmniejszy wspólny mianownik i określając straight_join, możesz poprawić wydajność zapytania.


11

STRAIGHT_JOIN, używając tej klauzuli, możesz kontrolować JOINkolejność: która tabela jest skanowana w pętli zewnętrznej, a która w pętli wewnętrznej.


Co to jest pętla zewnętrzna i wewnętrzna?
Istiaque Ahmed

Tabele @IstiaqueAhmed są połączone zagnieżdżonymi pętlami (weź pierwszy wiersz z tabeli A i rzuć tabelę B w pętli, a następnie weź drugi wiersz ... i tak dalej. Tutaj tabela A jest na zewnętrznej pętli)
Księgowy م

6

Powiem ci, dlaczego musiałem użyć STRAIGHT_JOIN:

  • Miałem wydajności problem z kwerendy.
  • Upraszczając zapytanie, stało się ono nagle bardziej wydajne
  • Próbując dowiedzieć się, która konkretna część powoduje problem, po prostu nie mogłem. (2 połączenia po lewej stronie były powolne, a każde niezależnie szybkie)
  • Następnie wykonałem EXPLAIN z zarówno wolnym, jak i szybkim zapytaniem (dodaj jedno z lewych złączeń)
  • Co zaskakujące, MySQL całkowicie zmienił kolejność JOIN między dwoma zapytaniami.

Dlatego wymusiłem na jednym z łączeń straight_join, aby WYMUSIĆ poprzednie sprzężenie, aby było czytane jako pierwsze. To uniemożliwiło MySQL zmianę kolejności wykonywania i działało jak marzenie!


2

Z mojego krótkiego doświadczenia wynika, że ​​jedną z sytuacji, STRAIGHT_JOINktóra skróciła moje zapytanie z 30 sekund do 100 milisekund, jest to, że pierwsza tabela w planie wykonania nie była tabelą, która ma kolejność według kolumn

-- table sales (45000000) rows
-- table stores (3) rows
SELECT whatever
FROM 
    sales 
    INNER JOIN stores ON sales.storeId = stores.id
ORDER BY sales.date, sales.id 
LIMIT 50;
-- there is an index on (date, id)

JEŚLI optymalizator zdecyduje się uderzyć jako stores pierwszy , spowoduje to, Using index; Using temporary; Using filesortponieważ

jeśli ORDER BY lub GROUP BY zawiera kolumny z tabel innych niż pierwsza tabela w kolejce łączenia, tworzona jest tabela tymczasowa.

źródło

tutaj optymalizator potrzebuje niewielkiej pomocy, mówiąc mu, aby uderzył jako salespierwszy za pomocą

sales STRAIGHT_JOIN stores

1
(Ozdabiałem twoją odpowiedź.)
Rick James

2

Jeśli Twoje zapytania z końców ORDER BY... LIMIT..., to może być optymalna przeformułować zapytania oszukać optymalizator do wykonując LIMIT przedJOIN .

(Ta odpowiedź nie dotyczy tylko pierwotnego pytania STRAIGHT_JOIN, ani nie ma zastosowania we wszystkich przypadkach STRAIGHT_JOIN.)

Zaczynając od przykładu @Accountant م , powinno to działać szybciej w większości sytuacji. (I unika potrzeby podpowiedzi.)

SELECT  whatever
    FROM  ( SELECT id FROM sales
                ORDER BY  date, id
                LIMIT  50
          ) AS x
    JOIN  sales   ON sales.id = x.id
    JOIN  stores  ON sales.storeId = stores.id
    ORDER BY  sales.date, sales.id;

Uwagi:

  • Najpierw pobieranych jest 50 identyfikatorów. Będzie to szczególnie szybkie w przypadku INDEX(date, id).
  • Następnie połączenie z powrotem do salespozwala uzyskać tylko 50 „bez względu na wszystko” bez ciągnięcia ich w tymczasowym stole.
  • ponieważ podzapytanie jest z definicji nieuporządkowane, ORDER BYnależy je powtórzyć w zapytaniu zewnętrznym. (Optymalizator może znaleźć sposób na uniknięcie wykonywania innego sortowania).
  • Tak, jest bardziej niechlujny. Ale zwykle jest szybszy.

Sprzeciwiam się używaniu hitów, ponieważ „Nawet jeśli jest szybszy dzisiaj, jutro może nie być szybszy”.


0

Wiem, że jest trochę stary, ale oto scenariusz, robiłem skrypt wsadowy, aby wypełnić określoną tabelę. W pewnym momencie zapytanie działało bardzo wolno. Wygląda na to, że kolejność łączenia była nieprawidłowa w niektórych rekordach:

  • W odpowiedniej kolejności

wprowadź opis obrazu tutaj

  • Zwiększenie identyfikatora o 1 psuje zamówienie. Zwróć uwagę na pole „Extra”

wprowadź opis obrazu tutaj

  • Użycie straight_join rozwiązuje problem

wprowadź opis obrazu tutaj

Nieprawidłowa kolejność działa przez około 65 sekund, podczas gdy używa się przebiegów straight_join w milisekundach


-5
--use 120s, 18 million data
    explain SELECT DISTINCT d.taid
    FROM tvassist_recommend_list_everyday_diverse d, tvassist_taid_all t
    WHERE d.taid = t.taid
      AND t.client_version >= '21004007'
      AND t.utdid IS NOT NULL
      AND d.recommend_day = '20170403'
    LIMIT 0, 10000

--use 3.6s repalce by straight join
 explain SELECT DISTINCT d.taid
    FROM tvassist_recommend_list_everyday_diverse d
    STRAIGHT_JOIN 
      tvassist_taid_all t on d.taid = t.taid 
    WHERE 
     t.client_version >= '21004007'
       AND d.recommend_day = '20170403'

      AND t.utdid IS NOT NULL  
    LIMIT 0, 10000

3
To nie daje prawie wystarczających informacji, aby dowiedzieć się, kiedy proste łączenia są odpowiednie.
Hannele,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.