Database
 sql >> Datenbank >  >> RDS >> Database

Manchmal können Sie eine Spalte direkt vergrößern

Letztes Jahr hat Andy Mallon über das Upsizing einer Spalte von int gebloggt zu bigint ohne Ausfallzeiten. (Warum dies in modernen Versionen von SQL Server keine reine Metadatenoperation ist, ist mir schleierhaft, aber das ist ein anderer Beitrag.)

Wenn wir uns mit diesem Problem befassen, handelt es sich normalerweise um breite und massive Tabellen (sowohl in Bezug auf die Zeilenanzahl als auch auf die reine Größe), und die Spalte, die wir ändern müssen, ist die einzige/führende Spalte im Clustering-Schlüssel. In der Regel gibt es auch andere Komplikationen – Einschränkungen für eingehende Fremdschlüssel, viele nicht geclusterte Indizes und eine ausgelastete Datenbank, die äußerst empfindlich auf Protokollaktivitäten reagiert (weil sie an Änderungsverfolgung, Replikation, Verfügbarkeitsgruppen oder allen dreien beteiligt ist). ).

Aus diesem Grund müssen wir einen Ansatz wie den von Andy skizzierten verfolgen, bei dem wir eine Schattentabelle mit dem neuen Schema erstellen, Trigger erstellen, um beide Kopien synchron zu halten, und dann im eigenen Tempo des Teams Batch/Backfill durchführen, bis sie zum Austausch bereit sind in der Kopie wie das Original.

Aber ich bin faul!

Es gibt einige Fälle, in denen Sie die Spalte direkt ändern können, wenn Sie sich ein kleines Zeitfenster für Ausfallzeit/Blockierung leisten können, und es wird ein viel einfacherer Vorgang. Letzte Woche tauchte ein solcher Fall auf, mit einer Tabelle über 1 TB, aber nur 100.000 Zeilen. Fast alle Daten waren Off-Row (LOB), sie konnten sich bei Bedarf ein kleines Ausfallzeitfenster leisten, und sie planten, die Änderungsnachverfolgung zu deaktivieren und sie trotzdem neu zu konfigurieren. In der Zuversicht, dass die Neuerstellung des geclusterten PK die LOB-Daten nicht (viel) berühren müsste, schlug ich vor, dass dies ein Fall sein könnte, in dem wir die Änderung einfach direkt anwenden können.

In einem isolierten Szenario (keine eingehenden Fremdschlüssel, keine zusätzlichen Indizes, keine vom Protokollleser abhängigen Aktivitäten und keine Bedenken hinsichtlich der Parallelität) habe ich einige Tests zusammengestellt, um im Vakuum zu sehen, wie lange diese Änderung dauern würde und Auswirkungen auf das Transaktionsprotokoll. Die Hauptfrage, auf die ich im Voraus keine Antwort wusste, lautete:„Wie hoch sind die inkrementellen Kosten für die Aktualisierung von Tabellen vor Ort, wenn große Mengen an Nicht-Schlüsseldaten vorhanden sind?“

Ich werde versuchen, hier viel in einen Beitrag zu packen. Ich habe viele Tests durchgeführt, und es hängt alles zusammen, auch wenn nicht alle Testszenarien auf Sie zutreffen. Bitte haben Sie Geduld mit mir.

Die Tische

Ich habe 6 Tabellen erstellt, darunter eine Baseline, die nur hatte die Schlüsselspalte, eine Tabelle mit 4 KB in der Zeile gespeichert und dann vier Tabellen mit jeweils einer varchar(max)-Spalte, die mit unterschiedlichen Mengen an Zeichenfolgendaten (4 KB, 16 KB, 64 KB und 256 KB) gefüllt war.

CREATE TABLE dbo.withJustId( id int NOT NULL, CONSTRAINT pk_withJustId PRIMARY KEY CLUSTERED (id)); CREATE TABLE dbo.withoutLob (id int NOT NULL, extradata varchar(4000) NOT NULL DEFAULT (REPLICATE('x', 4000)), CONSTRAINT pk_withoutLob PRIMARY KEY CLUSTERED (id)); CREATE TABLE dbo.withLob004 (id int NOT NULL, extradata varchar(max) NOT NULL DEFAULT (REPLICATE('x', 4000)), CONSTRAINT pk_withLob004 PRIMARY KEY CLUSTERED (id)); CREATE TABLE dbo.withLob016 (id int NOT NULL, extradata varchar(max) NOT NULL DEFAULT (REPLICATE(CONVERT(varchar(max),'x'), 16000)), CONSTRAINT pk_withLob016 PRIMARY KEY CLUSTERED (id)); CREATE TABLE dbo.withLob064 (id int NOT NULL, extradata varchar(max) NOT NULL DEFAULT (REPLICATE(CONVERT(varchar(max),'x'), 64000)), CONSTRAINT pk_withLob064 PRIMARY KEY CLUSTERED (id)); CREATE TABLE dbo.withLob256 (id int NOT NULL, extradata varchar(max) NOT NULL DEFAULT (REPLICATE(CONVERT(varchar(max),'x'), 256000)), CONSTRAINT pk_withLob256 PRIMARY KEY CLUSTERED (id)); 

