Warum verwendet diese Abfrage meinen nicht gruppierten Index nicht und wie kann ich ihn erstellen?

12

Als Antwort auf diese Frage zur Steigerung der Abfrageleistung möchte ich wissen, ob es eine Möglichkeit gibt, meinen Index standardmäßig zu verwenden.

Diese Abfrage dauert ungefähr 2,5 Sekunden:

SELECT TOP 1000 * FROM [CIA_WIZ].[dbo].[Heartbeats]
WHERE [DateEntered] BETWEEN '2011-08-30' and '2011-08-31';

Dieser läuft in ca. 33ms:

SELECT TOP 1000 * FROM [CIA_WIZ].[dbo].[Heartbeats]
WHERE [DateEntered] BETWEEN '2011-08-30' and '2011-08-31' 
ORDER BY [DateEntered], [DeviceID];

Es gibt einen Clustered-Index für das Feld [ID] (pk) und einen Nicht-Clustered-Index für [DateEntered], [DeviceID]. Die erste Abfrage verwendet den gruppierten Index, die zweite Abfrage verwendet meinen nicht gruppierten Index. Meine Frage besteht aus zwei Teilen:

  • Warum verwendet der Server, da beide Abfragen eine WHERE-Klausel im Feld [DateEntered] enthalten, den Clustered-Index im ersten, aber nicht im zweiten?
  • Wie kann ich festlegen, dass der nicht gruppierte Index auch ohne orderby standardmäßig für diese Abfrage verwendet wird? (Oder warum sollte ich dieses Verhalten nicht wollen?)
Nate
quelle
DateEntered ist eine DateTime, in diesem Fall verwende ich den Datumsteil, frage aber manchmal sowohl nach Datum als auch nach Uhrzeit zusammen.
Nate

Antworten:

9

Bei der ersten Abfrage wird eine Tabellensuche basierend auf dem Schwellenwert durchgeführt, den ich zuvor erläutert habe: Ist es möglich, die Abfrageleistung für eine enge Tabelle mit Millionen von Zeilen zu erhöhen?

(Höchstwahrscheinlich gibt Ihre Abfrage ohne die TOP 1000Klausel mehr als 46.000 Zeilen zurück. Oder einige, bei denen zwischen 35.000 und 46.000 Zeilen liegen. (der graue Bereich ;-))

Die zweite Abfrage muss bestellt werden. Da Ihr NC-Index in der von Ihnen gewünschten Reihenfolge geordnet ist, ist es für das Optimierungsprogramm günstiger, diesen Index zu verwenden und dann nach Lesezeichen zum Clustered-Index zu suchen, um die fehlenden Spalten zu ermitteln, die für einen Clustered-Index-Scan erforderlich sind um das zu bestellen.

kehren Sie die Reihenfolge der Spalten in der ORDER BYKlausel um, und Sie kehren zu einem Clustered-Index-Scan zurück, da der NC-INDEX dann unbrauchbar ist.

edit hat die Antwort auf deine zweite Frage vergessen, warum du das NICHT willst

Die Verwendung eines nicht gruppierten nicht abdeckenden Index bedeutet, dass eine Zeilen-ID im NC-Index nachgeschlagen wird und dann die fehlenden Spalten im gruppierten Index nachgeschlagen werden müssen (der gruppierte Index enthält alle Spalten einer Tabelle). IOs zum Nachschlagen der fehlenden Spalten im Clustered-Index sind zufällige IOs.

Der Schlüssel hier ist RANDOM. denn für jede im NC-Index gefundene Zeile müssen die Zugriffsmethoden eine neue Seite im Clustered-Index nachschlagen. Dies ist zufällig und daher sehr teuer.

Andererseits könnte der Optimierer auch einen Clustered-Index-Scan durchführen. Mithilfe der Zuordnungszuordnungen können Scanbereiche gesucht und der Clustered-Index in großen Blöcken gelesen werden. Dies ist sequentiell und viel billiger. (Solange Ihre Tabelle nicht fragmentiert ist :-)) Der Nachteil ist, dass der GANZE gruppierte Index gelesen werden muss. Dies ist schlecht für Ihren Puffer und möglicherweise eine große Anzahl von E / A-Vorgängen. aber immer noch sequentielle IOs.

In Ihrem Fall entscheidet sich das Optimierungsprogramm für einen Bereich zwischen 35.000 und 46.000 Zeilen. Ein vollständiger Clustered-Index-Scan ist kostengünstiger. Ja, das ist falsch. Und in vielen Fällen mit engen, nicht gruppierten Indizes mit nicht zu selektiven WHEREKlauseln oder großen Tabellen geht dies schief. (Dein Tisch ist schlimmer, weil er auch sehr eng ist.)

Durch Hinzufügen von ORDER BYwird es nun teurer, den gesamten Clustered-Index zu scannen und die Ergebnisse dann zu ordnen. Stattdessen geht der Optimierer davon aus, dass es billiger ist, den bereits bestellten NC-Index zu verwenden und dann die zufällige E / A-Strafe für die Lesezeichensuche zu zahlen.

Ihre Bestellung von ist also eine perfekte Lösung für "Abfragetipps". ABER zu einem bestimmten Zeitpunkt, wenn Ihre Abfrageergebnisse so groß sind, wird die Strafe für die zufälligen E / A-Vorgänge bei der Lesezeichensuche so groß, dass sie langsamer werden. Ich gehe davon aus, dass das Optimierungsprogramm die Pläne vor diesem Zeitpunkt wieder auf den Clustered-Index-Scan umstellt, aber Sie wissen es nie genau.

In Ihrem Fall ist es besser, den Clustered-Index für die Spalte "enteredDate" zu erstellen, wenn Ihre Einfügungen nach dem eingegebenen Datum sortiert sind, wie im Chat und in der vorherigen Frage (siehe Link) erläutert.

