Effectuer des mises à jour de schéma

Spanner vous permet d'effectuer des mises à jour de schéma sans temps d'arrêt. Vous pouvez mettre à jour le schéma d'une base de données existante de plusieurs manières:

Mises à jour de schéma compatibles

Spanner accepte les mises à jour de schéma suivantes pour une base de données existante:

  • Ajoutez ou supprimez un schéma nommé.
  • créer une table ; Les colonnes des nouvelles tables peuvent avoir la valeur NOT NULL.
  • Supprimer une table si elle ne comporte pas de table entrelacée et si elle n'a pas d'index secondaire.
  • Créer ou supprimer une table avec une clé étrangère
  • Ajouter ou supprimer une clé étrangère dans une table existante.
  • Ajouter une colonne non-clé à n'importe quelle table. Les nouvelles colonnes non-clés ne peuvent pas avoir la valeur NOT NULL.
  • Supprimer une colonne non-clé de n'importe quelle table, sauf si elle est utilisée par un index secondaire, une clé étrangère, une colonne générée stockée ou une contrainte de vérification.
  • Ajouter NOT NULL à une colonne non-clé, à l'exception des colonnes ARRAY.
  • Supprimer la valeur NOT NULL d'une colonne non-clé.
  • Remplacer une colonne STRING par une colonne BYTES, ou une colonne BYTES par une colonne STRING.
  • Remplacer une colonne PROTO par une colonne BYTES, ou une colonne BYTES par une colonne PROTO.
  • Modifiez le type de message proto d'une colonne PROTO.
  • Ajoutez des valeurs à une définition ENUM et renommez les valeurs existantes à l'aide de ALTER PROTO BUNDLE
  • Modifier les messages définis dans un élément PROTO BUNDLE de manière arbitraire, à condition que les champs modifiés de ces messages ne sont utilisés comme clés dans aucune table et que les données existantes satisfont aux nouvelles contraintes.
  • Augmenter ou diminuer la longueur maximale d'un type STRING ou BYTES (y compris la valeur MAX), sauf s'il s'agit d'une colonne de clé primaire héritée d'une ou de plusieurs tables enfants.
  • Augmentez ou diminuez la limite de longueur pour ARRAY<STRING>, ARRAY<BYTES>, ou ARRAY<PROTO> au maximum autorisé.
  • Activer ou désactiver les horodatages de commit dans les colonnes de valeur et de clé primaire.
  • Ajouter ou supprimer un index secondaire.
  • Ajouter ou supprimer une contrainte de vérification dans une table existante.
  • Ajouter ou supprimer une colonne générée stockée dans une table existante.
  • Créez un nouveau package de statistiques d'optimiseur.
  • créer et gérer des vues ;
  • Créer et gérer des séquences
  • Créer des rôles de base de données et octroyer des droits.
  • Définissez, modifiez ou supprimez la valeur par défaut d'une colonne.
  • Modifiez les options de la base de données (default_leader ou version_retention_period, par exemple).
  • créer et gérer des flux de modifications ;
  • Créer et gérer des modèles de ML

Mises à jour de schéma non compatibles

Spanner n'accepte pas les mises à jour de schéma suivantes d'une instance base de données:

  • Si un champ PROTO de type ENUM est référencé par une clé de table ou d'index, vous ne pouvez pas supprimer les valeurs ENUM du fichier proto et les énumérations. (Suppression des valeurs ENUM des énumérations utilisées par ENUM<> est compatible, y compris lorsqu'elles sont utilisées comme clés.)

Performances de la mise à jour de schéma

Les mises à jour de schéma dans Spanner ne nécessitent pas de temps d'arrêt. Lorsque vous émettez un d'instructions LDD dans une base de données Spanner, vous pouvez continuer à écrire et lire des données depuis la base de données sans interruption pendant que Spanner applique la mise à jour opération de longue durée.

La durée d'exécution d'une instruction DDL varie en fonction de la nécessité de valider les données existantes ou de remplir des données. Par exemple : si vous ajoutez l'annotation NOT NULL à une colonne existante, Spanner doit lisez toutes les valeurs de la colonne pour vous assurer qu'elle ne contient pas toutes les valeurs NULL. Cette étape peut être longue s'il y a beaucoup de données à valider. Autre exemple: Spanner remplit l'index à l'aide des données existantes. Ce processus peut peut prendre beaucoup de temps, selon la façon dont la définition de l'index et la taille table de base correspondante. Toutefois, si vous ajoutez une colonne à un tableau, aucune donnée existante à valider. Spanner peut donc effectuer rapidement.

