Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

Neue Änderungen an Nur-Metadaten-Spalten in SQL Server 2016

Die ALTER TABLE ... ALTER COLUMN Befehl ist sehr mächtig. Sie können es verwenden, um den Datentyp, die Länge, die Genauigkeit, die Skalierung, die Nullzulässigkeit, die Sortierung usw. einer Spalte zu ändern.

Es ist sicherlich bequemer als die Alternative:Bei jeder notwendigen Änderung eine neue Tabelle anlegen und die Daten migrieren. Dennoch kann nur begrenzt viel getan werden, um die zugrunde liegende Komplexität zu verbergen. Neben vielen Einschränkungen, was mit diesem Befehl überhaupt möglich ist, stellt sich immer die Frage nach der Performance.

Letztendlich werden Tabellen als eine Folge von Bytes mit einigen Metadaten an anderer Stelle im System gespeichert, um zu beschreiben, was jedes dieser Bytes bedeutet und wie sie sich auf die verschiedenen Spalten der Tabelle beziehen. Wenn wir SQL Server auffordern, einen Aspekt der Definition einer Spalte zu ändern, muss überprüft werden, ob die vorhandenen Daten mit der neuen Definition kompatibel sind. Außerdem muss festgestellt werden, ob das aktuelle physische Layout geändert werden muss.

Abhängig von der Art der Änderung und der Konfiguration der Datenbank, ein ALTER COLUMN Befehl muss eine der folgenden Aktionen ausführen:

  1. Metadaten nur in Systemtabellen ändern.
  2. Überprüfen Sie alle vorhandenen Daten auf Kompatibilität und ändern Sie dann die Metadaten.
  3. Schreiben Sie einige oder alle gespeicherten Daten um, damit sie der neuen Definition entsprechen.

Option 1 stellt aus Performance-Sicht den Idealfall dar. Es erfordert nur wenige Änderungen an Systemtabellen und eine minimale Protokollierung. Die Operation erfordert weiterhin eine restriktive Schemaänderung Sch-M lock, aber die Metadatenänderungen selbst werden sehr schnell abgeschlossen, unabhängig von der Größe der Tabelle.

Nur-Metadaten-Änderungen

Es gibt eine Reihe von Sonderfällen, auf die Sie achten müssen, aber als allgemeine Zusammenfassung erfordern die folgenden Aktionen nur Änderungen an Metadaten:

  • Ausgehend von NOT NULL auf NULL für denselben Datentyp.
  • Erhöhung der maximalen Größe eines varchar , nvarchar , oder varbinary Spalte (außer bis max ).

Verbesserungen in SQL Server 2016

Das Thema dieses Beitrags sind die zusätzlichen Änderungen, die ab SQL Server 2016 nur für Metadaten aktiviert sind . Es sind keine Änderungen an der Syntax erforderlich, und es müssen keine Konfigurationseinstellungen geändert werden. Sie erhalten diese undokumentierten Verbesserungen kostenlos.

Die neuen Funktionen zielen auf eine Teilmenge der Festlänge ab Datentypen. Die neuen Fähigkeiten gelten unter den folgenden Umständen für Zeilenspeichertabellen:

  • Komprimierung muss aktiviert sein:
    • Auf allen Indizes und Partitionen , einschließlich des Basisheaps oder Clustered-Index.
    • Entweder ROW oder PAGE Komprimierung.
    • Indizes und Partitionen können eine Mischung verwenden dieser Kompressionsstufen. Wichtig ist, dass es keine unkomprimierten Indizes oder Partitionen gibt.
  • Wechsel von NULL zu NOT NULL ist nicht erlaubt .
  • Die folgenden ganzzahligen Änderungen werden unterstützt:
    • smallint zu integer oder bigint .
    • integer zu bigint .
    • smallmoney zu money (verwendet intern die Ganzzahldarstellung).
  • Die folgenden String- und Binärtypen ändern sich werden unterstützt:
    • char(n) zu char(m) oder varchar(m)
    • nchar(n) zu nchar(m) oder nvarchar(m)
    • binary(n) zu binary(m) oder varbinary(m)
    • Alles Obige nur für n < m und m != max
    • Sortierungsänderungen sind nicht erlaubt

Diese Änderungen können nur Metadaten sein, da sich das zugrunde liegende binäre Datenlayout nicht ändert, wenn Column Descriptor Zeilenformat verwendet (daher die Notwendigkeit der Komprimierung). Ohne Komprimierung verwendet der Zeilenspeicher die ursprüngliche FixedVar Darstellung, die diese Datentypänderungen mit fester Länge nicht aufnehmen kann, ohne das physische Layout neu zu schreiben.

