Warum dauert es länger, einen Index zu erstellen, nachdem sich die Spaltengröße erhöht hat?

18

Unser Anbieter hat die Spaltenbreite für fast jede Spalte in der gesamten Datenbank geändert. Die Datenbank umfasst über 7 TB, 9000 Tabellen. Wir versuchen, einen Index für eine Tabelle mit 5,5 Milliarden Zeilen zu erstellen. Vor dem Upgrade des Anbieters konnten wir den Index in 2 Stunden erstellen. Jetzt dauert es Tage. Was sie getan haben, ist, eine beliebige varchar (xx) -Größe auf varchar (256) zu erhöhen. Die meisten Spalten waren also varchar (18) oder varchar (75) usw.

Auf jeden Fall besteht der Primärschlüssel aus 6 Spalten mit einer kombinierten Breite von 126 Zeichen. Nach dem Upgrade besteht der Primärschlüssel nun aus 1283 Zeichen, was gegen das SQL Server-Limit von 900 Zeichen verstößt. Die gesamte Tabellenspaltenbreite ging von einer kombinierten Varchar-Gesamtzahl von 1049 auf eine kombinierte Varchar-Gesamtzahl von 4009 über.

Es gibt keine Zunahme der Daten, die Tabelle nimmt nicht mehr "Platz" in Anspruch als vor der Erhöhung der gesamten Spaltenbreite, aber die Leistung zum Erstellen eines so einfachen Index dauert jetzt unangemessen lange.

Kann jemand erklären, warum das Erstellen und Indizieren so viel länger dauert, wenn nur die Größe der Spalten erhöht wurde?

Der Index, den wir erstellen möchten, ist nicht gruppiert, da pk der gruppierte Index ist. Nach mehreren Versuchen, den Index zu erstellen, gaben wir auf. Ich denke, es lief 4 oder 5 Tage ohne Abschluss.

Ich habe dies in einer Nicht-Produktionsumgebung versucht, indem ich einen Dateisystem-Snapshot erstellt und die Datenbank auf einem leiseren Server hochgefahren habe.

Daniel Miller
quelle

Antworten:

12

Remus hat hilfreich darauf hingewiesen, dass sich die maximale Länge der VARCHARSpalte auf die geschätzte Zeilengröße auswirkt und daher die von SQL Server bereitgestellten Speicherzuweisungen.

Ich habe versucht, etwas mehr Nachforschungen anzustellen, um den Teil seiner Antwort zu erweitern, der von dieser Kaskade der Dinge ausgeht. Ich habe keine vollständige oder präzise Erklärung, aber hier ist, was ich gefunden habe.

Repro-Skript

Ich habe ein vollständiges Skript erstellt , das einen gefälschten Datensatz generiert, für den die Indexerstellung auf meinem Computer ungefähr 10-mal so lange dauert wie für die VARCHAR(256)Version. Die verwendeten Daten sind genau die gleiche, aber die erste Tabelle verwendet , die tatsächlichen Längen von max 18, 75, 9, 15, 123, und 5, während alle Spalten verwenden , um eine maximale Länge des 256in der zweiten Tabelle.


Eingabe der Originaltabelle

Hier sehen wir, dass die ursprüngliche Abfrage in ungefähr 20 Sekunden abgeschlossen ist und die logischen Lesevorgänge der Tabellengröße von ~1.5GB(195 KB Seiten, 8 KB pro Seite) entsprechen.

-- CPU time = 37674 ms,  elapsed time = 19206 ms.
-- Table 'testVarchar'. Scan count 9, logical reads 194490, physical reads 0
CREATE CLUSTERED INDEX IX_testVarchar
ON dbo.testVarchar (s1, s2, s3, s4)
WITH (MAXDOP = 8) -- Same as my global MAXDOP, but just being explicit
GO


Eingabe der VARCHAR (256) -Tabelle

In der VARCHAR(256)Tabelle sehen wir, dass die verstrichene Zeit dramatisch zugenommen hat.

Interessanterweise erhöhen sich weder die CPU-Zeit noch die logischen Lesevorgänge. Dies ist sinnvoll, da die Tabelle exakt dieselben Daten enthält, erklärt jedoch nicht, warum die verstrichene Zeit so viel langsamer ist.

-- CPU time = 33212 ms,  elapsed time = 263134 ms.
-- Table 'testVarchar256'. Scan count 9, logical reads 194491
CREATE CLUSTERED INDEX IX_testVarchar256
ON dbo.testVarchar256 (s1, s2, s3, s4)
WITH (MAXDOP = 8) -- Same as my global MAXDOP, but just being explicit
GO


I / O und Wartestatistik: Original

Wenn wir ein bisschen mehr Details erfassen (mithilfe von p_perfMon, einer Prozedur, die ich geschrieben habe ), können wir feststellen, dass der Großteil der E / A- Vorgänge für die LOGDatei ausgeführt wird. Wir sehen eine relativ bescheidene Menge an E / A in der eigentlichen ROWSDatei (der Hauptdatendatei), und der primäre Wartetyp ist LATCH_EX, was auf einen Konflikt auf der Speicherseite hinweist.