Ich habe jeweils 100.000 Zeilen gefüllt:

INSERT dbo.withJustId (id) SELECT TOP (100000) id =ROW_NUMBER() OVER (ORDER BY c1.name) FROM sys.all_columns AS c1 CROSS JOIN sys.all_objects; dbo.ohneLob (id) EINFÜGEN ID AUS dbo.withJustId AUSWÄHLEN;dbo.withLob004 (ID) EINFÜGEN ID AUS dbo.withJustId AUSWÄHLEN;dbo.withLob016 EINFÜGEN (id) ID AUS dbo.withJustId AUSWÄHLEN;dbo.withLob064 (ID) EINFÜGEN AUSWÄHLEN id FROM dbo.withJustId;INSERT dbo.withLob256 (id) SELECT id FROM dbo.withJustId;

Ich erkenne an, dass das Obige unrealistisch ist; Wie oft haben wir eine Tabelle, die nur aus einer Kennung + LOB-Daten besteht? Ich habe die Tests mit diesen zusätzlichen vier Spalten erneut durchgeführt, um den Nicht-LOB-Datenseiten etwas mehr reale Substanz zu verleihen:

 fill1 char(320) NICHT NULL STANDARD ('x'), count1 int NICHT NULL STANDARD (0), count2 int NICHT NULL STANDARD (0), dt datetime2 NICHT NULL STANDARD sysutcdatetime(),

Diese Tabellen sind in Bezug auf die Gesamtgröße nur geringfügig größer, aber die proportionale Zunahme der Menge an Nicht-LOB-Daten (in dieser Grafik nicht dargestellt) ist der große, aber verborgene Unterschied:

Reservierte Größe von Tabellen in GB

Die Prüfungen

Dann habe ich für jede dieser Operationen die Zeit gemessen und Protokolldaten gesammelt (mit und ohne ONLINE = ON ) für jede Variation der Tabelle:

ALTER TABLE dbo. DROP CONSTRAINT pk_; ALTER TABLE dbo. ALTER COLUMN id bigint NOT NULL; -- MIT (ONLINE =EIN); ALTER TABLE dbo. ADD CONSTRAINT pk_ PRIMARY KEY CLUSTERED (id);

In Wirklichkeit habe ich dynamisches SQL verwendet, um all diese Tests zu generieren, sodass ich nicht vor jedem Test manuell mit Skripten herumhantieren musste.
In einem anderen Beitrag teile ich das dynamische SQL, das ich verwendet habe, um diese Tests zu generieren, und erfasse die Zeitangaben bei jedem Schritt.

Zum Vergleich habe ich auch Andys Methode getestet (allerdings ohne Batching und nur auf der dünnen Version der Tabelle):

CREATE TABLE dbo._copy ( id bigint NOT NULL -- <, zusätzliche Datenspalte, falls relevant> CONSTRAINT pk_copy_ PRIMARY KEY CLUSTERED (id)); INSERT dbo._copy SELECT * FROM dbo.; EXEC sys.sp_rename N'dbo.', N'dbo._old', N'OBJECT';EXEC sys.sp_rename N'dbo._copy', N'dbo.' , N'OBJEKT';

Ich habe hier die breiteren Tische übersprungen; Ich wollte nicht auf die Komplexität des Codierens und Messens von Batch-Operationen eingehen. Der offensichtliche Schmerzpunkt hier ist, dass Sie im Gegensatz zum direkten Ändern der Spalte bei der Shadow-Methode jedes einzelne Byte dieser LOB-Daten kopieren müssen. Batching kann die großen Auswirkungen des Versuchs, dies in einer einzigen Transaktion zu tun, minimieren, aber all dieses Mischen muss schließlich nachgelagert wiederholt werden. Das Batching an der Quelle kann nicht vollständig kontrollieren, wie sehr dies am Ziel schaden wird.

Die Ergebnisse

Die ersten Ergebnisse, die ich zeigen werde, sind nur die durchschnittlichen Dauern für In-Place-Änderungen, für alle 12 Tischkonfigurationen und mit und ohne ONLINE = ON :

