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.
straight_join
.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.”
źródło
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_join
moż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
a
należy dołączyć wcześniejb
.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_join
lub 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.źródło
straight_join
ocenia lewą tabelę przed prawą. Więc jeśli chcesz przejść zA -> B -> C
w moim przykładzie, pierwszejoin
słowo kluczowe można zastąpićstraight_join
.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.
źródło
STRAIGHT_JOIN
, używając tej klauzuli, możesz kontrolowaćJOIN
kolejność: która tabela jest skanowana w pętli zewnętrznej, a która w pętli wewnętrznej.źródło
Powiem ci, dlaczego musiałem użyć STRAIGHT_JOIN:
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!
źródło
Z mojego krótkiego doświadczenia wynika, że jedną z sytuacji,
STRAIGHT_JOIN
któ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 filesort
ponieważźródło
tutaj optymalizator potrzebuje niewielkiej pomocy, mówiąc mu, aby uderzył jako
sales
pierwszy za pomocąźródło
Jeśli Twoje zapytania z końców
ORDER BY... LIMIT...
, to może być optymalna przeformułować zapytania oszukać optymalizator do wykonującLIMIT
przedJOIN
.(Ta odpowiedź nie dotyczy tylko pierwotnego pytania
STRAIGHT_JOIN
, ani nie ma zastosowania we wszystkich przypadkachSTRAIGHT_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:
INDEX(date, id)
.sales
pozwala uzyskać tylko 50 „bez względu na wszystko” bez ciągnięcia ich w tymczasowym stole.ORDER BY
należy je powtórzyć w zapytaniu zewnętrznym. (Optymalizator może znaleźć sposób na uniknięcie wykonywania innego sortowania).Sprzeciwiam się używaniu hitów, ponieważ „Nawet jeśli jest szybszy dzisiaj, jutro może nie być szybszy”.
źródło
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:
Nieprawidłowa kolejność działa przez około 65 sekund, podczas gdy używa się przebiegów straight_join w milisekundach
źródło
--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
źródło