En résumé, les mises à jour de schéma ne nécessitant pas de validation par Spanner les données existantes peuvent être réalisées en quelques minutes. Les mises à jour de schéma nécessitant une validation peuvent prendre plus longtemps, en fonction de la quantité de données existantes à valider. Cependant, la validation des données s'effectue en arrière-plan avec une priorité moindre que celle du trafic de production. Les mises à jour de schéma nécessitant une validation des données sont abordées plus en détail dans la section suivante.

Mises à jour de schéma validées par rapport aux définitions de vue

Lorsque vous effectuez une mise à jour de schéma, Spanner vérifie que la mise à jour n'invalide pas les requêtes utilisées pour définir des vues existantes. Si la validation réussit, la mise à jour du schéma réussit. Si la validation échoue, la mise à jour du schéma échoue. Consultez la section Bonnes pratiques lors de la création de vues pour plus de détails.

Mises à jour de schéma nécessitant une validation des données

Certaines mises à jour de schéma nécessitent de vérifier que les données existantes répondent aux nouvelles contraintes. Lorsqu'une mise à jour de schéma nécessite la validation des données, Spanner interdit les mises à jour de schéma en conflit avec le schéma concerné et valide les données en arrière-plan. Si la validation réussit, la mise à jour du schéma réussit. Si la validation échoue, la mise à jour du schéma échoue également. Les opérations de validation sont exécutées comme des opérations de longue durée. Vous pouvez vérifier le statut de ces opérations pour savoir si elles ont réussi ou échoué.

Par exemple, supposons que vous ayez défini le fichier music.proto suivant avec une Énumération RecordLabel et message de protocole Songwriter:

  enum RecordLabel {
    COOL_MUSIC_INC = 0;
    PACIFIC_ENTERTAINMENT = 1;
    XYZ_RECORDS = 2;
  }

  message Songwriter {
    required string nationality   = 1;
    optional int64  year_of_birth = 2;
  }

Pour ajouter une table Songwriters à votre schéma, procédez comme suit:

GoogleSQL

CREATE PROTO BUNDLE (
  googlesql.example.music.Songwriter,
  googlesql.example.music.RecordLabel,
);

CREATE TABLE Songwriters (
  Id         INT64 NOT NULL,
  FirstName  STRING(1024),
  LastName   STRING(1024),
  Nickname   STRING(MAX),
  OpaqueData BYTES(MAX),
  SongWriter googlesql.example.music.Songwriter
) PRIMARY KEY (Id);

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

Les mises à jour de schéma suivantes sont autorisées, mais elles nécessitent une validation et peuvent durer longtemps selon la quantité de données existantes :

  • Ajouter la valeur NOT NULL à une colonne non-clé. Exemple :

    ALTER TABLE Songwriters ALTER COLUMN Nickname STRING(MAX) NOT NULL;
    
  • Réduire la longueur d'une colonne. Exemple :

    ALTER TABLE Songwriters ALTER COLUMN FirstName STRING(10);
    
  • Remplacer le type BYTES par STRING. Exemple :

    ALTER TABLE Songwriters ALTER COLUMN OpaqueData STRING(MAX);
    
  • Remplacer le type INT64/INT32 par ENUM. Exemple :

    ALTER TABLE Albums ALTER COLUMN Label googlesql.example.music.RecordLabel;
    
  • Suppression des valeurs existantes de la définition d'énumération RecordLabel.

  • Activer les horodatages de commit sur une colonne TIMESTAMP existante. Exemple :

    ALTER TABLE Albums ALTER COLUMN LastUpdateTime SET OPTIONS (allow_commit_timestamp = true);
    
  • Ajouter une contrainte de vérification à une table existante.

  • Ajouter une colonne générée stockée à une table existante.

  • Créer une nouvelle table avec une clé étrangère.

  • Ajouter une clé étrangère à une table existante.

Ces mises à jour de schéma échouent si les données sous-jacentes ne répondent pas aux nouvelles contraintes. Par exemple, l'instruction ALTER TABLE Songwriters ALTER COLUMN Nickname STRING(MAX) NOT NULL échoue si l'une des valeurs de Nickname est NULL, car les données existantes ne correspondent pas aux critères NOT NULL de la nouvelle définition.

