Zusammenfassung der Fragen
Ein fragmentierter Clustered-Index funktioniert auch nach einem Index nicht gut REBUILD
. Wenn der Index ist, REORGANIZED
erhöht sich die Leistung für die angegebene Tabelle / den angegebenen Index.
Ich sehe dieses ungewöhnliche Verhalten nur unter SQL Server 2016 und höher. Ich habe dieses Szenario auf unterschiedlicher Hardware und verschiedenen Versionen getestet (alle PCs und alle haben die herkömmliche rotierende Festplatte). Lassen Sie mich wissen, wenn Sie weitere Informationen benötigen.
Ist dies ein Fehler in SQL Server 2016 und höher?
Ich kann die vollständigen Details und Analysen mit dem Skript bereitstellen, wenn jemand dies wünscht, aber momentan nicht, da das Skript ziemlich groß ist und viel Platz in der Frage beansprucht.
Testen Sie die kürzere Version des Beispielskripts aus dem unten angegebenen Link in Ihrer DEV-Umgebung, wenn Sie über SQL Server 2016 und höher verfügen.
SKRIPT
-- SECTION 1
/*
Create a Test Folder in the machine and spefiy the drive in which you created
*/
USE MASTER
CREATE DATABASE RebuildTest
ON
( NAME = 'RebuildTest',
FILENAME = 'F:\TEST\RebuildTest_db.mdf',
SIZE = 200MB,
MAXSIZE = UNLIMITED,
FILEGROWTH = 50MB )
LOG ON
( NAME = 'RebuildTest_log',
FILENAME = 'F:\TEST\RebuildTest_db.ldf',
SIZE = 100MB,
MAXSIZE = UNLIMITED,
FILEGROWTH = 10MB ) ;
GO
BEGIN TRAN
USE RebuildTest
select top 1000000
row_number () over ( order by (Select null)) n into Numbers from
sys.all_columns a cross join sys.all_columns
CREATE TABLE [DBO].FRAG3 (
Primarykey int NOT NULL ,
SomeData3 char(1000) NOT NULL )
ALTER TABLE DBO.FRAG3
ADD CONSTRAINT PK_FRAG3 PRIMARY KEY (Primarykey)
INSERT INTO [DBO].FRAG3
SELECT n , 'Some text..'
FROM Numbers
Where N/2 = N/2.0
Update DBO.FRAG3 SET Primarykey = Primarykey-500001
Where Primarykey>500001
COMMIT
-- SECTION 2
SELECT @@VERSION
/* BEGIN PART FRAG1.1 */
----- BEGIN CLEANBUFFER AND DATABASE AND MEASURE TIME
CHECKPOINT;
DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS;
SET STATISTICS TIME ON
Select Count_Big (*) From [DBO].[FRAG3] Where Primarykey >0 Option (MAxDop 1)
SET STATISTICS TIME OFF
----- END CLEANBUFFER AND DATABASE AND MEASURE TIME
-------------BEGIN PART FRAG1.2: REBUILD THE INDEX AND TEST AGAIN
--BEGIN Rebuild the Index
Alter Table [DBO].[FRAG3] REBUILD
--END Rebuild the Index
----- BEGIN CLEANBUFFER FROM DATABASE AND MEASURE TIME
CHECKPOINT;
DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS;
SET STATISTICS TIME ON
Select Count_Big (*) From [DBO].[FRAG3] Where Primarykey >0 Option (MAxDop 1)
SET STATISTICS TIME OFF
----- END CLEANBUFFER FROM DATABASE AND MEASURE TIME
--BEGIN REORGANIZE the Index
ALTER INDEX ALL ON [DBO].[FRAG3] REORGANIZE ;
--END REORGANIZE the Index
----- BEGIN CLEANBUFFER FROM DATABASE AND MEASURE TIME
CHECKPOINT;
DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS;
SET STATISTICS TIME ON
Select Count_Big (*) From [DBO].[FRAG3] Where Primarykey >0 Option (MAxDop 1)
SET STATISTICS TIME OFF
----- END CLEANBUFFER FROM DATABASE AND MEASURE TIME
-------------BEGIN PART FRAG1.4: REBUILD THE INDEX AND TEST AGAIN
--BEGIN Rebuild the Index
Alter Table [DBO].[FRAG3] REBUILD
--END Rebuild the Index
----- BEGIN CLEANBUFFER FROM DATABASE AND MEASURE TIME
CHECKPOINT;
DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS;
SET STATISTICS TIME ON
Select Count_Big (*) From [DBO].[FRAG3] Where Primarykey >0 Option (MAxDop 1)
SET STATISTICS TIME OFF
----- END CLEANBUFFER FROM DATABASE AND MEASURE TIME
-------------END PART FRAG1.4: REBUILD THE INDEX AND TEST AGAIN
Ergebnisse
Crystal Disk Mark Testergebnisse
Detail
Ich sehe ein ungewöhnliches Verhalten der Speicher-Engine (möglicherweise) unter SQL Server 2016 und höher. Ich habe eine stark fragmentierte Tabelle für (Leseprobleme mit Fragmentierung) Demo-Zwecke erstellt und sie dann neu erstellt.
Auch nach dem Wiederaufbau steigt die Indexleistung nicht wie erwartet. Um sicherzustellen, dass das Datenzugriffsmuster in Schlüsselreihenfolge und nicht in IAM-gesteuertem Scan (Allocation Order Scan) vorliegt, habe ich das Bereichsprädikat verwendet.
Anfangs dachte ich, dass SQL Server 2016 und höher für große Scans möglicherweise aggressiver ist. Um dies zu überprüfen, habe ich die Seitenzahl und die Zeilenanzahl angepasst, aber das Leistungsmuster ändert sich nicht. Ich habe alles auf einem persönlichen System getestet, damit ich sagen kann, dass keine andere Benutzeraktivität stattgefunden hat.
Ich habe dieses Verhalten auch auf anderer Hardware getestet ( alle haben herkömmliche rotierende Festplatten ). Leistungsmuster sind fast gleich.
Ich habe überprüft, dass die Wartestatistiken dort nur normal erscheinen PAGELATCH_IO
(mit Paul Randal-Skript). Ich habe Datenseiten mit DMV überprüft, sys.dm_db_database_page_allocations
es scheint auch in Ordnung zu sein.
Wenn ich die Tabelle neu organisiere oder alle Daten in eine neue Tabelle mit derselben Indexdefinition verschiebe, erhöht sich die E / A-Leistung der Festplatte. Ich habe dies mit perfmon überprüft und es scheint, dass eine Neuorganisation des Index / einer neuen Tabelle sequentielle E / A und den Wiederherstellungsindex nutzen kann, wobei immer noch die zufälligen Lesevorgänge verwendet werden, obwohl beide fast die gleiche interne und externe Fragmentierung der Datenseiten aufweisen.
Ich füge die vollständige Abfrage mit den Ergebnissen auf meinem System hinzu, die ich erfasst habe. Wenn Sie SQL Server 2016 und höher DEV-Box haben, überprüfen Sie dies bitte und teilen Sie Ihre Ergebnisse.
WARNUNG : Dieser Test besteht aus einigen undokumentierten Befehlen und wird DROPCLEANBUFFERS
daher überhaupt nicht auf dem Produktionsserver ausgeführt.
Wenn dies wirklich ein Fehler ist, sollte ich ihn einreichen.
Die Frage ist also: Ist es wirklich ein Fehler oder fehlt mir etwas;)
Links (Pastebin)
1 Fragmentierte Tabellenerstellung
2 Unterstützende SPs LAUFEN NACH DER TABELLENERSTELLUNG
5 Daten in neue Tabelle einfügen
Links: (Google Drive)
quelle
Antworten:
Die fraglichen Abfragen üben die Vorauslesefunktion von SQL Server aus . Mit der Read-Ahead-Leistungsoptimierung ruft die SQL Server-Speicher-Engine Daten während der Scans vorab ab, sodass sich die Seiten bei Bedarf für die Abfrage bereits im Puffer-Cache befinden, sodass weniger Zeit für das Warten auf Daten während der Abfrageausführung aufgewendet wird.
Der Unterschied in den Ausführungszeiten mit Read-Alead spiegelt wider, wie gut (oder nicht) das Speichersystem und die Windows-APIs mit großen E / A-Größen umgehen, sowie Unterschiede im Vorlesungsverhalten von SQL Server, die je nach Version variieren. Ältere SQL Server-Versionen (SQL Server 2008 R2 im oben genannten Artikel) beschränken Prefeatch auf 512 KB-E / A-Größen, während SQL Server 2016 und spätere E / A-Vorlesungen in größeren Größen die Funktionen moderner Standardhardware (RAID und SSD) nutzen. Beachten Sie, dass SQL Server zum Zeitpunkt der Veröffentlichung im Allgemeinen für die Ausführung auf Hardware der aktuellen Generation optimiert ist und dabei den größeren Prozessor-Cache, die NUMA-Architektur und die IOPS- / Bandbreitenfähigkeit des Speichersystems ausnutzt. Darüber hinaus führen Enterprise / Developer-Editionen den Prefetch aggressiver aus als kleinere Editionen, um den Durchsatz noch weiter zu maximieren.
Um den Grund für die unterschiedliche Leistung von SQL 2008 R2 im Vergleich zu späteren Versionen besser zu verstehen, habe ich eine geänderte Version Ihrer Skripts auf einem älteren physischen Computer mit verschiedenen Versionen von SQL Server Developer Edition ausgeführt. Diese Testbox verfügt sowohl über eine Festplatte mit 7200 U / min als auch über eine SATA-SSD, sodass derselbe Test auf demselben Computer für verschiedene Speichersysteme und SQL-Versionen ausgeführt werden kann. Ich habe während jedes Tests die Ereignisse file_read und file_read_completed mit einer erweiterten Ereignisablaufverfolgung erfasst, um eine detailliertere Analyse der E / A und des Timings zu erhalten.
Die Ergebnisse zeigen eine ungefähr vergleichbare Leistung mit allen SQL Server-Versionen und Speichersystemtypen mit Ausnahme von SQL Server 2012 und späteren Versionen auf einer einzelnen Festplattenspindel nach der Neuerstellung des Clustered-Index. Interessanterweise zeigte der XE-Trace nur bei Vorauslesevorgängen in SQL Server 2008 R2 den Modus "Fortlaufend". Die Ablaufverfolgung zeigte, dass der "Scatter / Gather" -Modus in allen anderen Versionen verwendet wurde. Ich kann nicht sagen, ob dieser Unterschied zur schnelleren Leistung beiträgt.
Die Analyse der Trace-Daten zeigt außerdem, dass SQL 2016 bei Read-Ahead-Scans viel größere Lesevorgänge ausführt und die durchschnittliche E / A-Größe je nach Speichertyp variiert. Dies bedeutet nicht unbedingt, dass SQL Server die Größe der vorauslesbaren E / A basierend auf der physischen Hardware anpasst, sondern dass die Größe möglicherweise basierend auf unbekannten Messungen angepasst wird. Die von der Speicher-Engine verwendeten Heuristiken sind nicht dokumentiert und können je nach Version und Patch-Level variieren.
Unten finden Sie eine Zusammenfassung der Testzeiten. Ich werde weitere Informationen aus den Traces hinzufügen, wenn ich mehr Zeit habe (leider ist die E / A-Größe in SQL Server 2008 R2 XE nicht verfügbar). Zusammenfassend unterscheidet sich das E / A-Profil je nach Version und Speichertyp. Die durchschnittliche E / A-Größe für Versionen über SQL Server 2014 hat 512 KB nie überschritten, während SQL Server 2016 in diesen Tests mehr als 4 MB in einem einzelnen E / A gelesen hat. Die Anzahl der ausstehenden Lesevorgänge war im SQL 2016-Test ebenfalls viel geringer, da SQL Server weniger E / A-Anforderungen hat, um dieselbe Arbeit auszuführen.
Ich habe diese Tests auch auf einer VM mit einem HDD-gestützten SAN ausgeführt und eine ähnliche Leistung wie bei der SATA-SSD festgestellt. Unter dem Strich tritt dieses Leistungsproblem nur bei einer einspindeligen HDD-Datendatei auf, was nur auf PCs und nicht auf modernen Produktionssystemen üblich ist. Ob dies als Leistungsregressionsfehler angesehen werden sollte oder nicht, ist fraglich, aber ich werde mich bemühen, weitere Informationen zu erhalten.
BEARBEITEN
Ich habe mich an meine Kontakte gewandt und Erland Sommarskog hat auf die höhere Fragmentanzahl hingewiesen, die von sys.dm_db_index_physical_stats in späteren Versionen gemeldet wurde. Als ich tiefer grub, bemerkte ich, dass die REBUILD-Anweisung eine parallele Abfrage ist. Die Implikation ist, dass eine parallele Neuerstellung tatsächlich die Fragmentanzahl erhöhen kann (und sogar eine Fragmentierung in einer Tabelle ohne Fragmentierung einführt), da die Speicherplatzzuweisungen parallel erfolgen. Dies wirkt sich im Allgemeinen nicht auf die Leistung aus, außer bei Read-Ahead-Scans, und ist insbesondere bei Einzelspindel-Spinnmedien ein Problem, wie die Tests zeigen. Dies ist in allen SQL-Versionen eine Überlegung.
Paul Randal wies darauf hin und bezog sich auf dieses Dokument, um weitere Informationen zu erhalten. Eine bewährte Methode für Indexwiederherstellungen für eine Workload, die Read-Ahead-Scans (z. B. Data Warehousing) nutzt, ist die Neuerstellung
WITH (MAXDOP = 1)
.quelle