Ich habe die folgende Tabelle mit 7,5 Millionen Datensätzen:
CREATE TABLE [dbo].[TestTable](
[Id] [int] IDENTITY(1,1) NOT NULL,
[TestCol] [nvarchar](50) NOT NULL,
[TestCol2] [nvarchar](50) NOT NULL,
[TestCol3] [nvarchar](50) NOT NULL,
[Anonymised] [tinyint] NOT NULL,
[Date] [datetime] NOT NULL,
CONSTRAINT [PK_TestTable] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
Ich stelle fest, dass, wenn das Datumsfeld einen nicht gruppierten Index enthält:
CREATE NONCLUSTERED INDEX IX_TestTable_Date ON [dbo].[TestTable] ([Date])
-und ich führe die folgende Abfrage aus:
UPDATE TestTable
SET TestCol='*GDPR*', TestCol2='*GDPR*', TestCol3='*GDPR*', Anonymised=1
WHERE [Date] <= '25 August 2016'
-Die von der Indexzugriffsoperation zurückgegebenen Daten werden so sortiert, dass sie der Schlüsselreihenfolge des PK / CX entsprechen, wodurch die Leistung verringert wird.
Ich war überrascht, dass das Entfernen des Index aus dem Datumsfeld die Leistung der Abfrage tatsächlich um etwa 30% verbessert, da die Sortierung nicht mehr ausgeführt wird:
Meine Theorie, und dies mag für die erfahreneren unter Ihnen offensichtlich sein, ist, dass sie herausgefunden hat, dass die Datumsspalte implizit genau so geordnet ist wie der Primärschlüssel / Clustered-Index.
Meine Frage lautet also: Ist es möglich, diese Tatsache zu nutzen, um die Leistung meiner Abfrage zu verbessern?
quelle
[Date]
aber in der richtigenDESC
Reihenfolge? Nur neugierig, da das Prädikat ist<=
. Wenn der Index onDate
(in der StandardreihenfolgeACS
) anderen Abfragen hilft, können Sie möglicherweise versuchen, dem UPDATE einen Tabellenhinweis hinzuzufügen, um die Verwendung der PK zu erzwingen. Oder teilen Sie dies in zwei Teile auf: Erstellen Sie eine temporäre Tabelle, füllen Sie sie mit[Id]
basierend auf[Date] <= '25 August 2016'
und entfernen Sie sie dannWHERE
aus dem UPDATE und fügen Sie sie hinzuFROM dbo.TestTable tt INNER JOIN #tmp ids ON ids.[Id] = tt.[Id]
. Es ist schließlich ein UPDATE und muss die tatsächlichen Zeilen, den Index oder die Nr. Finden.Antworten:
Ich habe Testdaten verspottet, die Ihr Problem hauptsächlich reproduzieren:
Statistiken für die Abfrage, die den nicht gruppierten Index verwendet:
Statistiken für die Abfrage, die den Clustered-Index verwendet:
Kommen wir zu Ihrer Frage:
Ja. Sie können den nicht gruppierten Index verwenden, den Sie bereits
id
benötigen, um den Maximalwert , der aktualisiert werden muss, effizient zu ermitteln . Wenn Sie dies in einer Variablen speichern und danach filtern, erhalten Sie einen Abfrageplan für das Update, der den Clustered-Index-Scan (ohne Sortierung) durchführt, der vorzeitig beendet wird und daher weniger E / A ausführt. Hier ist eine Implementierung:Führen Sie Statistiken für die neue Abfrage aus:
Sowie der Abfrageplan:
Nach alledem lässt Ihr Wunsch, die Abfrage schneller zu machen, darauf schließen, dass Sie die Abfrage mehrmals ausführen möchten. Im Moment hat Ihre Abfrage einen offenen Filter für die
date
Spalte. Ist es wirklich notwendig, die Zeilen mehr als einmal zu anonymisieren? Können Sie vermeiden, bereits anonymisierte Zeilen zu aktualisieren oder zu scannen? Es sollte auf jeden Fall schneller sein, eine Reihe von Daten mit Daten auf beiden Seiten zu aktualisieren. Sie können dieAnonymised
Spalte auch zu Ihrem Index hinzufügen , dieser Index muss jedoch während IhrerUPDATE
Abfrage aktualisiert werden. Zusammenfassend lässt sich sagen, dass Sie es vermeiden sollten, dieselben Daten immer wieder zu verarbeiten, wenn Sie können.Die ursprüngliche Abfrage, die Sie mit der Sortierung haben, ist aufgrund der im
Clustered Index Update
Operator ausgeführten Arbeit langsamer . Die für die Indexsuche und -sortierung aufgewendete Zeit beträgt nur 407 ms. Sie können dies im aktuellen Plan sehen. Der Plan wird im Zeilenmodus ausgeführt, sodass die für die Sortierung aufgewendete Zeit die Zeit dieses Operators zusammen mit jedem untergeordneten Operator ist:Damit bleibt dem Sortieroperator etwa 1600 ms Zeit. SQL Server muss Seiten aus dem Clustered-Index lesen, um die Aktualisierung durchzuführen. Sie können sehen, dass der
Clustered Index Update
Operator 1205921 logische Lesevorgänge ausführt. Weitere Informationen zum Sortieren von Optimierungen für DML und zum optimierten Prefetch finden Sie in diesem Blogbeitrag von Paul White .Der andere Abfrageplan, den Sie (ohne Sortierung) haben, benötigt 683 ms für den Clustered-Index-Scan und etwa 550 ms für den
Clustered Index Update
Operator. Der Aktualisierungsoperator führt für diese Abfrage keine E / A durch.Die einfache Antwort darauf, warum der Plan mit der Sortierung langsamer ist, besteht darin, dass SQL Server den Clustered-Index für diesen Plan logischer liest als der Clustered-Index-Scan-Plan. Selbst wenn sich alle benötigten Daten im Speicher befinden, ist der Aufwand und die Kosten für diese logischen Lesevorgänge immer noch hoch. Eine bessere Antwort ist viel schwieriger zu bekommen, da die Pläne meines Wissens keine weiteren Details enthalten. Es ist möglich, PerfView oder ein anderes auf ETW-Tracing basierendes Tool zu verwenden, um Anrufstapel zwischen den Abfragen zu vergleichen:
Links befindet sich die Abfrage, die den Clustered-Index-Scan durchführt, und rechts die Abfrage, die die Sortierung durchführt. Ich habe Anrufstapel blau oder rot markiert, die nur in einer Abfrage angezeigt werden. Es überrascht nicht, dass die verschiedenen Aufrufstapel mit einer hohen Anzahl von abgetasteten CPU-Zyklen für die Sortierabfrage mit den logischen Lesevorgängen zu tun zu haben scheinen, die zum Durchführen der Aktualisierung des Clustered-Index erforderlich sind. Darüber hinaus gibt es Unterschiede in der Anzahl der abgetasteten Zyklen zwischen den Abfragen für dieselbe Operation. Zum Beispiel benötigt die Abfrage mit der Sortierung 31 Zyklen zum Erfassen von Latches, während die Abfrage mit dem Scan nur 9 Zyklen zum Erfassen von Latches benötigt.
Ich vermute, dass SQL Server den langsameren Plan aufgrund einer Kostenbeschränkung für den Abfrageplan-Operator auswählt. Möglicherweise ist ein Teil des Unterschieds in der Laufzeit auf die Hardware oder Ihre Edition von SQL Server zurückzuführen. In jedem Fall kann SQL Server nicht herausfinden, dass die Datumsspalte implizit genau so geordnet ist wie der Clustered-Index. Die Daten werden vom Clustered-Index-Scan in der Reihenfolge der Clustered-Schlüssel zurückgegeben, sodass beim Sortieren der Clustered-Index-Aktualisierung keine Sortierung durchgeführt werden muss, um die E / A zu optimieren.
quelle