Die neue Eigenschaft „Actual Rows Read“ in Ausführungsplänen (die in SQL Server Management Studio als „Number of Rows Read“ angezeigt wird) war eine willkommene Ergänzung für Leistungstuner. Es ist, als hätte man eine neue Supermacht, in der Lage zu sein, die Bedeutung des Suchprädikats gegenüber dem Restprädikat innerhalb eines Suchoperators zu erkennen. Ich liebe das, weil es für Abfragen wirklich wichtig sein kann.
Sehen wir uns zwei Abfragen an, die ich für AdventureWorks2012 ausführe. Sie sind sehr einfach – einer listet Leute auf, die John S genannt werden, und der andere listet Leute auf, die J Smith heißen. Wie alle guten Telefonbücher haben wir einen Index für Nachname, Vorname.
select FirstName, LastName from Person.Person where LastName like 'S%' and FirstName = 'John'; select FirstName, LastName from Person.Person where LastName = 'Smith' and FirstName like 'J%';
Falls Sie neugierig sind, ich bekomme 2 Zeilen zurück von der ersten und 14 Zeilen zurück von der zweiten. Ich interessiere mich eigentlich nicht so sehr für die Ergebnisse, ich interessiere mich für die Ausführungspläne.
Mal sehen, was los ist. Ich habe eine ältere Kopie von SQL Sentry Plan Explorer geöffnet und meine Pläne nebeneinander geöffnet. Übrigens – ich hatte beide Abfragen zusammen ausgeführt und so befanden sich beide Pläne in derselben .sqlplan-Datei. Aber ich könnte dieselbe Datei zweimal in PE öffnen und sie glücklich nebeneinander in Registerkartengruppen platzieren.
Toll. Sie sehen gleich aus! Ich kann sehen, dass der Seek auf der linken Seite zwei statt vierzehn Zeilen erzeugt – offensichtlich ist dies die bessere Abfrage.
Aber mit einem größeren Fenster hätte ich mehr Informationen gesehen, und es ist ein Glück, dass ich die beiden Abfragen im selben Stapel ausgeführt hatte.
Sie können sehen, dass die zweite Abfrage, die 14 Zeilen statt 2 Zeilen erzeugte, schätzungsweise 80 % der Kosten in Anspruch nahm! Wenn ich die Abfragen separat ausführen würde, würde mir jede 100 % anzeigen.
Vergleichen wir nun mit der neuesten Version von Plan Explorer.
Was mir sofort auffällt, ist die Warnung. Schauen wir etwas genauer hin.
Die Warnung lautet „Operation verursachte Rest-IO. Die tatsächliche Anzahl der gelesenen Zeilen war 2.130, aber die Anzahl der zurückgegebenen Zeilen war 2.“ Tatsächlich sehen wir weiter oben „Actual Rows Read“ mit 2.130 und Actual Rows mit 2.
Wow! Um diese Zeilen zu finden, mussten wir 2.130 durchsuchen?
Sie sehen, die Art und Weise, wie die Seek abläuft, besteht darin, damit zu beginnen, über das Seek-Prädikat nachzudenken. Das ist derjenige, der den Index gut nutzt und der tatsächlich dazu führt, dass die Operation ein Seek ist. Ohne ein Suchprädikat wird die Operation zu einem Scan. Wenn dieses Suchprädikat nun garantiert höchstens eine Zeile ist (z. B. wenn es einen Gleichheitsoperator für einen eindeutigen Index hat), dann haben wir eine Singleton-Suche. Andernfalls haben wir einen Bereichsscan, und dieser Bereich kann ein Präfix, einen Start und ein Ende haben (aber nicht unbedingt sowohl einen Start als auch ein Ende). Dies definiert die Zeilen in der Tabelle, an denen wir für Seek interessiert sind.
Aber „interessiert an“ bedeutet nicht unbedingt „zurückgekehrt“, weil wir möglicherweise noch mehr Arbeit zu erledigen haben. Diese Arbeit wird im anderen Prädikat beschrieben, das oft als Restprädikat bekannt ist.
Nun könnte dieses Restprädikat tatsächlich die meiste Arbeit erledigen. Es ist definitiv da – es filtert Dinge von 2.130 Zeilen auf nur 2 herunter.
Der Range Scan beginnt im Index bei „John S“. Wir wissen, dass, wenn es ein „John S“ gibt, dies die erste Reihe sein muss, die das Ganze befriedigen kann. „Ian S“ kann das nicht. Wir können also an diesem Punkt den Index durchsuchen, um unseren Range Scan zu starten. Wenn wir uns die Plan-XML ansehen, können wir dies explizit sehen.
Beachten Sie, dass wir kein Präfix haben. Das gilt, wenn Sie eine Gleichheit in der ersten Spalte innerhalb des Indexes haben. Wir haben nur StartRange und EndRange. Der Beginn des Bereichs ist „Größer als oder gleich“ (GE) ScanType, beim Wert „S, John“ (die Spaltenverweise außerhalb des Bildschirms sind Nachname, Vorname), und das Ende des Bereichs ist „Kleiner als“ ( LT) den Wert T. Wenn der Scan T erreicht, ist er fertig. Nichts mehr zu tun. Der Seek hat nun seinen Range Scan abgeschlossen. Und in diesem Fall werden 2.130 Zeilen zurückgegeben!
Abgesehen davon, dass es nicht wirklich 2.130 Zeilen zurückgibt, sondern nur 2.130 Zeilen liest. Namen wie Barry Sai und Ken Sánchez werden gelesen, aber nur die Namen, die die nächste Prüfung bestehen, werden zurückgegeben – das Residual Predicate, das sicherstellt, dass der FirstName John ist.
Der Eintrag Actual Rows Read in den Eigenschaften des Operators Index Seek zeigt uns diesen Wert von 2.130. Und obwohl es in früheren Versionen von Plan Explorer sichtbar ist, erhalten wir keine Warnung darüber. Das ist relativ neu.
Unsere zweite Abfrage (Suche nach J Smith) ist viel netter, und es gibt einen Grund, warum sie schätzungsweise mehr als viermal billiger war.
Hier kennen wir den Nachnamen genau (Smith) und der Range Scan ist auf dem Vornamen (J%).
Hier kommt das Präfix ins Spiel.
Wir sehen, dass unser Präfix ein Gleichheitsoperator ist (=, ScanType=”EQ”) und dass LastName Smith sein muss. Wir haben noch nicht einmal den Anfang oder das Ende des Bereichs berücksichtigt, aber das Präfix sagt uns, dass der Bereich in dem Teil des Index enthalten ist, in dem Nachname Smith ist. Jetzt können wir die Zeilen>=J und
Hier gibt es noch ein Residual Predicate, aber das soll nur sicherstellen, dass „LIKE J%“ tatsächlich getestet wird. Während es für uns intuitiv erscheint, dass „LIKE J%“ genau gleichbedeutend mit „>=J und
Vor Service Pack 3 von SQL Server 2012 hatten wir diese Eigenschaft nicht, und um ein Gefühl für den Unterschied zwischen „Actual Rows Read“ und „Actual Rows“ zu bekommen, müssten wir das Ablaufverfolgungsflag 9130 verwenden. Hier sind diese beiden Pläne mit eingeschaltetem TF:
Sie können sehen, dass es diesmal keine Warnung gibt, weil der Seek-Operator alle 2130 Zeilen zurückgibt. Ich denke, wenn Sie eine Version von SQL Server verwenden, die dieses Lesen von tatsächlichen Zeilen unterstützt, sollten Sie bei Ihren Untersuchungen aufhören, das Ablaufverfolgungsflag 9130 zu verwenden, und sich stattdessen die Warnungen im Plan-Explorer ansehen. Aber vor allem müssen Sie verstehen, wie Ihre Bediener ihre Arbeit erledigen, denn dann können Sie interpretieren, ob Sie mit dem Plan zufrieden sind oder ob Sie Maßnahmen ergreifen müssen.
In einem anderen Beitrag zeige ich Ihnen eine Situation, in der Sie es vielleicht vorziehen, dass „Actual Rows Read“ höher ist als „Actual Rows“.
@rob_farley