Wann wird STRAIGHT_JOIN mit MySQL verwendet?

86

Ich hatte gerade eine ziemlich komplexe Abfrage, mit der ich gearbeitet habe, und die Ausführung dauerte 8 Sekunden. EXPLAIN zeigte eine seltsame Tabellenreihenfolge und meine Indizes wurden nicht alle verwendet, selbst mit dem FORCE INDEX-Hinweis. Ich bin auf das Join-Schlüsselwort STRAIGHT_JOIN gestoßen und habe begonnen, einige meiner INNER JOIN-Schlüsselwörter durch dieses zu ersetzen. Ich bemerkte eine erhebliche Geschwindigkeitsverbesserung. Schließlich habe ich gerade alle meine INNER JOIN-Schlüsselwörter für diese Abfrage durch STRAIGHT_JOIN ersetzt und sie wird jetzt in 0,01 Sekunden ausgeführt.

Meine Frage ist, wann Sie STRAIGHT_JOIN verwenden und wann Sie INNER JOIN verwenden? Gibt es einen Grund, STRAIGHT_JOIN nicht zu verwenden, wenn Sie gute Abfragen schreiben?

Greg
quelle

Antworten:

73

Ich würde STRAIGHT_JOIN nicht ohne guten Grund empfehlen. Meine eigene Erfahrung ist, dass das MySQL-Abfrageoptimierungsprogramm öfter einen schlechten Abfrageplan auswählt, als ich möchte, aber nicht oft genug, dass Sie ihn im Allgemeinen einfach umgehen sollten, was Sie tun würden, wenn Sie immer STRAIGHT_JOIN verwenden würden.

Meine Empfehlung ist, alle Anfragen als reguläre JOINs zu hinterlassen. Wenn Sie feststellen, dass eine Abfrage einen suboptimalen Abfrageplan verwendet, würde ich empfehlen, zunächst zu versuchen, die Abfrage ein wenig umzuschreiben oder neu zu strukturieren, um festzustellen, ob das Optimierungsprogramm dann einen besseren Abfrageplan auswählt. Stellen Sie außerdem zumindest für innodb sicher, dass Ihre Indexstatistiken nicht nur veraltet sind ( ANALYZE TABLE ). Dies kann dazu führen, dass der Optimierer einen schlechten Abfrageplan auswählt. Optimierungshinweise sollten im Allgemeinen Ihr letzter Ausweg sein.

Ein weiterer Grund, keine Abfragehinweise zu verwenden, besteht darin, dass sich Ihre Datenverteilung im Laufe der Zeit oder Ihre Indexselektivität usw. ändern kann, wenn Ihre Tabelle wächst. Ihre jetzt optimalen Abfragehinweise können im Laufe der Zeit suboptimal werden. Das Optimierungsprogramm kann den Abfrageplan jedoch aufgrund Ihrer jetzt veralteten Hinweise nicht anpassen. Sie bleiben flexibler, wenn Sie dem Optimierer erlauben, die Entscheidungen zu treffen.

Nathan
quelle
56
Diese Antwort erklärt nicht wirklich, wann sie verwendet werden soll straight_join .
Pacerier
23

Aus der MySQL JOIN-Referenz :

"STRAIGHT_JOIN ähnelt JOIN, außer dass die linke Tabelle immer vor der rechten Tabelle gelesen wird. Dies kann für die (wenigen) Fälle verwendet werden, in denen der Join-Optimierer die Tabellen in die falsche Reihenfolge bringt."

jjclarkson
quelle
24
Danke, aber ich habe bereits das MySQL-Handbuch gelesen. Ich hoffe auf weitere Erklärungen.
Greg
19

Hier ist ein Szenario, das erst kürzlich bei der Arbeit aufgetaucht ist.

Betrachten Sie drei Tabellen, A, B, C.

A hat 3.000 Zeilen; B hat 300.000.000 Zeilen; und C hat 2.000 Zeilen.

Fremdschlüssel sind definiert: B (a_id), B (c_id).

Angenommen, Sie hatten eine Abfrage, die folgendermaßen aussieht:

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

Nach meiner Erfahrung kann MySQL in diesem Fall C -> B -> A wählen. C ist kleiner als A und B ist enorm und sie sind alle gleichwertig.

