Kann jemand erklären, warum es so langsam ist, zwei Ansichten in MySQL zu verbinden?

7

Hier ist eine Frage, die ich gestern gestellt habe - /programming/22180727/left-joining-two-views-is-slow .

Ich habe eine gute Antwort erhalten, die mir geholfen hat, aber ich verstehe nicht, warum der LEFT JOIN so viel langsamer ist als die Suche. Der LEFT JOIN betrug 16 Sekunden - und ich bin mir ziemlich sicher, dass meine Tabellen zu mindestens 90% optimiert sind - und bei der Suche sind es nur 0,14 Sekunden. Wenn ich JOIN-Tabellen verlassen habe, ist es nicht so langsam. Warum also Ansichten?

LOSTinDB
quelle
Wie werden die Tabellen unter den Ansichten indiziert? Haben die Spalten, mit denen Sie sich links verbinden, nützliche Indizes für diesen Join?
RLF
@RLF - Es gibt zwei Felder uid und trid, die für alle Joins verwendet werden, und beide Felder werden für alle ihre Tabellen indiziert. Ich kann Tabellen aus den Ansichten erstellen und LEFT JOIN ist wie 1,5 Sekunden.
LOSTinDB

Antworten:

10

Gemäß der MySQL-Dokumentation zu Ansichten

Ansichten (einschließlich aktualisierbarer Ansichten) sind in MySQL Server 5.6 verfügbar. Ansichten sind gespeicherte Abfragen, die beim Aufrufen eine Ergebnismenge erzeugen. Eine Ansicht fungiert als virtuelle Tabelle.

Das erste, was an einer Ansicht erkannt werden muss, ist, dass sie eine Ergebnismenge erzeugt. Die Ergebnismenge, die aus der in der Ansicht aufgerufenen Abfrage hervorgeht, ist eine virtuelle Tabelle, da sie bei Bedarf erstellt wird. Es gibt keine DDL, die Sie anschließend aufrufen können, um die Ergebnismenge sofort zu indizieren. Die Ergebnismenge ist in jeder Hinsicht eine Tabelle ohne Indizes. Tatsächlich handelt es sich bei dem von Ihnen ausgeführten LEFT JOIN im Grunde genommen um ein kartesisches Produkt mit einigen Filtern.

Um Ihnen einen genaueren Überblick über die Verbindung zweier Ansichten zu geben, verweise ich auf einen Beitrag, den ich im letzten Jahr verfasst habe, in dem die internen Mechanismen erläutert werden, mit denen MySQL JOINs und WHEREs bewertet ( Gibt es einen Ausführungsunterschied zwischen einer JOIN-Bedingung und einer WHERE-Bedingung? ). Ich werde Ihnen den Mechanismus zeigen, wie er in Understanding MySQL Internals (Seite 172) veröffentlicht wurde:

  • Bestimmen Sie, mit welchen Schlüsseln die Datensätze aus Tabellen abgerufen werden können, und wählen Sie für jede Tabelle den besten aus.
  • Entscheiden Sie für jede Tabelle, ob ein Tabellenscan besser ist als das Lesen eines Schlüssels. Wenn viele Datensätze mit dem Schlüsselwert übereinstimmen, werden die Vorteile des Schlüssels verringert und der Tabellenscan wird schneller.
  • Bestimmen Sie die Reihenfolge, in der Tabellen verknüpft werden sollen, wenn mehr als eine Tabelle in der Abfrage vorhanden ist.
  • Schreiben Sie die WHERE-Klauseln neu, um toten Code zu eliminieren, unnötige Berechnungen zu reduzieren und die Einschränkungen nach Möglichkeit zu ändern, um den Weg für die Verwendung von Schlüsseln freizumachen.
  • Entfernen Sie nicht verwendete Tabellen aus dem Join.
  • Bestimmen Sie, ob Schlüssel für ORDER BYund verwendet werden können GROUP BY.
  • Versuchen Sie, Unterabfragen zu vereinfachen und festzustellen, inwieweit ihre Ergebnisse zwischengespeichert werden können.
  • Ansichten zusammenführen (Ansichtsreferenz als Makro erweitern)