Sie werden vielleicht bemerken, dass tinyint wird aus der Liste der Integer-Typen weggelassen. Dies liegt daran, dass es unsigniert ist, während die anderen Integer-Typen alle signiert sind, sodass eine Änderung nur der Metadaten nicht möglich ist. Beispielsweise kann ein Wert von 255 in ein Byte für tinyint passen , erfordert aber zwei Bytes in jedem der signierten Formate. Die vorzeichenbehafteten Formate können komprimiert -128 bis +127 in einem Byte enthalten.

Integer-Beispiel

Eine sehr praktische Anwendung dieser Verbesserung ist das Ändern des Datentyps einer Spalte mit dem IDENTITY Eigentum.

Angenommen, wir haben die folgende Heap-Tabelle mit Zeilenkomprimierung (Seitenkomprimierung würde auch funktionieren):

DROP TABLE IF EXISTS dbo.Test;
GO
CREATE TABLE dbo.Test
(
    id integer IDENTITY NOT NULL,
    some_value integer NOT NULL
)
WITH (DATA_COMPRESSION = ROW);

Lassen Sie uns 5 Millionen Datenzeilen hinzufügen. Dies reicht aus, um (vom Leistungsstandpunkt aus) deutlich zu machen, ob das Ändern des Spaltendatentyps eine reine Metadatenoperation ist oder nicht:

WITH Numbers AS
(
    SELECT 
        n = ROW_NUMBER() OVER (ORDER BY @@SPID) 
    FROM sys.all_columns AS AC1
    CROSS JOIN sys.all_columns AS AC2
    ORDER BY n
    OFFSET 0 ROWS
    FETCH FIRST 5 * 1000 * 1000 ROWS ONLY
)
INSERT dbo.Test
    WITH (TABLOCKX)
(
    some_value
)
SELECT
    N.n
FROM Numbers AS N;

Als nächstes werden wir die IDENTITY neu setzen um den Anschein zu erwecken, dass uns fast die Werte ausgehen, die in eine integer passen :

DBCC CHECKIDENT
(
    N'dbo.Test',
    RESEED,
    2147483646
);

Wir können erfolgreich eine weitere Zeile hinzufügen:

INSERT dbo.Test
    (some_value)
VALUES
    (123456);

Aber der Versuch, eine weitere Zeile hinzuzufügen:

INSERT dbo.Test
    (some_value)
VALUES
    (7890);

Führt zu einer Fehlermeldung:

Msg 8115, Level 16, State 1, Line 1
Arithmetischer Überlauffehler beim Konvertieren von IDENTITY in den Datentyp int.

Wir können das beheben, indem wir die Spalte in bigint konvertieren :

ALTER TABLE dbo.Test
ALTER COLUMN id bigint NOT NULL;

Dank der Verbesserungen in SQL Server 2016 ändert dieser Befehl nur Metadaten , und wird sofort abgeschlossen. Das vorherige INSERT -Anweisung (diejenige, die den arithmetischen Überlauffehler ausgelöst hat) wird nun erfolgreich abgeschlossen.

Diese neue Fähigkeit löst nicht alle Probleme beim Ändern des Typs einer Spalte mit IDENTITY Eigentum. Wir müssen weiterhin alle Indizes für die Spalte löschen und neu erstellen, alle referenzierenden Fremdschlüssel neu erstellen und so weiter. Das würde den Rahmen dieses Beitrags etwas sprengen (obwohl Aaron Bertrand schon einmal darüber geschrieben hat). Die Möglichkeit, den Typ als reine Metadatenoperation zu ändern, schadet sicherlich nicht. Bei sorgfältiger Planung können die anderen erforderlichen Schritte so effizient wie möglich gestaltet werden, beispielsweise durch minimal protokollierte oder ONLINE Operationen.

Seien Sie vorsichtig mit der Syntax

Achten Sie darauf, immer Geben Sie NULL an oder NOT NULL beim Ändern von Datentypen mit ALTER COLUMN . Angenommen, wir wollten auch den Datentyp von some_value ändern Spalte in unserer Testtabelle von integer NOT NULL zu bigint NOT NULL .

Wenn wir den Befehl schreiben, lassen wir NULL weg oder NOT NULL Qualifizierer:

ALTER TABLE dbo.Test
ALTER COLUMN some_value bigint;

Dieser Befehl wird erfolgreich als Nur-Metadaten-Änderung abgeschlossen, entfernt aber auch NOT NULL Zwang. Die Spalte ist jetzt bigint NULL , was nicht unsere Absicht war. Dieses Verhalten ist dokumentiert, aber leicht zu übersehen.

Wir könnten versuchen, unseren Fehler zu beheben mit:

ALTER TABLE dbo.Test
ALTER COLUMN some_value bigint NOT NULL;

