Schemaaktualisierungen vornehmen

Mit Spanner lassen sich Schemas ohne Ausfallzeit aktualisieren. Das Schema einer vorhandenen Datenbank kann mit verschiedenen Methoden aktualisiert werden:

Unterstützte Schemaaktualisierungen

Spanner unterstützt die folgenden Schemaaktualisierungen einer vorhandenen Datenbank:

  • Erstellen einer neuen Tabelle Spalten in neuen Tabellen können NOT NULL sein.
  • Eine Tabelle löschen, sofern keine anderen Tabellen damit verschränkt sind und die zu löschende Tabelle keine sekundären Indexe hat.
  • Erstellen oder löschen Sie eine Tabelle mit einem Fremdschlüssel.
  • Hinzufügen oder entfernen eines Fremdschlüssels aus einer vorhandenen Tabelle.
  • Eine Nicht-Schlüsselspalte in eine Tabelle aufnehmen. Neue Nicht-Schlüsselspalten dürfen nicht NOT NULL sein.
  • Eine Nicht-Schlüsselspalte aus einer Tabelle löschen, sofern sie nicht von einem sekundären Index, einem Fremdschlüssel, einer gespeicherten generierten Spalte oder einer Diagnoseeinschränkung verwendet wird.
  • NOT NULL in eine Nicht-Schlüsselspalte aufnehmen und dabei ARRAY-Spalten ausschließen.
  • NOT NULL aus einer Nicht-Schlüsselspalte entfernen.
  • Eine Spalte des Typs STRING in eine Spalte des Typs BYTES ändern oder umgekehrt (BYTES in STRING).
  • Die Längenbeschränkung für einen STRING- oder BYTES-Typ erhöhen oder verringern (einschließlich auf MAX), es sei denn, es ist eine Primärschlüsselspalte, die von einer oder mehreren untergeordneten Tabellen übernommen werden.
  • Commit-Zeitstempel in Wert- und Primärschlüsselspalten aktivieren oder deaktivieren.
  • Fügen Sie einen sekundären Index hinzu oder entfernen Sie ihn.
  • Diagnoseeinschränkung zu einer vorhandenen Tabelle hinzufügen oder daraus entfernen.
  • Gespeicherte generierte Spalte zu einer vorhandenen Tabelle hinzufügen oder daraus entfernen.
  • Erstellen Sie ein neues Statistikpaket für die Optimierung.

Leistung während der Schemaaktualisierung

Schemaaktualisierungen in Spanner erfordern keine Ausfallzeiten. Wenn Sie einen Batch von DDL-Anweisungen an eine Spanner-Datenbank absetzen, können Sie ohne Unterbrechung weiter aus der Datenbank schreiben und lesen, während Spanner die Aktualisierung als Vorgang mit langer Ausführungszeit ausführt.

Die Ausführungsdauer einer DDL-Anweisung hängt davon ab, ob die Aktualisierung eine Validierung von vorhandenen Daten oder einen Daten-Backfill voraussetzt. Wenn Sie beispielsweise die Annotation NOT NULL in eine vorhandene Spalte einfügen, muss Spanner alle Werte in der Spalte lesen, damit die Spalte keine NULL-Werte enthält. Dieser Schritt kann lang dauern, wenn viele Daten validiert werden müssen. Ein weiteres Beispiel ist das Hinzufügen eines Index zu einer Datenbank: Spanner führt einen Backfill des Index mit vorhandenen Daten durch. Dieser Prozess kann je nach Definition des Index und Größe der entsprechenden Basistabelle viel Zeit in Anspruch nehmen. Wenn Sie jedoch einer Tabelle eine neue Spalte hinzufügen, müssen keine vorhandenen Daten validiert werden, sodass Spanner die Aktualisierung schneller durchführen kann.

Zusammenfassend lässt sich sagen, dass Schemaaktualisierungen, bei denen Spanner keine vorhandenen Daten validieren muss, in wenigen Minuten erfolgen. Schemaaktualisierungen mit Validierung können je nach Menge der zu validierenden vorhandenen Daten länger dauern. Allerdings erfolgt die Datenvalidierung im Hintergrund mit niedrigerer Priorität als Produktionstraffic. Schemaaktualisierungen, die eine Datenvalidierung voraussetzen, werden im nächsten Abschnitt ausführlicher erläutert.