OK, es scheint, als sollten Indizes verwendet werden. Schauen Sie jedoch genauer hin. Wenn Sie das Wort Viewdurch ersetzen Table, schauen Sie, was mit der Ausführung des Mechanismus passiert:

MECHANISMUS GEÄNDERT

  • Bestimmen Sie, von welchen Schlüsseln die Datensätze abgerufen werden können views, und wählen Sie jeweils den besten aus view.
  • viewEntscheiden Sie für jeden , ob ein viewScan besser ist als das Lesen auf einem Schlüssel. Wenn viele Datensätze mit dem Schlüsselwert übereinstimmen, werden die Vorteile des Schlüssels verringert und der viewScanvorgang wird schneller.
  • Bestimmen Sie die Reihenfolge, in der verbunden werden viewssoll, wenn mehr als eine viewsin der Abfrage vorhanden ist.
  • Schreiben Sie die WHERE-Klauseln neu, um toten Code zu eliminieren, unnötige Berechnungen zu reduzieren und die Einschränkungen nach Möglichkeit zu ändern, um den Weg für die Verwendung von Schlüsseln freizumachen.
  • Beseitigen Sie nicht verwendete Elemente viewsaus dem Join.
  • Bestimmen Sie, ob Schlüssel für ORDER BYund verwendet werden können GROUP BY.
  • Versuchen Sie, Unterabfragen zu vereinfachen und festzustellen, inwieweit ihre Ergebnisse zwischengespeichert werden können.
  • Ansichten zusammenführen (Ansichtsreferenz als Makro erweitern)

Jede Tabelle (Ansicht) hat keinen Index. Daher wird die Arbeit mit virtuellen Tabellen, temporären Tabellen oder Tabellen ohne Indizes beim Ausführen eines JOIN wirklich undeutlich. Die verwendeten Schlüssel sind nur für JOIN-Operationen gedacht, nicht so sehr für das schnellere Nachschlagen.

Stellen Sie sich Ihre Anfrage so vor, als würden Sie zwei Telefonbücher abholen, die Gelben Seiten 2014 und die Gelben Seiten 2013. Jedes Buch mit den Gelben Seiten enthält die Weißen Seiten für Telefonnummern für Privathaushalte.

  • Ende 2012 wurde eine Datenbanktabelle verwendet, um die Gelben Seiten 2013 zu generieren.
  • Im Jahr 2013
    • Die Leute haben die Telefonnummern geändert
    • Die Leute erhielten neue Telefonnummern
    • Die Leute ließen Telefonnummern fallen und wechselten zum Handy
  • Ende 2013 wurde eine Datenbanktabelle verwendet, um die Gelben Seiten 2014 zu generieren.

Offensichtlich gibt es Unterschiede zwischen den beiden Telefonbüchern. Das Zusammenführen von Datenbanktabellen, um die Unterschiede zwischen 2013 und 2014 herauszufinden, sollte kein Problem darstellen.

Stellen Sie sich vor, Sie führen die beiden Telefonbücher von Hand zusammen, um Unterschiede zu lokalisieren. Klingt verrückt, nicht wahr? Ungeachtet dessen ist es genau das, was Sie von mysqld verlangen, wenn Sie zwei Ansichten verbinden. Denken Sie daran, dass Sie keine echten Tabellen verbinden und es keine Indizes gibt, aus denen Sie Huckepack nehmen können.

Lassen Sie uns nun auf die eigentliche Abfrage zurückblicken.

SELECT DISTINCT
viewA.TRID, 
viewA.hits,
viewA.department,
viewA.admin,
viewA.publisher,
viewA.employee,
viewA.logincount,
viewA.registrationdate,
viewA.firstlogin,
viewA.lastlogin,
viewA.`month`,
viewA.`year`,
viewA.businesscategory,
viewA.mail,
viewA.givenname,
viewA.sn,
viewA.departmentnumber,
viewA.sa_title,
viewA.title,
viewA.supemail,
viewA.regionname
FROM
viewA
LEFT JOIN viewB ON viewA.TRID = viewB.TRID
WHERE viewB.TRID IS NULL 