La validation des données peut prendre de quelques minutes à plusieurs heures. Le temps nécessaire à cette validation dépend des éléments suivants :

  • Taille de l'ensemble de données
  • La capacité de calcul de l'instance
  • La charge sur l'instance

Certaines mises à jour de schéma peuvent modifier le comportement des requêtes adressées à la base de données avant la fin de la mise à jour. Par exemple, si vous ajoutez NOT NULL à une Spanner commence presque immédiatement à rejeter les écritures pour Requêtes qui utilisent NULL pour la colonne. Si finalement la mise à jour du nouveau schéma échoue lors de la validation des données, il y aura eu une période pendant laquelle les écritures auront été bloquées, même si l'ancien schéma les avait acceptées.

Vous pouvez annuler une opération de validation des données de longue durée à l'aide de la méthode projects.instances.databases.operations.cancel ou en utilisant gcloud spanner operations.

Ordre d'exécution des instructions dans les lots

Si vous utilisez la Google Cloud CLI, l'API REST ou l'API RPC, vous pouvez émettre un traitement par lot d'une ou plusieurs instructions CREATE, ALTER ou DROP.

Spanner applique les instructions d'un même lot dans l'ordre, en s'arrêtant première erreur. Si l'application d'une instruction entraîne une erreur, cette instruction est annulée. Les instructions du lot précédemment appliquées ne sont en revanche pas annulées.

Spanner peut combiner et réorganiser des instructions de différents lots, potentiellement mélanger des instructions de différents lots en une seule modification atomique qui est appliquée à la base de données. Au sein de chaque modification atomique, les instructions des différents lots se déroulent dans un ordre arbitraire. Par exemple, si un lot d'instructions contient ALTER TABLE MyTable ALTER COLUMN MyColumn STRING(50) et un autre le lot d'instructions contient ALTER TABLE MyTable ALTER COLUMN MyColumn STRING(20), Spanner laisse cette colonne dans l'un de ces deux états, mais il n'est pas spécifié lequel.

Versions de schéma créées lors des mises à jour de schéma

Spanner utilise la gestion des versions de schéma afin d'éviter les temps d'arrêt de schéma vers une base de données volumineuse. Spanner conserve l'ancien schéma pour permettre les lectures pendant le traitement de la mise à jour du schéma. Spanner Ensuite, il crée une ou plusieurs versions du schéma pour le traiter. mise à jour. Chaque version contient le résultat d'un ensemble d'instructions dans une modification atomique unique.

Chaque version de schéma ne correspond pas nécessairement à un lot d'instructions DDL ou à une instruction DDL individuelle. Certaines instructions LDD individuelles, comme la création d'index pour les tables de base existantes ou les instructions nécessitant une validation des données, génèrent plusieurs versions de schéma. Dans d'autres cas, plusieurs instructions DDL peuvent être regroupées dans une même version. Les anciennes versions de schéma consomment d'importantes ressources de serveur et de stockage, et sont conservées jusqu'à ils expirent (plus besoin de diffuser des lectures de versions antérieures des données).

Le tableau suivant indique le temps nécessaire à Spanner pour mettre à jour un schéma.

Opération sur le schéma Durée approximative
CREATE TABLE Minutes
CREATE INDEX

Quelques minutes, voire plusieurs heures, si la table de base est créée avant l'index.

Quelques minutes, si l'instruction est exécutée en même temps que l'instruction CREATE TABLE pour la table de base.

DROP TABLE Minutes
DROP INDEX Minutes
ALTER TABLE ... ADD COLUMN Minutes
ALTER TABLE ... ALTER COLUMN

Quelques minutes, voire plusieurs heures, si une validation en arrière-plan est nécessaire.

Quelques minutes, si la validation en arrière-plan n'est pas requise.

ALTER TABLE ... DROP COLUMN Minutes
ANALYZE

Quelques minutes à quelques heures, selon la taille de la base de données.

Modifications des types de données et flux de modifications

Si vous changez le type de données d'une colonne qui change Visionnages de flux, le champ column_types du flux de modifications ultérieur pertinent enregistrements reflète son nouveau type, tout comme les données JSON old_values dans le records' mods.