Schemaaktualisierungen, die mit Ansichtsdefinitionen validiert werden

Wenn Sie eine Schemaaktualisierung vornehmen, prüft Spanner, ob die Abfragen, die zum Definieren vorhandener Ansichten verwendet werden, durch die Aktualisierung ungültig werden. Ist die Validierung erfolgreich, verläuft auch die Schemaaktualisierung erfolgreich. Wenn die Validierung nicht erfolgreich ist, schlägt die Schemaaktualisierung fehl. Weitere Informationen finden Sie unter Best Practices beim Erstellen von Ansichten.

Schemaaktualisierungen mit erforderlicher Datenvalidierung

Sie können Schemaaktualisierungen vornehmen, bei denen überprüft werden muss, ob die vorhandenen Daten den neuen Einschränkungen entsprechen. Wenn eine Schemaaktualisierung eine Datenvalidierung erfordert, erlaubt Spanner keine in Konflikt stehenden Schemaaktualisierungen für die betroffenen Schemaentitäten und validiert die Daten im Hintergrund. Ist die Validierung erfolgreich, verläuft auch die Schemaaktualisierung erfolgreich. Wenn die Validierung nicht erfolgreich ist, gilt das auch für die Schemaaktualisierung. Validierungsvorgänge sind lang laufende Vorgänge. Anhand des Status dieser Vorgänge können Sie feststellen, ob sie erfolgreich beendet wurden oder fehlgeschlagen sind.

Beispiel: Sie haben in Ihrem Schema die Tabelle Songwriters definiert:

GoogleSQL

CREATE TABLE Songwriters (
  Id         INT64 NOT NULL,
  FirstName  STRING(1024),
  LastName   STRING(1024),
  Nickname   STRING(MAX),
  OpaqueData BYTES(MAX),
) PRIMARY KEY (Id);

In diesem Fall sind folgende Schemaaktualisierungen zulässig, die jedoch validiert werden müssen und je nach Menge der vorhandenen Daten länger dauern können:

  • Die Annotation NOT NULL in eine Nicht-Schlüsselspalte aufnehmen. Beispiel:

    ALTER TABLE Songwriters ALTER COLUMN Nickname STRING(MAX) NOT NULL;
    
  • Die Länge einer Spalte verringern. Beispiel:

    ALTER TABLE Songwriters ALTER COLUMN FirstName STRING(10);
    
  • BYTES in STRING ändern. Beispiel:

    ALTER TABLE Songwriters ALTER COLUMN OpaqueData STRING(MAX);
    
  • Commit-Zeitstempel für eine vorhandene TIMESTAMP-Spalte aktivieren. Beispiel:

    ALTER TABLE Albums ALTER COLUMN LastUpdateTime SET OPTIONS (allow_commit_timestamp = true);
    
  • Diagnoseeinschränkung zu einer vorhandenen Tabelle hinzufügen

  • Gespeicherte generierte Spalte zu einer vorhandenen Tabelle hinzufügen

  • Eine neue Tabelle mit einem Fremdschlüssel erstellen

  • Einer vorhandenen Tabelle einen Fremdschlüssel hinzufügen

Diese Schemaaktualisierungen schlagen fehl, wenn die zugrunde liegenden Daten den neuen Einschränkungen nicht entsprechen. Beispielsweise schlägt die obige Anweisung ALTER TABLE Songwriters ALTER COLUMN Nickname STRING(MAX) NOT NULL fehl, wenn in der Spalte Nickname ein Wert NULL ist. In diesem Fall entsprechen die vorhandenen Daten nicht der Einschränkung NOT NULL der neuen Definition.

Die Datenvalidierung kann mehrere Minuten bis viele Stunden andauern. Die Dauer der Datenvalidierung hängt von folgenden Faktoren ab:

  • Größe des Datasets
  • Rechenkapazität der Instanz
  • Auslastung der Instanz