Sie verwenden eine virtuelle Tabelle (Tabelle ohne Indizes), viewA, und verbinden sie mit einer anderen virtuellen Tabelle, viewB. Die temporäre Tabelle, die intermittierend generiert wird, ist so groß wie viewA. Anschließend führen Sie eine interne Sortierung für die große temporäre Tabelle aus, um sie zu unterscheiden.

EPILOG

Angesichts der internen Mechanismen zur Auswertung von JOINs sollte Ihre ursprüngliche Abfrage (LEFT JOIN aus zwei Ansichten) Laufzeiten in Größenordnungen erhalten, da die Ergebnismenge einer Ansicht vorübergehend und indexlos ist. Gleichzeitig sollte die Antwort , die Sie von StackOverflow erhalten haben , angesichts des gleichen JOIN-Algorithmus, den ich gerade beschrieben habe, gut funktionieren.

Ich hoffe, die blutigen Details, die ich gerade gepostet habe, beantworten Ihre Frage, warum.

RolandoMySQLDBA
quelle
Ich weiß, dass Sie die "Ansichten (einschließlich aktualisierbarer Ansichten) sind in MySQL Server 5.6 verfügbar" aus der offiziellen Dokumentation kopiert haben, aber es klingt so, als ob Ansichten in 5.6 eingeführt wurden, während sie ab Version 5.0 verfügbar sind.
Ypercubeᵀᴹ
Ich habe gelesen, dass Ansichten die ursprünglichen Indizes aus den Tabellen verwenden, aber die Erstellung eines neuen Index für ihre Felder nicht zulassen. Wenn ich die Ansicht in einer Select * From-Ansicht abfrage, in der TRID = 10 ist, wird der ursprüngliche Index verwendet, nein?
Bergkamp
1

EXPLAIN EXTENDED [select query]und zeigt dann SHOW WARNINGSdie umgeschriebene Form der Ansicht an. Von hier aus ist es einfacher, Leistungsmerkmale zu analysieren.

Abfragen zur Sichtprüfung sind im Allgemeinen nicht einfach zu optimieren.

Morgan Tocker
quelle
Ich verstehe diese beiden Taktiken, aber beantwortet das die Frage?
LOSTinDB
1
Es kann :) Um es zu paraphrasieren: Jede Antwort ist Spekulation ohne diese Informationen.
Morgan Tocker
-2

Die Antwort hat mit der Methode zur Durchführung jeder dieser Operationen zu tun.

Da Ansichten von Natur aus nicht indiziert sind, dauern JOIN-Vorgänge mit Feldern aus Ansichten länger als JOIN-Vorgänge mit Tabellen, da der Scan keinen Index verwenden kann.

In diesem Fall begrenzt die Suche auch die Anzahl der Datensätze, die bei der Verarbeitung zurückgegeben werden müssen. Es werden nur Datensätze aus einer Ansicht abgerufen, die in der anderen nicht vorhanden sind. Der JOIN zieht alle Datensätze und prüft dann, ob in beiden Datensätzen vorhanden sind.

Thomas Cleberg
quelle
1
Bedeutet dies, dass MySQL beim Abfragen einer Ansicht keine Indizes für die zugrunde liegende Tabelle verwenden kann?
a_horse_with_no_name
1
@a_horse_with_no_name nein, das tut es nicht. Diese Antwort ist in dieser Hinsicht falsch. Wenn der MERGEAlgorithmus zum Verarbeiten der Ansicht verwendet werden kann, können und werden die Indizes für die zugrunde liegenden Tabellen verwendet. Nur wenn die Ansichtsdefinition den TEMPTABLEAlgorithmus explizit verwendet oder die Ansicht Funktionen enthält, die implizit eine temporäre Tabelle erfordern, werden die Ansichtsergebnisse in einer temporären Tabelle materialisiert. dev.mysql.com/doc/refman/5.6/en/view-algorithms.html
Michael - sqlbot