Das ist nicht eine reine Metadatenänderung. Wir dürfen nicht von NULL wechseln zu NOT NULL (Beziehen Sie sich auf die vorherige Tabelle, wenn Sie eine Auffrischung der Bedingungen benötigen). SQL Server muss alle vorhandenen Werte überprüfen, um sicherzustellen, dass keine Nullen vorhanden sind. Es wird dann physisch jede Zeile neu schreiben des Tisches. Diese Aktionen sind nicht nur an sich langsam, sondern erzeugen auch eine Menge Transaktionsprotokolle, die Folgewirkungen haben können.

Nebenbei bemerkt, derselbe Fehler ist für Spalten mit dem IDENTITY nicht möglich Eigentum. Wenn wir ein ALTER COLUMN schreiben Anweisung ohne NULL oder NOT NULL in diesem Fall geht die Engine hilfreicherweise davon aus, dass wir NOT NULL gemeint haben da die Identitätseigenschaft für Nullable-Spalten nicht zulässig ist. Es ist dennoch eine gute Idee, sich nicht auf dieses Verhalten zu verlassen.

Geben Sie immer NULL an oder NOT NULL mit ALTER COLUMN .

Sortierung

Besondere Vorsicht ist geboten, wenn eine Zeichenfolgenspalte geändert wird, deren Sortierung nicht mit der Standardeinstellung für die Datenbank übereinstimmt.

Nehmen wir zum Beispiel an, wir haben eine Tabelle mit einer Kollatierung, bei der zwischen Groß- und Kleinschreibung und Akzenten unterschieden wird (vorausgesetzt, der Datenbankstandard ist anders):

DROP TABLE IF EXISTS dbo.Test2;
GO
CREATE TABLE dbo.Test2
(
    id integer IDENTITY NOT NULL,
    some_string char(8) COLLATE Latin1_General_100_CS_AS NOT NULL
)
WITH (DATA_COMPRESSION = ROW);

Fügen Sie 5 Millionen Datenzeilen hinzu:

WITH Numbers AS
(
    SELECT 
        n = ROW_NUMBER() OVER (ORDER BY @@SPID) 
    FROM sys.all_columns AS AC1
    CROSS JOIN sys.all_columns AS AC2
    ORDER BY n
    OFFSET 0 ROWS
    FETCH FIRST 5 * 1000 * 1000 ROWS ONLY
)
INSERT dbo.Test2
    WITH (TABLOCKX)
(
    some_string
)
SELECT
    CONVERT(char(8), N.n) COLLATE Latin1_General_100_CS_AS
FROM Numbers AS N;

Verdoppeln Sie die Länge der String-Spalte mit dem folgenden Befehl:

ALTER TABLE dbo.Test2
ALTER COLUMN some_string char(16) NOT NULL;

Wir haben daran gedacht, NOT NULL anzugeben , aber die nicht standardmäßige Sortierung vergessen. SQL Server geht davon aus, dass wir die Sortierung auf den Datenbankstandard ändern wollten (Latin1_General_CI_AS für meine Testdatenbank). Durch das Ändern der Sortierung wird verhindert, dass die Operation nur Metadaten enthält, und daher wird die Operation mehrere Minuten lang ausgeführt und erzeugt Unmengen von Protokollen.

Erstellen Sie die Tabelle und die Daten mit dem vorherigen Skript neu und versuchen Sie es dann mit ALTER COLUMN erneut ausführen, aber die vorhandene nicht standardmäßige Sortierung als Teil des Befehls angeben:

ALTER TABLE dbo.Test2
ALTER COLUMN some_string 
    char(16) COLLATE Latin1_General_100_CS_AS NOT NULL;

Die Änderung wird jetzt sofort als reiner Metadatenvorgang abgeschlossen. Wie beim NULL und NOT NULL Syntax, lohnt es sich, explizit zu sein, um Unfälle zu vermeiden. Dies ist allgemein ein guter Rat, nicht nur für ALTER COLUMN .

Komprimierung

Bitte beachten Sie, dass die Komprimierung explizit für jeden Index und separat für die Basistabelle angegeben werden muss, wenn es sich um einen Heap handelt. Dies ist ein weiteres Beispiel, bei dem die Verwendung von abgekürzter Syntax oder Abkürzungen das gewünschte Ergebnis verhindern kann.

Beispielsweise gibt die folgende Tabelle keine explizite Komprimierung für den Primärschlüssel oder die Inline-Indexdefinition an:

CREATE TABLE dbo.Test
(
    id integer IDENTITY NOT NULL PRIMARY KEY,
    some_value integer NOT NULL
        INDEX [IX dbo.Test some_value]
)
WITH (DATA_COMPRESSION = PAGE);

Der PRIMARY KEY wird ein Name zugewiesen, standardmäßig CLUSTERED , und sei PAGE komprimiert. Der Inline-Index ist NONCLUSTERED und überhaupt nicht komprimiert. Diese Tabelle wird für keine der neuen Optimierungen aktiviert, da nicht alle Indizes und Partitionen komprimiert sind.

