Deckungsindex trotz fehlender Spalte verwendet

8

Ich habe die folgende Abfrage mit MariaDB 10 / InnoDB:

SELECT id, sender_id, receiver_id, thread_id, date_created, content 
FROM user_message 
WHERE thread_id = 12345 
  AND placeholder = FALSE
ORDER BY date_created DESC 
LIMIT 20

Diese Abfrage ruft Nachrichten gemäß den angegebenen Bedingungen ab und sortiert sie nach dem Erstellungsdatum.

Ich habe einen Deckungsindex über (thread_id, date_created).

Beim Ausführen von EXPLAIN wird der richtige Index verwendet und ich erhalte die Ausgabe "Using where", obwohl die Abfrage eine Spalte in der Mitte der Anweisung verwendet, die nicht im Index enthalten ist. Ich kann einen beliebigen Wert für "placeholder = x" verwenden und das Ergebnis ist das gleiche.

Wenn ich die Sortierung so ändere, dass eine andere Spalte verwendet wird, zeigt EXPLAIN korrekt "Using where. Using filesort" an.

Ich habe einen kopfkratzenden Moment. Könnte jemand bitte Licht ins Dunkel bringen? Was ich erwarten würde, ist, dass ein zusätzlicher Dateisort benötigt wird, da der Abdeckungsindex aufgrund der zusätzlichen Spalte nicht vollständig verwendet werden konnte.

Tom
quelle

Antworten:

8

Fall A
Abfrage:

WHERE thread_id = 12345 
  AND placeholder = FALSE
ORDER BY some_column DESC 
LIMIT 20

Index:

(thread_id, date_created)

Planen:

Index is used
Using Where
Using filesort

Kein Problem, oder? Wenn der Index verwendet wird (um teilweise mit der WHEREBedingung übereinzustimmen), benötigen wir noch eine Sortieroperation, um die Ergebnisse nach zu sortieren some_column(was nicht im Index enthalten ist). Wir benötigen außerdem eine zusätzliche Prüfung (Verwenden von Where), um nur die Zeilen beizubehalten, die auch der zweiten Bedingung entsprechen. OK.


Fall B (die Frage)
Abfrage:

WHERE thread_id = 12345 
  AND placeholder = FALSE
ORDER BY date_created DESC 
LIMIT 20

Index:

(thread_id, date_created)

Planen:

Index is used
Using Where
-- no "Using filesort"

Also, warum braucht es nicht eine Art hier ? Weil der Index ausreicht, um nach Abfrage zu sortieren. Es gibt natürlich das zusätzliche Problem der zusätzlichen Bedingung ( AND placeholder = FALSE), die nicht vom Index abgedeckt wird.

OK, aber wir brauchen hier nicht wirklich eine Sorte. Der Index kann uns Ergebnisse liefern, die der ersten Bedingung ( WHERE thread_id = 12345) entsprechen und in der gewünschten Reihenfolge für die Ausgabe vorliegen . Die einzige zusätzliche Überprüfung, die wir benötigen - und was der Plan tut - besteht darin, die Zeilen aus der Tabelle in der vom Index angegebenen Reihenfolge abzurufen und diese zweite Bedingung zu überprüfen, bis wir 20 Übereinstimmungen erhalten. Das ist es, was ** Using Where "" bedeutet.

Wir können die 20 Übereinstimmungen in den ersten 20 Reihen (also wirklich gut und schnell) oder in den ersten 100 (wahrscheinlich immer noch schnell genug) oder in den ersten 1000000 (wahrscheinlich sehr, sehr langsam) bekommen, oder wir können nur 19 Übereinstimmungen von der bekommen Tabelle auch nach dem Lesen aller übereinstimmenden Zeilen aus dem Index (wirklich sehr langsam auf einer großen Tabelle). Alles hängt von der Verteilung der Daten ab.


Fall C (noch besserer Plan)
Abfrage:

WHERE thread_id = 12345 
  AND placeholder = FALSE
ORDER BY date_created DESC 
LIMIT 20

Index:

(placeholder, thread_id, date_created)

Planen:

Index is used
-- no "Using Where"
-- no "Using filesort"

Jetzt entspricht unser Index sowohl den Bedingungen als auch der Reihenfolge von. Der Plan ist ziemlich einfach: Holen Sie sich die ersten * 20 Übereinstimmungen aus dem Index und lesen Sie die entsprechenden Zeilen aus der Tabelle. Keine zusätzliche Prüfung (kein "Using Where") und keine Sortierung (kein "Using filesort") erforderlich.

first *: die ersten 20, wenn der Index vom Ende rückwärts gelesen wird (wie wir es getan haben ORDER BY .. DESC), aber das ist kein Problem. B-Tree-Indizes können mit nahezu gleicher Leistung vorwärts und rückwärts gelesen werden.

ypercubeᵀᴹ
quelle
7
  • Mit Index weist auf einen „ Covering Index“ - Alle Spalten überall in dem SELECTist überall in dem einen Index. Sie haben also keinen "Deckungsindex". Und es ist nicht praktisch, einen Deckungsindex für Ihre Abfrage zu erstellen (zu viele Spalten erwähnt).
  • Verwenden wo - meistens Lärm.
  • Dateisortierung verwenden - Die Abfrage benötigt eine Sortierung, befindet sich jedoch möglicherweise im RAM oder in einer temporären Tabelle. Und es kann mehrere Arten geben (z. B. GROUP BY x ORDER BY b)
  • Beides ermöglicht es, nur 20 Zeilen zu betrachten. Für jeden anderen Index müssen mehr Zeilen berührt werden, möglicherweise die gesamte Tabelle:

    INDEX(thread_id, placeholder, date_created)
    INDEX(placeholder, thread_id, date_created)
  • Nein, die Kardinalität der Komponenten eines zusammengesetzten Index spielt bei der Reihenfolge der Spalten im Index keine Rolle.

In meinem Kochbuch wird erklärt, wie man den optimalen Index ableitet, wenn a SELECT.

Rick James
quelle
Danke für das Kochbuch - sehr schönes Blatt.
Tom