Einige Schemaaktualisierungen können das Verhalten von Anfragen an die Datenbank während der Schemaaktualisierung ändern. Wenn Sie beispielsweise NOT NULL in eine Spalte aufnehmen, beginnt Spanner fast sofort damit, Schreibvorgänge für neue Anfragen abzulehnen, in denen NULL für die Spalte verwendet wird. Sollte die neue Schemaaktualisierung aufgrund der Datenvalidierung letztendlich fehlschlagen, wurden Schreibvorgänge in einem gewissen Zeitraum blockiert, auch wenn sie vom alten Schema akzeptiert worden wären.

Sie können einen lang andauernden Datenvalidierungsprozess mit der Methode projects.instances.databases.operations.cancel oder mit gcloud spanner operations abbrechen.

Ausführungsreihenfolge von Anweisungen in Batches

Wenn Sie die Google Cloud CLI, die REST API oder die RPC API verwenden, können Sie einen Batch von einer oder mehreren CREATE-, ALTER- oder DROP-Anweisungen absetzen.

Spanner wendet Anweisungen aus demselben Batch der Reihe nach an und stoppt beim ersten Fehler. Wenn die Anwendung einer Anweisung zu einem Fehler führt, wird diese Anweisung zurückgesetzt. Die Ergebnisse von vorher angewendeten Anweisungen im Batch bleiben jedoch erhalten.

Spanner kann Anweisungen aus verschiedenen Batches kombinieren und neu anordnen. Dies kann dazu führen, dass Anweisungen aus verschiedenen Batches zu einer atomaren Änderung zusammengefasst werden, die auf die Datenbank angewendet wird. Die Ausführung von Anweisungen aus verschiedenen Batches erfolgt in jeder atomaren Änderung in zufälliger Reihenfolge. Wenn beispielsweise ein Batch von Anweisungen ALTER TABLE MyTable ALTER COLUMN MyColumn STRING(50) und ein anderer ALTER TABLE MyTable ALTER COLUMN MyColumn STRING(20) enthält, belässt Spanner diese Spalte in einem dieser beiden Status, wobei nicht angegeben ist, in welchem.

Während der Schemaaktualisierung erstellte Schemaversionen

Spanner verwendet die Schemaversionsverwaltung, damit es während einer Schemaaktualisierung auf eine große Datenbank nicht zu Ausfallzeiten kommt. Spanner behält die ältere Schemaversion bei, um Lesevorgänge zu unterstützen, während die Schemaaktualisierung verarbeitet wird. Spanner erstellt dann eine oder mehrere neue Versionen des Schemas, um die Schemaaktualisierung zu verarbeiten. Jede Version enthält das Ergebnis einer Gruppe von Anweisungen in einer einzelnen atomaren Änderung, wie oben beschrieben.

Die Schemaversionen stimmen nicht zwangsläufig eins zu eins mit Batches von DDL-Anweisungen oder einzelnen DDL-Anweisungen überein. Manche einzelne DDL-Anweisungen führen zu mehreren Schemaversionen, beispielsweise die Indexerstellung für vorhandene Basistabellen oder Anweisungen, die eine Datenvalidierung erfordern. In anderen Fällen können mehrere DDL-Anweisungen in einer einzigen Version zusammengefasst sein. Alte Schemaversionen können erhebliche Server- und Speicherressourcen verbrauchen und werden aufbewahrt, bis sie abgelaufen bzw. nicht mehr erforderlich sind, um Lesevorgänge von älteren Datenversionen bereitzustellen.

Die folgende Tabelle zeigt, wie lange Spanner zum Aktualisieren eines Schemas benötigt.

Schemavorgang Geschätzte Dauer
CREATE TABLE Minuten
CREATE INDEX

Minuten bis Stunden, wenn die Basistabelle vor dem Index erstellt wird.

Minuten, wenn die Anweisung gleichzeitig mit der Anweisung CREATE TABLE für die Basistabelle ausgeführt wird.

DROP TABLE Minuten
DROP INDEX Minuten
ALTER TABLE ... ADD COLUMN Minuten
ALTER TABLE ... ALTER COLUMN

Minuten bis Stunden, wenn eine Validierung im Hintergrund erforderlich ist.

Minuten, wenn keine Validierung im Hintergrund erforderlich ist.

ALTER TABLE ... DROP COLUMN Minuten
ANALYZE

Minuten bis Stunden, je nach Datenbankgröße.