Das Problem ist, dass MySQL nicht unbedingt die Größe des Schnittpunkts zwischen (C.id und B.c_id) und (A.id und B.a_id) berücksichtigt. Wenn die Verknüpfung zwischen B und C genauso viele Zeilen wie B zurückgibt, ist dies eine sehr schlechte Wahl. Wenn das Beginnen mit A B auf so viele Zeilen wie A heruntergefiltert hätte, wäre es eine viel bessere Wahl gewesen. straight_joinkönnte verwendet werden, um diese Reihenfolge wie folgt zu erzwingen:

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

Jetzt amuss vorher noch mitgemacht werden b.

Im Allgemeinen möchten Sie Ihre Verknüpfungen in einer Reihenfolge ausführen, in der die Anzahl der Zeilen in der resultierenden Menge minimiert wird. Es ist daher ideal, mit einer kleinen Tabelle zu beginnen und so zu verbinden, dass die resultierende Verknüpfung ebenfalls klein ist. Die Dinge werden birnenförmig, wenn man mit einem kleinen Tisch beginnt und ihn mit einem größeren Tisch verbindet, der genauso groß ist wie der große Tisch.

Es ist jedoch abhängig von den Statistiken. Wenn sich die Datenverteilung ändert, kann sich die Berechnung ändern. Dies hängt auch von den Implementierungsdetails des Join-Mechanismus ab.

Die schlimmsten Fälle, die ich für MySQL gesehen habe, die alles andere als erforderlich sind, straight_joinoder aggressive Indexhinweise, sind Abfragen, die über viele Daten in einer strengen Sortierreihenfolge mit Lichtfilterung paginieren. MySQL bevorzugt nachdrücklich die Verwendung von Indizes für Filter und Verknüpfungen über Sortierungen. Dies ist sinnvoll, da die meisten Benutzer nicht versuchen, die gesamte Datenbank zu sortieren, sondern nur eine begrenzte Teilmenge von Zeilen haben, die auf die Abfrage reagieren. Das Sortieren einer begrenzten Teilmenge ist viel schneller als das Filtern der gesamten Tabelle, unabhängig davon, ob sie sortiert ist oder nicht nicht. In diesem Fall wollte ich die direkte Verknüpfung unmittelbar nach der Tabelle mit der indizierten Spalte setzen, die ich nach festen Dingen sortieren wollte.

Barry Kelly
quelle
Wie würden Sie Straight Join verwenden, um das Problem zu beheben?
Hannele
@Hannele straight_joinwertet die linke Tabelle vor der rechten aus. Wenn Sie also von A -> B -> Cmeinem Beispiel ausgehen möchten , könnte das erste joinSchlüsselwort durch ersetzt werden straight_join.
Barry Kelly
Ah ordentlich. Es wäre nützlich, dies als Beispiel in Ihre Antwort aufzunehmen :)
Hannele
18

MySQL ist nicht unbedingt gut darin, die Verknüpfungsreihenfolge in komplexen Abfragen auszuwählen. Durch Angabe einer komplexen Abfrage als Straight_Join führt die Abfrage die Verknüpfungen in der angegebenen Reihenfolge aus. Indem Sie die Tabelle zuerst auf den kleinsten gemeinsamen Nenner setzen und gerade_join angeben, können Sie die Abfrageleistung verbessern.

IAdapter
quelle
11

STRAIGHT_JOINMit dieser Klausel können Sie die JOINReihenfolge steuern : Welche Tabelle wird in der äußeren Schleife gescannt und welche in der inneren Schleife.

Mitendra
quelle
Was sind äußere und innere Schleife?
Istiaque Ahmed
@IstiaqueAhmed-Tabellen werden durch verschachtelte Schleifen verbunden (nehmen Sie die erste Zeile aus Tabelle A und werfen Sie die Tabelle B, dann nehmen Sie die zweite Zeile ... und so weiter. Hier befindet sich Tabelle A in der äußeren Schleife)
Accountant م
6