Dauer in Sekunden für die direkte Änderung der Spalte

Die Ausführung als Online-Vorgang dauert länger (im schlimmsten Fall 200 Sekunden), blockiert Benutzer jedoch nicht. Es scheint mit der Größe zuzunehmen, aber nicht ganz linear. Das Offline-Durchführen dieser Operation führt zu einer Blockierung, ist aber viel schneller und ändert sich nicht ganz so drastisch, wenn die Tabelle größer wird (selbst bei der größten Größe passierte dies immer noch in etwa einer Minute).

Der Vergleich dieser In-Place-Operationen mit der Swap-and-Drop-Operation ist bei Verwendung eines Liniendiagramms aufgrund des massiven Maßstabsunterschieds schwierig. Stattdessen zeige ich ein horizontales Balkendiagramm für die Dauer jeder Tabellenkonfiguration. Wenn die Neuerstellung schneller ist, male ich den Hintergrund dieser Zeile grün; Wenn es langsamer ist (oder zwischen Offline- und Online-Methode liegt), muss ich es wahrscheinlich nicht, aber ich male den Hintergrund dieser Zeile rot.

LOB-Größe | Ansatz | Tabellenkonfiguration Dauer (Sekunden)
Nur ID ALTER offline Skinnier-Tabelle (10 MB) 8.8
Breitere Tabelle (30 MB) 6.3
ÄNDERN Online Dünnerer Tisch 11.0
Breitere Tabelle 13.6
Neu erstellen Dünnerer Tisch 3.4
varchar 4K Offline Skinnier-Tabelle (390 MB) 16.6
Breitere Tabelle (780 MB) 14.0
Online Dünnerer Tisch 30.4
Breitere Tabelle 48,6
Neu erstellen Dünnerer Tisch 1.290,0
maximal 4k Offline Skinnier-Tabelle (390 MB) 33.1
Breitere Tabelle (780 MB) 32.1
Online Dünnerer Tisch 81,9
Breitere Tabelle 103.3
Neu erstellen Dünnerer Tisch 28.9
maximal 16.000 Offline Dünnere Tabelle (1,6 GB) 53,3
Breitere Tabelle (1,7 GB) 46,7
Online Dünnerer Tisch 130,9
Breitere Tabelle 150,2
Neu erstellen Dünnerer Tisch 81.8
maximal 64 KB Offline Skinnier-Tabelle (7,0 GB) 51,5
Breitere Tabelle (7,1 GB) 58,5
Online Dünnerer Tisch 136,5
Breitere Tabelle 152,6
Neu erstellen Dünnerer Tisch 226,5
maximal 256.000 Offline Dünnere Tabelle (25,8 GB) 60,9
Breitere Tabelle (25,9 GB) 61,3
Online Dünnerer Tisch 149.1
Breitere Tabelle 197.1
Neu erstellen Dünnerer Tisch 1.576,7

Dies ist eine unfaire Erschütterung von Andys Methode, da Sie – in der realen Welt – nicht die gesamte Operation auf einmal durchführen würden. Ich habe die Nutzung des Transaktionsprotokolls hier der Kürze halber nicht gezeigt, aber es wäre einfacher, dies auch durch Batching in einem Side-by-Side-Vorgang zu kontrollieren. Während sein Ansatz im Vorfeld mehr Arbeit erfordert, ist er in Bezug auf Ausfallzeiten und/oder Blockierungen viel sicherer. Aber Sie können sehen, dass in Fällen, in denen Sie viele Off-Row-Daten haben und sich einen kurzen Ausfall leisten können, das direkte Ändern der Spalte viel weniger schmerzhaft ist. „Zu groß, um an Ort und Stelle geändert zu werden“ ist subjektiv und kann zu unterschiedlichen Ergebnissen führen, je nachdem, was „groß“ bedeutet. Bevor Sie sich auf einen Ansatz festlegen, kann es sinnvoll sein, die Änderung anhand einer angemessenen Kopie zu testen, da der Vor-Ort-Vorgang möglicherweise einen akzeptablen Kompromiss darstellt.

Schlussfolgerung

Ich habe das nicht geschrieben, um mit Andy zu streiten. Der Ansatz im ursprünglichen Beitrag ist solide, 100% zuverlässig und wir verwenden ihn ständig. Wenn rohe Gewalt wichtiger ist als chirurgische Präzision, und vor allem, wenn Sie sich ein Stück Ausfallzeit nehmen können, kann der einfachere Ansatz für bestimmte Tischformen wertvoll sein.