Datentypänderungen und Änderungsstreams

Wenn Sie den Datentyp einer Spalte ändern, die von einem Änderungsstream beobachtet wird, spiegelt das Feld column_types der relevanten nachfolgenden Änderungsstreameinträge den neuen Typ wider. Dies gilt auch für die old_values-JSON-Daten im Feld mods der Datensätze.

Der new_values des Felds mods eines Änderungsstreameintrags entspricht immer dem aktuellen Typ einer Spalte. Das Ändern des Datentyps einer beobachteten Spalte wirkt sich nicht auf Änderungsstream-Einträge aus, die dieser Änderung vorausgehen.

Im speziellen Fall einer Änderung von BYTES zu STRING validiert Spanner die alten Werte der Spalte im Rahmen der Schemaaktualisierung. Daher hat Spanner die alten Werte vom Typ BYTES sicher in Strings decodiert, bis alle nachfolgenden Änderungsstreameinträge geschrieben werden.

Best Practices für Schemaaktualisierungen

In den folgenden Abschnitten werden Best Practices für das Aktualisieren von Schemas beschrieben.

Vor der Schemaaktualisierung auszuführende Schritte

Führen Sie vor dem Aktualisieren eines Schemas folgende Schritte aus:

  • Überprüfen Sie, ob alle vorhandenen Daten in der zu ändernden Datenbank den Einschränkungen entsprechen, die die Schemaaktualisierung auferlegt. Da die erfolgreiche Ausführung von manchen Arten von Schemaaktualisierungen von den Daten in der Datenbank und nicht nur vom aktuellen Schema abhängt, garantiert eine erfolgreiche Schemaaktualisierung einer Testdatenbank nicht zwangsläufig, dass auch das Schema einer Produktionsdatenbank erfolgreich aktualisiert wird. Hier ein paar gängige Beispiele:

    • Achten Sie beim Hinzufügen der Annotation NOT NULL zu einer vorhandenen Spalte darauf, dass in der Spalte keine NULL-Werte vorhanden sind.
    • Achten Sie beim Verringern der zulässigen Länge einer STRING- oder BYTES-Spalte darauf, dass alle vorhandenen Werte in dieser Spalte der gewünschten Längenbeschränkung entsprechen.
  • Wenn Sie in Spalten, Tabellen oder Indexe schreiben, die gerade einer Schemaaktualisierung unterzogen werden, sollten Sie prüfen, ob die von Ihnen geschriebenen Werte den neuen Einschränkungen entsprechen.

  • Achten Sie beim Löschen von Spalten, Tabellen oder Indexen darauf, dass Sie dafür keine Schreib- oder Lesevorgänge mehr ausführen.

Häufigkeit von Schemaaktualisierungen begrenzen

Wenn Sie zu viele Schemaaktualisierungen in einem kurzen Zeitraum ausführen, throttle kann Spanner die Verarbeitung der Schemaaktualisierungen in der Warteschlange throttle durchführen. Dies liegt daran, dass Spanner den Speicherplatz zum Speichern von Schemaversionen begrenzt. Die Schemaaktualisierung kann gedrosselt werden, wenn innerhalb der Aufbewahrungsdauer zu viele alte Schemaversionen vorhanden sind. Die maximale Rate von Schemaänderungen hängt von vielen Faktoren ab, darunter die Gesamtzahl der Spalten in der Datenbank. Beispielsweise kann eine Datenbank mit 2.000 Spalten (ungefähr 2.000 Zeilen in INFORMATION_SCHEMA.COLUMNS) innerhalb der Aufbewahrungsdauer maximal 1.500 einfache Schemaänderungen vornehmen (weniger, wenn für die Schemaänderung mehrere Versionen erforderlich sind). Verwenden Sie den Befehl gcloud spanner operations list und filtern Sie nach Vorgängen vom Typ DATABASE_UPDATE_DDL, um den Status laufender Schemaaktualisierungen anzeigen zu lassen. Wenn Sie eine laufende Schemaaktualisierung abbrechen möchten, verwenden Sie den Befehl gcloud spanner operations cancel und geben Sie die Vorgangs-ID an.