Edward Dortland
quelle
20

Das Ausdrücken der Abfrage mit einer anderen Syntax kann manchmal dazu beitragen, dem Optimierer den Wunsch zu übermitteln, einen nicht gruppierten Index zu verwenden. Sie sollten das Formular unten finden, um den gewünschten Plan zu erhalten:

SELECT
    [ID],
    [DeviceID],
    [IsPUp],
    [IsWebUp],
    [IsPingUp],
    [DateEntered]
FROM [dbo].[Heartbeats]
WHERE
    [ID] IN
(
    -- Keys
    SELECT TOP (1000)
        [ID]
    FROM [dbo].[Heartbeats]
    WHERE 
        [DateEntered] >= CONVERT(datetime, '2011-08-30', 121)
        AND [DateEntered]  < CONVERT(datetime, '2011-08-31', 121)
);

Abfrageplan

Vergleichen Sie diesen Plan mit dem Plan, der erstellt wurde, als der nicht gruppierte Index mit einem Hinweis erzwungen wurde:

SELECT TOP (1000) 
    * 
FROM [dbo].[Heartbeats] WITH (INDEX(CommonQueryIndex))
WHERE 
    [DateEntered] BETWEEN '2011-08-30' and '2011-08-31';

Forced Index Hint Plan

Die Pläne sind im Wesentlichen identisch (eine Schlüsselsuche ist nichts anderes als eine Suche im Clustered-Index). Beide Planformen führen immer nur eine Suche für den nicht gruppierten Index und maximal 1000 Suchvorgänge für den gruppierten Index durch.

Der wichtige Unterschied liegt in der Position des Top-Operators. Zwischen den beiden Suchvorgängen positioniert, verhindert der Top, dass das Optimierungsprogramm die beiden Suchvorgänge durch einen logisch äquivalenten Scan des Clustered-Index ersetzt. Das Optimierungsprogramm ersetzt Teile eines logischen Plans durch gleichwertige relationale Operationen. Top ist kein relationaler Operator, daher verhindert das Umschreiben die Umwandlung in einen Clustered-Index-Scan. Wenn das Optimierungsprogramm den Operator "Top" neu positionieren könnte, würde es den Scan aufgrund der Funktionsweise der Kostenschätzung immer noch dem Suchen + Nachschlagen vorziehen.

Kalkulation von Scans und Suchen

Auf einem sehr hohen Niveau ist das Kostenmodell des Optimierers für Scans und Suchvorgänge recht einfach: Es schätzt, dass 320 zufällige Suchvorgänge dasselbe kosten wie das Lesen von 1350 Seiten in einem Scan. Dies hat wahrscheinlich wenig Ähnlichkeit mit den Hardwarefunktionen eines bestimmten modernen E / A-Systems, funktioniert aber als praktisches Modell recht gut.

Das Modell geht auch von einer Reihe vereinfachender Annahmen aus. Eine der wichtigsten ist, dass angenommen wird, dass jede Abfrage ohne Daten- oder Indexseiten beginnt, die sich bereits im Cache befinden. Die Implikation ist, dass jede E / A zu einer physischen E / A führt - obwohl dies in der Praxis selten der Fall ist. Selbst bei einem kalten Cache bedeutet das Vorabrufen und Vorauslesen, dass die benötigten Seiten tatsächlich ziemlich wahrscheinlich im Speicher sind, wenn der Abfrageprozessor sie benötigt.

Eine weitere Überlegung ist, dass die erste Anforderung für eine Zeile, die sich nicht im Speicher befindet, dazu führt, dass die gesamte Seite von der Festplatte abgerufen wird. Nachfolgende Anforderungen für Zeilen auf derselben Seite verursachen höchstwahrscheinlich keine physischen E / A-Vorgänge. Das Kalkulationsmodell enthält zwar Logik, um solche Effekte zu berücksichtigen, ist aber nicht perfekt.

All diese Dinge (und mehr) bedeuten, dass das Optimierungsprogramm tendenziell früher zu einem Scan wechselt, als dies wahrscheinlich der Fall sein sollte. Zufällige E / A-Vorgänge sind nur dann "viel teurer" als "sequenzielle" E / A-Vorgänge, wenn sich eine physische Operation ergibt. Der Zugriff auf Seiten im Speicher ist in der Tat sehr schnell. Selbst wenn ein physischer Lesevorgang erforderlich ist, führt ein Scan aufgrund der Fragmentierung möglicherweise überhaupt nicht zu sequenziellen Lesevorgängen, und Suchvorgänge können so angeordnet werden, dass das Muster im Wesentlichen sequenziell ist. Hinzu kommt, dass die sich ändernden Leistungsmerkmale moderner E / A-Systeme (insbesondere von Festkörpern) und das Ganze sehr wackelig aussehen.

Reihenziele

Das Vorhandensein eines Top-Operators in einem Plan verändert den Kalkulationsansatz. Das Optimierungsprogramm ist intelligent genug, um zu wissen, dass zum Ermitteln von 1000 Zeilen bei einem Scan wahrscheinlich nicht der gesamte Clustered-Index durchsucht werden muss. Es kann beendet werden, sobald 1000 Zeilen gefunden wurden. Er legt ein Zeilenziel von 1000 Zeilen für den Operator "Oben" fest und verwendet statistische Informationen, um von dort aus abzuschätzen, wie viele Zeilen von der Zeilenquelle voraussichtlich benötigt werden (in diesem Fall ein Scan). Über die Einzelheiten dieser Berechnung habe ich hier geschrieben .

Die Bilder in dieser Antwort wurden mit SQL Sentry Plan Explorer erstellt .

Paul White 9
quelle