2016-07-21 14 views
-1

Ich habe folgende Self-Join-Abfrage:MySql - Self-Join - Full Table Scan (Kann nicht Index Scan)

SELECT A.id 
FROM mytbl  AS A 
LEFT JOIN mytbl AS B 
ON (A.lft BETWEEN B.lft AND B.rgt) 

Die Abfrage ist ziemlich langsam, und nach bei dem Ausführungsplan der Suche erscheint die Ursache zu sein eine vollständige Tabelle Scan in der JOIN. Die Tabelle hat nur 500 Zeilen, und da ich vermute, dass dies das Problem ist, habe ich sie auf 100.000 Zeilen erhöht, um zu sehen, ob sie die Auswahl des Optimierers beeinflusst hat. Es tat nicht, mit 100k Zeilen war es immer noch eine vollständige Tabelle scannen.

Mein nächster Schritt war, um zu versuchen und Kraft-Indizes mit der folgenden Abfrage, aber die gleiche Situation ergibt sich, einen vollständigen Tabellenscan:

SELECT A.id 
FROM categories_nested_set  AS A 
LEFT JOIN categories_nested_set AS B 
FORCE INDEX (idx_lft, idx_rgt) 
ON (A.lft BETWEEN B.lft AND B.rgt) 

Execution plan for full table scan query :/

Alle Spalten (id, LFT, RGT) sind Ganzzahlen, alle sind indiziert.

Warum macht MySql hier einen vollständigen Tabellenscan?

Wie kann ich meine Abfrage ändern, um Indizes anstelle eines vollständigen Tabellenscan zu verwenden?

CREATE TABLE mytbl (lft int(11) NOT NULL DEFAULT '0', 
rgt int(11) DEFAULT NULL, 
id int(11) DEFAULT NULL, 
category varchar(128) DEFAULT NULL, 
    PRIMARY KEY (lft), 
    UNIQUE KEY id (id), 
    UNIQUE KEY rgt (rgt), 
    KEY idx_lft (lft), 
    KEY idx_rgt (rgt)) ENGINE=InnoDB DEFAULT CHARSET=utf8 

Dank

+0

teilen sich die Ergebnisse der 'show create table xyz' für jede relevante xyz – Drew

+0

Ergebnisse unter: ' TABLE mytbl CREATE ( lft int (11) NOT NULL DEFAULT '0', RGT int (11) NULL DEFAULT, ID int (11) NULL DEFAULT, Kategorie VARCHAR (128) DEFAULT NULL, PRIMARY KEY (LFT), UNIQUE Schlüssel-ID (id), EINZIGARTIGER SCHLÜSSEL rgt (rgt), SCHLÜSSEL idx_l ft (lft), KEY idx_rgt (rgt) ) ENGINE = InnoDB DEFAULT CHARSET = utf8' – mils

+0

Ein 'PRIMARY KEY' ist ein' UNIQUE' Schlüssel ist ein 'KEY'. Also sind die zwei KEYs redundant und sollten entfernt werden. –

Antwort

-1

Die folgende SO ist Frage der Lösung kritisch, da es auf der Kombination von Adjazenzlisten und Indizes sehr wenig Informationen:

MySQL & nested set: slow JOIN (not using index)

Es scheint eine grundlegende Vergleichsbedingung, dass das Hinzufügen löst die Verwendung von ein Index, wie folgt:

SELECT A.id 
FROM mytbl  AS A 
LEFT JOIN mytbl AS B ON (A.lft BETWEEN B.lft AND B.rgt) 
-- THE FOLLOWING DUMMY CONDITIONS TRIGGER INDEX 
WHERE A.lft > 0 
AND B.lft > 0 
AND B.rgt > 0 

Und keine Tabellenscans mehr.

EDIT: Vergleich der EXPLAIN-Funktion zwischen festen und nicht fixierte Version der Abfrage: EXPLAIN function results, top is fixed, bottom is not

+0