Wir können auch sehen, dass meine sich drehende Scheibe laut Paul Randal irgendwo zwischen "schlecht" und "schockierend schlecht" liegt :)

Bildbeschreibung hier eingeben


E / A und Wartestatistik: VARCHAR (256)

Für die VARCHAR(256)Version sehen die E / A- und Wartestatistiken völlig anders aus! Hier sehen wir eine enorme Zunahme der E / A in der Datendatei ( ROWS), und die Standzeiten lassen Paul Randal jetzt einfach "WOW!" Sagen.

Es ist nicht überraschend, dass der Wartetyp # 1 jetzt ist IO_COMPLETION. Aber warum wird so viel E / A generiert?

Bildbeschreibung hier eingeben


Tatsächlicher Abfrageplan: VARCHAR (256)

Aus dem Abfrageplan können wir ersehen, dass der SortOperator in der VARCHAR(256)Version der Abfrage einen rekursiven Fehler aufweist (5 Ebenen tief!) . (In der Originalversion gibt es überhaupt keine Verschüttung.)

Bildbeschreibung hier eingeben


Fortschritt der Live-Abfrage: VARCHAR (256)

Mit sys.dm_exec_query_profiles können Sie den Fortschritt von Live-Abfragen in SQL 2014+ anzeigen . In der Originalversion kann das gesamte Table Scanund Sortohne Verschütten verarbeitet werden ( spill_page_countbleibt 0durchgehend erhalten).

In der VARCHAR(256)Version können wir jedoch feststellen, dass sich schnell Seitenverluste für den SortBediener ansammeln . Hier ist eine Momentaufnahme des Abfragefortschritts, kurz bevor die Abfrage abgeschlossen ist. Die Daten hier werden über alle Threads hinweg aggregiert.

Bildbeschreibung hier eingeben

Wenn ich in jeden Thread einzeln grabe, sehe ich, dass 2 Threads die Sortierung innerhalb von etwa 5 Sekunden abschließen (insgesamt 20 Sekunden, nach 15 Sekunden, die für den Tabellenscan aufgewendet wurden). Wenn alle Threads mit dieser Geschwindigkeit weitergearbeitet hätten, wäre die VARCHAR(256)Indexerstellung ungefähr zur selben Zeit wie die ursprüngliche Tabelle abgeschlossen worden.

Die verbleibenden 6 Threads werden jedoch viel langsamer ausgeführt. Dies kann an der Art und Weise liegen, wie der Speicher zugewiesen wird, und an der Art und Weise, wie die Threads von E / A gehalten werden, während sie Daten verschütten. Ich weiß es nicht genau.

Bildbeschreibung hier eingeben


Was kannst du tun?

Es gibt eine Reihe von Dingen, die Sie in Betracht ziehen könnten:

  • Arbeiten Sie mit dem Anbieter zusammen, um ein Rollback auf eine frühere Version durchzuführen. Wenn dies nicht möglich ist, teilen Sie dem Anbieter mit, dass Sie mit dieser Änderung nicht zufrieden sind, damit er sie in einer zukünftigen Version zurücksetzen kann.
  • Berücksichtigen Sie beim Hinzufügen Ihres Indexes, OPTION (MAXDOP X)wo Xeine niedrigere Zahl als die aktuelle Einstellung auf Serverebene ist. Als ich OPTION (MAXDOP 2)diesen spezifischen Datensatz auf meinem Computer verwendete, wurde die VARCHAR(256)Version in 25 seconds(im Vergleich zu 3-4 Minuten mit 8 Threads!) Abgeschlossen . Es ist möglich, dass das Verschüttungsverhalten durch eine höhere Parallelität verstärkt wird.
  • Wenn zusätzliche Hardwareinvestitionen möglich sind, analysieren Sie die E / A (den wahrscheinlichen Engpass) auf Ihrem System und erwägen Sie die Verwendung einer SSD, um die durch Überläufe verursachte E / A-Latenz zu verringern.


Weitere Lektüre

Paul White hat einen netten Blog-Beitrag über die Interna von SQL Server-Arten , die von Interesse sein könnten. Es geht ein wenig um Verschütten, Thread-Versatz und Speicherzuweisung für parallele Sortierungen.

Geoff Patterson
quelle
11

Die Zwischensortierungstabelle wird zwischen den beiden Fällen unterschiedlich geschätzt. Dies führt zu unterschiedlichen Speicherzuteilungsanforderungen ( VARCHAR(256)die größer sind) und wahrscheinlich zu einer viel geringeren tatsächlichen Zuteilung, prozentual im Vergleich zu der "idealen" Anforderung. Ich vermute, dies führt zu Verschüttungen beim Sortieren.

Wenn ich das Skript von Geoff aus teste (nur bei 100.000 Zeilen), kann ich einen deutlichen Unterschied in der geschätzten Sortierreihengröße feststellen (141B vs. 789B). Von da an kaskadieren die Dinge.

Remus Rusanu
quelle
8
Ich bin mir sicher, dass Paul eine gründlichere und vollständigere Antwort liefern wird, einschließlich vielleicht Call Stacks, die vom Entwicklerteam als Lernmaterial verwendet werden. Wieder ...
Remus Rusanu