Ich werde Ihnen sagen, warum ich STRAIGHT_JOIN verwenden musste:

  • Ich hatte ein Leistungsproblem mit einer Abfrage.
  • Durch die Vereinfachung der Abfrage war die Abfrage plötzlich effizienter
  • Als ich versuchte herauszufinden, welcher Teil das Problem verursachte, konnte ich es einfach nicht. (2 linke Verbindungen waren langsam und jede war unabhängig schnell)
  • Ich habe dann die EXPLAIN sowohl mit langsamer als auch mit schneller Abfrage ausgeführt (addind eine der linken Verknüpfungen)
  • Überraschenderweise hat MySQL die JOIN-Ordnungen zwischen den beiden Abfragen vollständig geändert.

Daher habe ich einen der Joins gezwungen, Straight_Join zu sein, um den vorherigen Join zu erzwingen, der zuerst gelesen werden soll. Dies verhinderte, dass MySQL die Ausführungsreihenfolge änderte, und wirkte wie ein Zauber!

Nicolas Thery
quelle
2

Nach meiner kurzen Erfahrung ist eine der Situationen, STRAIGHT_JOINdie meine Abfrage von 30 Sekunden auf 100 Millisekunden reduziert hat, dass die erste Tabelle im Ausführungsplan nicht die Tabelle war, die die Reihenfolge nach Spalten hat

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

Wenn der Optimierer stores zuerst trifft , wird dies verursacht, Using index; Using temporary; Using filesortweil

Wenn ORDER BY oder GROUP BY Spalten aus anderen Tabellen als der ersten Tabelle in der Join-Warteschlange enthält, wird eine temporäre Tabelle erstellt.

Quelle

Hier braucht der Optimierer ein wenig Hilfe, indem er ihm sagt, er solle saleszuerst mit drücken

sales STRAIGHT_JOIN stores
Buchhalter م
quelle
1
(Ich verschönerte Ihre Antwort.)
Rick James
2

Wenn Ihre Abfrage mit endet ORDER BY... LIMIT..., ist es möglicherweise optimal, die Abfrage neu zu formulieren, um den Optimierer dazu zu bringen, das LIMIT vor dem zu tun JOIN.

(Diese Antwort gilt nicht nur für die ursprüngliche Frage zu STRAIGHT_JOIN, noch gilt sie für alle Fälle von STRAIGHT_JOIN.)

Beginnend mit dem Beispiel von @Accountant م sollte dies in den meisten Situationen schneller laufen. (Und es werden keine Hinweise benötigt.)

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;

Anmerkungen:

  • Zunächst werden 50 IDs abgerufen. Dies wird besonders schnell mit INDEX(date, id).
  • Mit dem Join Back to erhalten salesSie nur 50 "Whatevers", ohne sie in einer temporären Tabelle herumzuschleppen.
  • Da eine Unterabfrage per Definition ungeordnet ist, ORDER BYmuss sie in der äußeren Abfrage wiederholt werden. (Der Optimierer findet möglicherweise einen Weg, um zu vermeiden, dass tatsächlich eine andere Sortierung durchgeführt wird.)
  • Ja, es ist chaotischer. Aber es ist normalerweise schneller.

Ich bin gegen die Verwendung von Treffern, weil "Auch wenn es heute schneller ist, kann es morgen nicht schneller sein."

Rick James
quelle
0

Ich weiß, dass es ein bisschen alt ist, aber hier ist ein Szenario: Ich habe ein Batch-Skript erstellt, um eine bestimmte Tabelle zu füllen. Irgendwann lief die Abfrage sehr langsam. Es scheint, dass die Verknüpfungsreihenfolge in bestimmten Datensätzen falsch war:

  • In der richtigen Reihenfolge

Geben Sie hier die Bildbeschreibung ein

  • Wenn Sie die ID um 1 erhöhen, wird die Reihenfolge durcheinander gebracht. Beachten Sie das Feld 'Extra'

Geben Sie hier die Bildbeschreibung ein

  • Die Verwendung von hetero_join behebt das Problem

Geben Sie hier die Bildbeschreibung ein

Eine falsche Reihenfolge wird ca. 65 Sekunden lang ausgeführt, während bei Verwendung von Straight_Join in Millisekunden ausgeführt wird

Rai
quelle
-4
--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
lhs295988029
quelle
2
Dies gibt Ihnen nicht annähernd genug Informationen, um herauszufinden, wann gerade Verknüpfungen angemessen sind.
Hannele