Die Anzahl der resultierenden Schemaversionen kann sich darauf auswirken, wie Ihre DDL-Anweisungen im Batch zusammengefasst sind und welche Reihenfolge sie in den einzelnen Batches haben. Um die Anzahl der Schemaaktualisierungen zu maximieren, die Sie in einem bestimmten Zeitraum durchführen können, sollten Sie die Batchverarbeitung verwenden, um die Anzahl der Schemaversionen zu minimieren. Einige Faustregeln werden unter umfangreiche Updates beschrieben.

Wie unter Schemaversionen beschrieben, erstellen einige DDL-Anweisungen mehrere Schemaversionen. Diese spielen für Überlegungen zum Batching und der Reihenfolge in jedem Batch eine wichtige Rolle. Es gibt zwei Haupttypen von Anweisungen, die möglicherweise mehrere Schemaversionen erstellen:

  • Anweisungen, die möglicherweise einen Indexdaten-Backfill durchführen müssen, z. B. CREATE INDEX
  • Anweisungen, die möglicherweise vorhandene Daten validieren, müssen z. B. das Hinzufügen von NOT NULL

Diese Arten von Anweisungen erstellen jedoch nicht immer mehrere Schemaversionen. Spanner versucht zu erkennen, wann diese Arten von Anweisungen optimiert werden können, um die Verwendung mehrerer Schemaversionen zu vermeiden, was von Batching abhängig ist. Beispielsweise kann eine CREATE INDEX-Anweisung, die im selben Batch wie eine CREATE TABLE-Anweisung für die Basistabelle des Index ohne dazwischenliegende Anweisungen für andere Tabellen ausgeführt wird, vermeiden, dass ein Backfill der Indexdaten erforderlich ist, da Spanner garantieren kann, dass die Basistabelle zum Zeitpunkt der Indexerstellung leer ist. Im Abschnitt Umfassende Updates wird beschrieben, wie Sie mit dieser Eigenschaft effizient viele Indexe erstellen können.

Wenn Sie die DDL-Anweisungen nicht im Batch verarbeiten können, um das Erstellen von vielen Schemaversionen zu vermeiden, sollten Sie die Anzahl der Schemaaktualisierungen innerhalb der Aufbewahrungsdauer für das Schema einer einzelnen Datenbank beschränken. Erhöhen Sie das Zeitfenster für Schemaaktualisierungen, damit Spanner alte Versionen des Schemas entfernen kann, bevor neue Versionen erstellt werden.

  • Für einige relationale Datenbankverwaltungssysteme gibt es Softwarepakete, die bei jeder Produktionsbereitstellung eine lange Reihe von Upgrade- und Downgrade-Schemaaktualisierungen auf die Datenbank anwenden. Diese Prozesstypen werden für Spanner nicht empfohlen.
  • Spanner ist für die Verwendung von Primärschlüsseln zum Partitionieren von Daten für Lösungen mit Mehrinstanzenfähigkeit optimiert. Lösungen mit Mehrinstanzfähigkeit, die eigene Tabellen für jeden Kunden verwenden, können dazu führen, dass ein großer Rückstand an Schemaaktualisierungsvorgängen entsteht, der nur langsam abgearbeitet wird.
  • Schemaaktualisierungen, die eine Validierung oder einen Index-Backfill erfordern, verwenden mehr Serverressourcen, da jede Anweisung intern mehrere Versionen des Schemas erstellt.

Optionen für umfangreiche Schemaaktualisierungen

Die beste Methode zum Erstellen einer Tabelle und einer großen Anzahl von Indexen für diese Tabelle besteht darin, alles zur selben Zeit zu erstellen, sodass nur eine Schemaversion erstellt wird. Es wird empfohlen, die Indexe unmittelbar nach der Tabelle in der Liste der DDL-Anweisungen zu erstellen. Sie können die Tabelle und ihre Indexe gleichzeitig mit der Datenbank oder als einzelnen großen Batch Anweisungen erstellen. Wenn Sie viele Tabellen mit jeweils vielen Indexen erstellen müssen, können Sie alle Anweisungen in einem einzigen Batch zusammenfassen. Sie können mehrere tausend Anweisungen in einem einzigen Batch zusammenfassen, wenn alle Anweisungen mithilfe einer einzigen Schemaversion gemeinsam ausgeführt werden können.