La valeur new_values du champ mods d'un enregistrement de flux de modifications correspond toujours le type actuel d’une colonne. La modification du type de données d'une colonne surveillée affecter les enregistrements de flux de modifications antérieurs à ce changement.

Dans le cas particulier d'un passage de BYTES à STRING, Spanner valide les anciens composants de la colonne valeurs lors de la mise à jour du schéma. Par conséquent, Spanner a décodé de manière sécurisée des valeurs de type BYTES dans des chaînes au moment où il écrit l'ensemble des les enregistrements de flux de modifications.

Bonnes pratiques pour les mises à jour de schéma

Les sections suivantes décrivent les recommandations pour la mise à jour des schémas.

Procédures à suivre avant de lancer la mise à jour de schéma

Avant de lancer une mise à jour de schéma :

  • Vérifiez que toutes les données existantes dans la base de données que vous modifiez respectent les contraintes imposées par la mise à jour du schéma. Étant donné que la réussite de certains types de mise à jour de schéma dépend des données de la base de données et pas seulement de son schéma actuel, une mise à jour de schéma réussie sur une base de test ne garantit pas la réussite de la mise à jour de schéma sur une base de production. Voici quelques exemples courants :

    • Si vous ajoutez une annotation NOT NULL à une colonne existante, vérifiez qu'elle ne contient aucune valeur NULL.
    • Si vous réduisez la longueur autorisée d'une colonne STRING ou BYTES, vérifiez que toutes les valeurs existantes de cette colonne respectent la longueur d'une contrainte.
  • Si vous écrivez dans une colonne, une table ou un index qui doit subir une mise à jour de schéma, assurez-vous que les valeurs que vous ajoutez répondent aux nouvelles contraintes.

  • Si vous supprimez une colonne, une table ou un index, assurez-vous de ne pas continuer à l'utiliser en lecture ou en écriture.

Limiter la fréquence des mises à jour de schéma

Si vous effectuez trop de mises à jour de schéma en peu de temps, Spanner peut throttle le le traitement des mises à jour de schéma en file d'attente. En effet, Spanner limite le nombre d'espace pour stocker les versions de schéma. La mise à jour de votre schéma peut être limitée si la période de conservation comporte trop d'anciennes versions de schéma. Le taux maximal de modifications de schéma dépend de nombreux facteurs, dont l'un est le nombre total de colonnes dans la base de données. Par exemple, un base de données contenant 2 000 colonnes (environ 2 000 lignes dans INFORMATION_SCHEMA.COLUMNS) peut effectuer 1 500 modifications de schéma au maximum (moins de 1 500 modifications du schéma modification nécessite plusieurs versions) au cours de la période de conservation. Pour afficher l'état des mises à jour de schéma en cours, utilisez la commande gcloud spanner operations list et filtrez les opérations de type DATABASE_UPDATE_DDL. Pour annuler une mise à jour de schéma en cours, exécutez la commande gcloud spanner operations cancel et spécifiez l'ID d'opération.

La manière dont vos instructions LDD sont regroupées et leur ordre dans chaque lot peuvent affecter le nombre de versions de schéma qui en résultent. Pour optimiser le nombre de mises à jour de schéma que vous pouvez effectuer sur une période donnée, il est conseillé d'utiliser un traitement par lots qui réduit le nombre de versions de schéma. Quelques règles de base sont décrites dans la section Mises à jour volumineuses.

Comme décrit dans la section Versions de schéma, certaines instructions LDD créent plusieurs versions de schéma. Celles-ci sont importantes pour le traitement par lot et l'ordre dans chaque lot. Deux principaux types d'instructions permettent de créer plusieurs versions de schéma :

  • Les instructions pouvant nécessiter un remplissage des données d'index, telles que CREATE INDEX
  • Les instructions pouvant nécessiter la validation des données existantes, comme l'ajout de NOT NULL

Cependant, ces types d'instructions ne créent pas toujours plusieurs versions de schéma. Spanner essaie de détecter si ces types d'instructions optimisé pour éviter d'utiliser plusieurs versions de schéma, qui dépendent du traitement par lot. Par exemple, une instruction CREATE INDEX se produit dans le même lot qu'un Instruction CREATE TABLE pour la table de base de l'index, sans interactivité pour les autres tables, évite d'avoir à remplir les données d'index. car Spanner peut garantir que la table de base est vide au moment l'index est créé. La section Mises à jour volumineuses explique comment utiliser cette propriété pour créer efficacement de nombreux index.

