Mettre à jour le 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:

  • 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.
  • 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.
  • 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.
  • Construit un package de statistiques d'optimisation.

Performances de la mise à jour de schéma

Les mises à jour de schémas dans Spanner ne nécessitent pas de temps d'arrêt. Lorsque vous soumettez un lot d'instructions LDD à une base de données Spanner, vous pouvez continuer à écrire et à lire dans la base de données sans interruption pendant que Spanner applique la mise à jour en tant qu'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 lire toutes les valeurs de la colonne pour s'assurer qu'elle ne contient aucune valeur NULL. Cette étape peut être longue s'il y a beaucoup de données à valider. Autre exemple : si vous ajoutez un index à une base de données : Spanner remplit l'index à l'aide des données existantes. Ce processus peut prendre beaucoup de temps en fonction de la définition de l'index et de la taille de la table de base correspondante. Toutefois, si vous ajoutez une colonne à une table, il n'y a aucune donnée existante à valider. Spanner peut donc effectuer la mise à jour plus rapidement.

En résumé, les mises à jour de schéma qui ne nécessitent pas de valider les données existantes par Spanner peuvent être effectué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 mettez à jour un 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 sur les entités de schéma concernées 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 une table Songwriters dans votre schéma :

GoogleSQL

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

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);
    
  • 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 ci-dessus échoue si la colonne Nickname contient NULL, car les données existantes ne respectent pas la contrainte 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 :

  • La 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 colonne, Spanner commence presque immédiatement à rejeter les écritures pour les nouvelles 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 lot d'une ou plusieurs instructions CREATE, ALTER ou DROP.

Spanner applique les instructions du même lot dans l'ordre, en s'arrêtant à la 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 en une seule modification atomique 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 contient ALTER TABLE MyTable ALTER COLUMN MyColumn STRING(20), Spanner laisse cette colonne dans l'un de ces deux états, mais sans spécifier 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 lors de la mise à jour du schéma vers une base de données volumineuse. Spanner conserve l'ancienne version du schéma pour autoriser les lectures pendant le traitement de la mise à jour du schéma. Spanner crée ensuite une ou plusieurs versions du schéma pour traiter la mise à jour du schéma. Chaque version contient le résultat d'une collection d'instructions dans une modification atomique unique, comme décrit ci-dessus.

Chaque version de schéma ne correspond pas nécessairement à un lot d'instructions LDD ou à une instruction LDD 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 LDD peuvent être regroupées dans une même version. Les anciennes versions de schéma peuvent consommer d’importantes ressources en termes de serveur et de stockage, et sont conservées jusqu'à leur expiration (plus nécessaires pour diffuser des lectures d'anciennes versions 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, voire plusieurs heures, en fonction de la taille de la base de données.

Modifications des types de données et flux de modifications

Si vous modifiez le type de données d'une colonne surveillée par un flux de modifications, le champ column_types des enregistrements de flux de modifications suivants pertinents reflète son nouveau type, tout comme les données JSON old_values dans le champ mods des enregistrements.

La valeur new_values du champ mods d'un enregistrement de flux de modifications correspond toujours au type actuel d'une colonne. La modification du type de données d'une colonne surveillée n'affecte pas les enregistrements de flux de modifications antérieurs à cette modification.

Dans le cas particulier d'une modification de BYTES en STRING, Spanner valide les anciennes valeurs de la colonne lors de la mise à jour du schéma. Par conséquent, Spanner a décodé en toute sécurité les anciennes valeurs de type BYTES en chaînes au moment où il écrit les enregistrements de flux de modifications suivants.

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 contrainte de longueur souhaitée.
  • 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 sur une courte période, Spanner risque d'throttle traiter les mises à jour de schéma en file d'attente. En effet, Spanner limite la quantité d'espace pour le stockage des 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, une base de données avec 2 000 colonnes (environ 2 000 lignes dans INFORMATION_SCHEMA.COLUMNS) peut effectuer au maximum 1 500 modifications de schéma simples (moins si la modification de schéma 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 tentera de détecter quand ces types d'instructions peuvent être optimisés pour éviter d'utiliser plusieurs versions de schéma, qui dépendent du traitement par lot. Par exemple, une instruction CREATE INDEX qui se produit dans le même lot qu'une instruction CREATE TABLE pour la table de base de l'index, sans aucune instruction intervenue pour les autres tables, peut éviter d'avoir à remplir les données d'index, car Spanner peut garantir que la table de base est vide au moment de la création de l'index. 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. Augmentez la période pendant laquelle vous effectuez les mises à jour de schéma pour permettre à Spanner de supprimer les anciennes versions du schéma avant d'en créer de nouvelles.

  • 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 des solutions d'architecture mutualisée. Les solutions d'architecture mutualisée qui utilisent des tables distinctes pour chaque client peuvent générer beaucoup d'attente dans le traitement des opérations de mise à jour de schéma, occasionnant un temps de traitement important.
  • 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 exige qu'il n'y ait pas 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.