Wenn eine Anweisung einen Backfill für Indexdaten erfordert oder eine Datenvalidierung durchführt, kann sie nicht in einer einzelnen Schemaversion ausgeführt werden. Dies geschieht bei CREATE INDEX-Anweisungen, wenn die Basistabelle des Index bereits vorhanden ist, entweder weil sie in einem vorherigen Batch von DDL-Anweisungen erstellt wurde oder weil im Batch zwischen den Anweisungen CREATE TABLE und CREATE INDEX eine Anweisung vorhanden war, für die mehrere Schemaversionen erforderlich waren. Für Spanner dürfen nicht mehr als zehn solcher Anweisungen in einem Batch vorhanden sein. Insbesondere die Indexerstellung, die Backfilling erfordert, verwendet mehrere Schemaversionen pro Index. Daher gilt als Faustregel, pro Tag nicht mehr als drei neue Indexe zu erstellen, die Backfilling erfordern. Dabei ist es unerheblich, wie das Batching erfolgt, es sei denn, Backfilling kann dadurch verhindert werden.

Dieser Batch Anweisungen verwendet beispielsweise eine einzige Schemaversion:

GoogleSQL

CREATE TABLE Singers (
SingerId   INT64 NOT NULL,
FirstName  STRING(1024),
LastName   STRING(1024),
) PRIMARY KEY (SingerId);

CREATE INDEX SingersByFirstName ON Singers(FirstName);

CREATE INDEX SingersByLastName ON Singers(LastName);

CREATE TABLE Albums (
SingerId   INT64 NOT NULL,
AlbumId    INT64 NOT NULL,
AlbumTitle STRING(MAX),
) PRIMARY KEY (SingerId, AlbumId);

CREATE INDEX AlbumsByTitle ON Albums(AlbumTitle);

Im Gegensatz dazu verwendet dieser Batch viele Schemaversionen, weil UnrelatedIndex Backfilling erfordert (da seine Basistabelle bereits vorhanden sein musste) und dadurch werden alle folgenden Indexe gezwungen, ebenfalls Backfilling zu erfordern (auch wenn sie sich im selben Batch wie ihre Basistabellen befinden):

GoogleSQL

CREATE TABLE Singers (
SingerId   INT64 NOT NULL,
FirstName  STRING(1024),
LastName   STRING(1024),
) PRIMARY KEY (SingerId);

CREATE TABLE Albums (
SingerId   INT64 NOT NULL,
AlbumId    INT64 NOT NULL,
AlbumTitle STRING(MAX),
) PRIMARY KEY (SingerId, AlbumId);

CREATE INDEX UnrelatedIndex ON UnrelatedTable(UnrelatedIndexKey);

CREATE INDEX SingersByFirstName ON Singers(FirstName);

CREATE INDEX SingersByLastName ON Singers(LastName);

CREATE INDEX AlbumsByTitle ON Albums(AlbumTitle);

Es ist besser, die Erstellung von UnrelatedIndex an das Ende des Batches oder in einen anderen Batch zu verschieben, um die Anzahl der Schemaversionen zu minimieren.

Auf Abschluss von API-Anfragen warten

Verwenden Sie beim Senden der Anfragen projects.instances.databases.updateDdl (REST API) oder UpdateDatabaseDdl (RPC API) jeweils projects.instances.databases.operations.get (REST API) oder GetOperation (RPC API), um auf den Abschluss der einzelnen Anfragen zu warten, bevor eine neue Anfrage gestartet wird. Ihre Anwendung kann dann den Fortschritt der Schemaaktualisierungen verfolgen. Außerdem bleibt der Rückstand von ausstehenden Schemaaktualisierungen in diesem Fall überschaubar.

Im Bulk laden

Wenn Sie nach dem Erstellen von Tabellen Daten im Bulk in die Tabellen laden, ist es in der Regel effizienter, Indexe nach dem Laden der Daten zu erstellen. Beim Hinzufügen mehrerer Indexe kann es sich als effizienter erweisen, die Datenbank mit allen Tabellen und Indexen im Anfangsschema zu erstellen, wie unter Optionen für umfangreiche Aktualisierungen beschrieben.