Si vous ne pouvez pas grouper vos instructions LDD pour éviter de créer de nombreuses versions de schéma, vous devez limiter le nombre de mises à jour de schéma pour un même schéma de base de données pendant sa période de conservation. Augmenter la durée de création du schéma Mises à jour permettant à Spanner de supprimer les versions antérieures du schéma avant sont créées.

  • Pour certains systèmes de gestion de bases de données relationnelles, il existe des packages logiciels qui effectuent toute une série de mises à jour de schéma (vers une version ultérieure ou antérieure) sur la base de données lors de chaque déploiement en production. Ces types de processus ne sont pas recommandés pour Spanner.
  • Spanner est optimisé pour utiliser des clés primaires afin de partitionner les données pour solutions d'architecture mutualisée. Les solutions d'architecture mutualisée qui utilisent des tables distinctes pour chaque client peuvent générer en raison d'un grand nombre d'opérations de mise à jour de schéma qui mettent beaucoup de temps terminé.
  • Les mises à jour de schéma nécessitant une validation ou un remplissage d'index utilisent davantage de ressources de serveur, car chaque instruction crée plusieurs versions du schéma en interne.

Options pour les mises à jour de schéma volumineuses

La meilleure façon de créer une table et un grand nombre d'index sur cette table est de les créer tous en même temps, de sorte qu'une seule version de schéma soit créée. Il est recommandé de créer les index immédiatement après la table dans la liste des instructions LDD. Vous pouvez créer la table et ses index lors de la création de la base de données ou via un seul grand groupe d'instructions. Si vous devez créer plusieurs tables, chacune avec de nombreux index, vous pouvez inclure toutes les instructions dans un seul lot. Vous pouvez inclure plusieurs milliers d'instructions dans un seul lot lorsque toutes les instructions peuvent être exécutées ensemble à l'aide d'une seule version de schéma.

Lorsqu'une instruction nécessite un remplissage des données d'index ou une validation de données, elle ne peut pas être exécutée dans une seule version de schéma. Cela se produit pour les instructions CREATE INDEX lorsque la table de base de l'index existe déjà (soit parce qu'elle a été créée dans un lot d'instructions LDD précédent, soit parce qu'une instruction du lot entre les instructions CREATE TABLE et CREATE INDEX nécessitait plusieurs versions de schéma. Spanner nécessite qu'il n'y ait plus de 10 instructions de ce type dans un seul lot. La création d'index nécessitant un remplissage, en particulier, utilise plusieurs versions de schéma par index. Il est donc recommandé de ne pas créer plus de trois nouveaux index nécessitant un remplissage par jour (quelle que soit la manière dont ils sont regroupés, à moins que ce regroupement ne permette d'éviter le remplissage).

Par exemple, ce lot d'instructions utilisera une seule version de schéma :

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);

En revanche, ce lot utilisera de nombreuses versions de schéma, car UnrelatedIndex nécessite un remplissage (puisque sa table de base doit avoir déjà existé), ce qui oblige tous les index suivants à nécessiter également un remplissage (même s'ils font partie du même lot que leurs tables de base) :

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);

Il est préférable de déplacer la création de UnrelatedIndex à la fin du lot, ou vers un autre lot, afin de réduire les versions de schéma.

Attendre que les demandes d'API soient terminées

Lorsque vous effectuez des requêtes projects.instances.databases.updateDdl (API REST) ou UpdateDatabaseDdl (API RPC), utilisez projects.instances.databases.operations.get (API REST) ou GetOperation (API RPC), respectivement, pour attendre que chaque requête soit terminée avant d'en lancer une nouvelle. En attendant la fin de chaque requête, votre application peut suivre la progression de vos mises à jour de schéma. Cela permet également de conserver le volume des mises à jour de schéma en attente à une taille raisonnable.

Chargement groupé

Si vous effectuez un chargement groupé des données dans vos tables après leur création, il est généralement plus efficace de créer des index une fois les données chargées. Si vous ajoutez plusieurs index, il peut être plus efficace de créer la base de données avec tous les index et toutes les tables du schéma initial, comme décrit dans les options pour les mises à jour volumineuses.