Eine viel bessere und explizitere Tabellendefinition wäre:

CREATE TABLE dbo.Test
(
    id integer IDENTITY NOT NULL
        CONSTRAINT [PK dbo.Test id]
        PRIMARY KEY CLUSTERED
        WITH (DATA_COMPRESSION = PAGE),
    some_value integer NOT NULL
        INDEX [IX dbo.Test some_value]
        NONCLUSTERED
        WITH (DATA_COMPRESSION = ROW)        
);

Diese Tabelle eignet sich für die neuen Optimierungen, da alle Indizes und Partitionen komprimiert sind. Wie bereits erwähnt, ist das Mischen von Komprimierungstypen in Ordnung.

Es gibt verschiedene Möglichkeiten, diese CREATE TABLE zu schreiben ausdrückliche Aussage, also gibt es ein Element persönlicher Vorlieben. Der wichtige Punkt zum Mitnehmen ist, immer explizit zu sein über das, was Sie wollen. Dies gilt für separates CREATE INDEX auch Aussagen.

Erweiterte Ereignisse und Trace-Flag

Es gibt ein erweitertes Ereignis speziell für das neue Nur-Metadaten-ALTER COLUMN Operationen, die ab SQL Server 2016 unterstützt werden.

Das erweiterte Ereignis ist compressed_alter_column_is_md_only im Debug Kanal. Seine Ereignisfelder sind object_id , column_id , und is_md_only (wahr/falsch).

Dieses Ereignis zeigt nur an, ob ein Vorgang aufgrund der neuen Fähigkeiten von SQL Server 2016 nur Metadaten ist. Spaltenänderungen, die vor 2016 nur Metadaten waren, zeigen is_md_only = false obwohl es immer noch nur Metadaten sind.

Andere erweiterte Ereignisse, die zum Verfolgen von ALTER COLUMN nützlich sind Operationen beinhalten metadata_ddl_alter_column und alter_column_event , beide in der Analyse Kanal.

Sollten Sie deaktivieren müssen die neuen SQL Server 2016-Funktionen aus irgendeinem Grund, das undokumentierte globale (oder Start-) Ablaufverfolgungsflag 3618 verwendet werden. Dieses Ablaufverfolgungsflag ist nicht wirksam, wenn es auf Sitzungsebene verwendet wird. Es gibt keine Möglichkeit, ein Trace-Flag auf Abfrageebene mit einem ALTER COLUMN anzugeben Befehl.

Abschließende Gedanken

Die Möglichkeit, einige ganzzahlige Datentypen fester Länge mit einer reinen Metadatenänderung zu ändern, ist eine sehr willkommene Produktverbesserung. Es erfordert zwar, dass die Tabelle bereits vollständig komprimiert ist, aber das wird sowieso immer häufiger. Dies gilt insbesondere, da die Komprimierung in allen Editionen ab SQL Server 2016 Service Pack 1 aktiviert wurde.

Spalten vom Typ Zeichenfolge mit fester Länge sind wahrscheinlich viel seltener. Einiges davon kann auf etwas veraltete Überlegungen wie die Speicherplatznutzung zurückzuführen sein. Wenn sie komprimiert sind, speichern Zeichenfolgenspalten mit fester Länge keine nachgestellten Leerzeichen, wodurch sie vom Speicherstandpunkt aus genauso effizient sind wie Zeichenfolgenspalten mit variabler Länge. Es kann lästig sein, Leerzeichen zur Manipulation oder Anzeige zu kürzen, aber wenn die Daten normalerweise den größten Teil der maximalen Länge einnehmen, können Typen mit fester Länge wichtige Vorteile haben, nicht zuletzt in Bezug auf Speicherzuteilungen für Dinge wie Sortierung und Hashing.

Es sind nicht alle guten Nachrichten, wenn die Komprimierung aktiviert ist. Ich habe bereits erwähnt, dass SQL Server manchmal eine reine Metadatenänderung durchführen kann, nachdem überprüft wurde, ob alle vorhandenen Werte erfolgreich in den neuen Typ konvertiert werden. Dies ist der Fall, wenn ALTER COLUMN verwendet wird um von integer zu wechseln zu smallint zum Beispiel. Leider sind diese Vorgänge derzeit nicht nur Metadaten für komprimierte Objekte.

Danksagungen

Besonderer Dank geht an Panagiotis Antonopoulos (Principal Software Engineer) und Mirek Sztajno (Senior Program Manager) vom SQL Server-Produktteam für ihre Unterstützung und Anleitung während der Recherche und dem Schreiben dieses Artikels.

Keine der in dieser Arbeit enthaltenen Details sollte als offizielle Microsoft-Dokumentation oder Produktaussage betrachtet werden.