Bitte testen Sie die folgenden mit und ohne diese 'reparieren': 'FLUSH STATUS; WÄHLEN ...; SHOW SESSION STATUS LIKE 'Handler%'; 'Wenn die Anzahl zwischen ihnen gleich ist, dann gibt es immer noch einen 'Full Scan', aber vielleicht im Index anstatt in der Tabelle. –

+0

Dank Rick, Zahlen unter (Null Zahlen ausgenommen): mit FIX 'Handler_commit', '1' 'Handler_external_lock', '4' 'Handler_read_first', '2' 'Handler_read_key', '2' 'Handler_read_next', '646' OHNE FIX 'Handler_commit', '1' 'Handler_external_lock', '4' 'Handler_read_first', '72' 'Handler_read_key', '72' 'Handler_read_rnd_next',‘ 37941 ' – mils

+0

Das überzeugt mich, dass die Lösung geholfen hat. –

2

Sie haben viel von Indizes, von denen einige redundant sind. Lasst uns damit beginnen, einige von ihnen aufzuklären. Zu viele Indizes verlangsamen Einfügungen und Aktualisierungen.

PRIMARY KEY (lft), 
KEY idx_lft (lft), 

Da Sie bereits einen Primärschlüssel auf LFT definiert haben, ist es nicht erforderlich, was so überhaupt für einen anderen Index auf LFT. In ähnlicher Weise ist bei einem eindeutigen Index für rgt der zweite Index nicht erforderlich, der nachstehend aufgeführt ist.

UNIQUE KEY rgt (rgt), 
KEY idx_rgt (rgt) 

Schauen wir uns jetzt Ihre Anfrage an.

SELECT A.id 
FROM mytbl  AS A 
LEFT JOIN mytbl AS B 
ON (A.lft BETWEEN B.lft AND B.rgt) 

Dies ist sehr unwahrscheinlich, eine Abfrage, die in freier Wildbahn auftreten wird. Mit 500 Zeilen kann diese Abfrage sogar 5000 Zeilen erzeugen? Brauchen Sie wirklich den gesamten Schlüssel, der auf einmal erstellt wird? Der Grund, warum diese Abfrage langsam ist, liegt daran, dass mysql nur optimize range comparisions für Konstanten verwenden kann. Es ist wahrscheinlicher, dass Ihr tatsächlich Abfrage etwas wie folgt aussehen:

SELECT B.* 
FROM mytbl  AS A 
LEFT JOIN mytbl AS B 
ON (A.lft BETWEEN B.lft AND B.rgt) 
WHERE a.id = N; 

Wo Sie den Knoten für eine bestimmte ID erstellen. Dies wird Indizes verwenden und wird sehr schnell sein. Was ist der Sinn der Optimierung für eine Abfrage, die Sie nicht viel verwenden werden, wenn überhaupt?

+0

danke für die Antwort, ich habe meine Frage mit einigen zusätzlichen Informationen aktualisiert. Grundsätzlich kann ich es nicht mit einer WHERE-Klausel tun, da es Teil eines größeren JOIN ist. Ich habe es aus Gründen der Einfachheit für diese Frage weggelassen. Und im realen Anwendungsfall mit dem JOIN wird kein Index verwendet. Im Szenario eines größeren JOIN, ist dies eine Konstante für den Bereich Vergleiche oder muss es eine benutzerdefinierte Konstante sein? Wie kann ich Tabellenscans in diesem Szenario vermeiden? Danke – mils

+0

Das verschiebt die Torpfosten und verschiebt es eine lange Distanz – e4c5

+0

Ich habe einige Leistungstests gemacht, die Größe dieser Mytbl macht den größten Einfluss auf das Laden meiner Daten in ein anderes System. Wenn ich 10k Zeilen statt 500 habe, verschlechtert sich die Leistung um 6000%. Im Moment dauert es 4 Stunden und es kann nur schlimmer werden. Ich würde es wirklich schätzen zu wissen, wie MySql einen Index für eine Bereichsabfrage verwendet. – mils