Spanner ti consente di aggiornare lo schema senza tempi di inattività. Puoi aggiornare lo schema di un database esistente in diversi modi:
Nella console Google Cloud
Invia un comando
ALTER TABLE
nella pagina Spanner Studio.Per accedere alla pagina Spanner Studio, fai clic su Spanner Studio nella pagina Panoramica del database o Panoramica della tabella.
Utilizzo dello strumento a riga di comando
gcloud spanner
Invia un comando
ALTER TABLE
utilizzando il comandogcloud spanner databases ddl update
.Utilizzo delle librerie client
Utilizzo dell'API REST
projects.instances.databases.updateDdl
Utilizzo dell'API RPC
UpdateDatabaseDdl
Aggiornamenti dello schema supportati
Spanner supporta i seguenti aggiornamenti dello schema di un database esistente:
- Aggiungi o rimuovi uno schema denominato.
- Crea una nuova tabella. Le colonne nelle nuove tabelle possono essere
NOT NULL
. - Elimina una tabella se non sono interlacciate altre tabelle al suo interno e non ha indici secondari.
- Crea o elimina una tabella con una chiave esterna.
- Aggiungi o rimuovi una chiave esterna da una tabella esistente.
- Aggiungi una colonna non chiave a qualsiasi tabella. Le nuove colonne non chiave non possono essere
NOT NULL
. - Elimina una colonna non chiave da qualsiasi tabella, a meno che non venga utilizzata da un indice secondario, una chiave esterna, una colonna generata memorizzata o un vincolo di controllo.
- Aggiungi
NOT NULL
a una colonna non chiave, escludendo le colonneARRAY
. - Rimuovi
NOT NULL
da una colonna non chiave. - Modificare una colonna
STRING
in una colonnaBYTES
o una colonnaBYTES
in una colonnaSTRING
. - Modificare una colonna
PROTO
in una colonnaBYTES
o una colonnaBYTES
in una colonnaPROTO
. - Modifica il tipo di messaggio proto di una colonna
PROTO
. - Aggiungi nuovi valori a una definizione
ENUM
e rinomina i valori esistenti utilizzandoALTER PROTO BUNDLE
. - Modificare i messaggi definiti in un
PROTO BUNDLE
in modi arbitrari, a condizione che i campi modificati di questi messaggi non vengano utilizzati come chiavi in nessuna tabella e che i dati esistenti soddisfino i nuovi vincoli. - Aumenta o diminuisci il limite di lunghezza per un tipo
STRING
oBYTES
(inclusoMAX
), a meno che non si tratti di una colonna di chiave primaria ereditata da una o più tabelle figlio. - Aumenta o diminuisci il limite di lunghezza per una colonna
ARRAY<STRING>
,ARRAY<BYTES>
oARRAY<PROTO>
fino al valore massimo consentito. - Attiva o disattiva i timestamp dei commit nelle colonne valore e chiave primaria.
- Aggiungi o rimuovi un indice secondario.
- Aggiungi o rimuovi una limitazione di controllo da una tabella esistente.
- Aggiungi o rimuovi una colonna generata archiviata da una tabella esistente.
- Crea un nuovo pacchetto di statistiche dello strumento di ottimizzazione.
- Creare e gestire le visualizzazioni.
- Creare e gestire sequenze.
- Creare ruoli di database e concedere privilegi.
- Imposta, modifica o elimina il valore predefinito di una colonna.
- Modifica le opzioni del database (ad esempio
default_leader
oversion_retention_period
). - Crea e gestisci i flussi di modifiche.
- Creare e gestire modelli ML.
Aggiornamenti dello schema non supportati
Spanner non supporta i seguenti aggiornamenti dello schema di un database esistente:
- Se esiste un campo
PROTO
di tipoENUM
a cui fa riferimento una chiave di tabella o indice, non puoi rimuovere i valoriENUM
dagli enum proto. La rimozione dei valoriENUM
dagli enum utilizzati dalle colonneENUM<>
è supportata, anche quando queste colonne vengono utilizzate come chiavi.
Prestazioni di aggiornamento dello schema
Gli aggiornamenti dello schema in Spanner non richiedono tempi di inattività. Quando invii un batch di istruzioni DDL a un database Spanner, puoi continuare a scrivere e leggere nel database senza interruzioni mentre Spanner applica l'aggiornamento come operazione a lunga esecuzione.
Il tempo necessario per eseguire un'istruzione DDL dipende dalla necessità o meno di convalidare i dati esistenti o di eseguire il backfill di dati durante l'aggiornamento. Ad esempio, se aggiungi l'annotazione NOT NULL
a una colonna esistente, Spanner deve leggere tutti i valori della colonna per assicurarsi che non contenga valori NOT NULL
.NULL
Questo passaggio può richiedere tempi lunghi in presenza di grandi quantità di dati da convalidare. Un altro esempio è se aggiungi un indice a un database:
Spanner esegue il backfill dell'indice utilizzando i dati esistenti e questo processo può essere molto lungo, a seconda della definizione dell'indice e delle dimensioni della tabella di base corrispondente. Tuttavia, se aggiungi una nuova colonna a una tabella, non esistono dati esistenti da convalidare, quindi Spanner può eseguire l'aggiornamento più rapidamente.
In sintesi, gli aggiornamenti dello schema che non richiedono la convalida dei dati esistenti da parte di Spanner possono essere eseguiti in pochi minuti. Gli aggiornamenti dello schema che richiedono la convalida possono richiedere più tempo, a seconda della quantità di dati esistenti che devono essere convalidati, ma la convalida dei dati avviene in background e con una priorità inferiore rispetto al traffico di produzione. Gli aggiornamenti dello schema che richiedono la convalida dei dati sono discussi in maggiore dettaglio nella sezione successiva.
Aggiornamenti dello schema convalidati in base alle definizioni delle visualizzazioni
Quando esegui un aggiornamento dello schema, Spanner convalida che l'aggiornamento non invalidi le query utilizzate per definire le viste esistenti. Se la convalida è riuscita, l'aggiornamento dello schema va a buon fine. Se la convalida non va a buon fine, l'aggiornamento dello schema non va a buon fine. Per maggiori dettagli, consulta Best practice per la creazione delle visualizzazioni.
Aggiornamenti dello schema che richiedono la convalida dei dati
Puoi apportare aggiornamenti allo schema che richiedono la convalida del fatto che i dati esistentisoddisfano i nuovi vincoli. Quando un aggiornamento dello schema richiede la convalida dei dati, Spanner non consente aggiornamenti dello schema in conflitto alle entità dello schema colpite e convalida i dati in background. Se la convalida va a buon fine, l'aggiornamento dello schema va a buon fine. Se la convalida non va a buon fine, l'aggiornamento dello schema non va a buon fine. Le operazioni di convalida vengono eseguite come operazioni a lunga esecuzione. Puoi controllare lo stato di queste operazioni per determinare se sono state eseguite correttamente o meno.
Ad esempio, supponiamo di aver definito il seguente file music.proto
con un enumerato RecordLabel
e un messaggio di protocollo 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;
}
Per aggiungere una tabella Songwriters
allo schema:
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);
I seguenti aggiornamenti dello schema sono consentiti, ma richiedono la convalida e potrebbero richiedere più tempo per essere completati, a seconda della quantità di dati esistenti:
Aggiunta dell'annotazione
NOT NULL
a una colonna non chiave. Ad esempio:ALTER TABLE Songwriters ALTER COLUMN Nickname STRING(MAX) NOT NULL;
Riduzione della lunghezza di una colonna. Ad esempio:
ALTER TABLE Songwriters ALTER COLUMN FirstName STRING(10);
Modifica da
BYTES
aSTRING
. Ad esempio:ALTER TABLE Songwriters ALTER COLUMN OpaqueData STRING(MAX);
Modifica da
INT64/INT32
aENUM
. Ad esempio:ALTER TABLE Albums ALTER COLUMN Label googlesql.example.music.RecordLabel;
Rimozione dei valori esistenti dalla definizione dell'enum
RecordLabel
.Attivazione dei timestamp dei commit in una colonna
TIMESTAMP
esistente. Ad esempio:ALTER TABLE Albums ALTER COLUMN LastUpdateTime SET OPTIONS (allow_commit_timestamp = true);
Aggiunta di una condizione di controllo a una tabella esistente.
Aggiunta di una colonna generata archiviata a una tabella esistente.
Creazione di una nuova tabella con una chiave esterna.
Aggiunta di una chiave esterna a una tabella esistente.
Questi aggiornamenti dello schema non vanno a buon fine se i dati sottostanti non soddisfano i nuovi vincoli. Ad esempio, l'istruzione ALTER TABLE Songwriters ALTER COLUMN Nickname
STRING(MAX) NOT NULL
non va a buon fine se un valore della colonna Nickname
è NULL
, perché i dati esistenti non soddisfano il vincolo NOT NULL
della nuova definizione.
La convalida dei dati può richiedere da alcuni minuti a molte ore. Il tempo necessario per completare la convalida dei dati dipende da:
- La dimensione del set di dati
- La capacità di calcolo dell'istanza
- Il carico sull'istanza
Alcuni aggiornamenti dello schema possono modificare il comportamento delle richieste al database prima del completamento dell'aggiornamento dello schema. Ad esempio, se aggiungi NOT NULL
a una colonna, Spanner inizia quasi immediatamente a rifiutare le scritture per le nuove richieste che utilizzano NOT NULL
per la colonna.NULL
Se l'aggiornamento dello schema nuovo non va a buon fine per la convalida dei dati, ci sarà stato un periodo di tempo in cui le scritture sono state bloccate, anche se sarebbero state accettate dallo schema precedente.
Puoi annullare un'operazione di convalida dei dati di lunga durata utilizzando il metodo
projects.instances.databases.operations.cancel
o utilizzando
gcloud spanner operations
.
Ordine di esecuzione delle istruzioni in batch
Se utilizzi Google Cloud CLI, l'API REST o l'API RPC, puoi emettere un batch
di una o più istruzione CREATE
, ALTER
o DROP
.
Spanner applica le istruzioni dello stesso batch in ordine, fermandosi al primo errore. Se l'applicazione di un'istruzione genera un errore, l'istruzione viene ritirata. I risultati di eventuali istruzioni applicate in precedenza nel batch non vengono sottoposti a rollback.
Spanner potrebbe combinare e riordinare le istruzioni di batch diversi,
potenzialmente mescolando le istruzioni di batch diversi in un'unica modifica atomica che
viene applicata al database. All'interno di ogni modifica atomica, le istruzioni di diversi
batch vengono eseguite in un ordine arbitrario. Ad esempio, se un batch di istruzioni contiene ALTER TABLE MyTable ALTER COLUMN MyColumn STRING(50)
e un altro contiene ALTER TABLE MyTable ALTER COLUMN MyColumn
STRING(20)
, Spanner lascerà la colonna in uno di questi due stati, ma non è specificato quale.
Versioni dello schema create durante gli aggiornamenti dello schema
Spanner utilizza il controllo delle versioni dello schema in modo che non si verifichino tempi di inattività durante un aggiornamento dello schema di un database di grandi dimensioni. Spanner mantiene la versione dello schema precedente per supportare le letture durante l'elaborazione dell'aggiornamento dello schema. Spanner crea quindi una o più nuove versioni dello schema per elaborare l'aggiornamento dello schema. Ogni versione contiene il risultato di una raccolta di istruzioni in una singola modifica atomica.
Le versioni dello schema non corrispondono necessariamente una a una ai batch di istruzioni DDL o alle singole istruzioni DDL. Alcune singole istruzioni DDL, come la creazione di indici per tabelle di base esistenti o istruzioni che richiedono la convalida dei dati, generano più versioni dello schema. In altri casi, è possibile raggruppare più istruzioni DDL in un'unica versione. Le versioni precedenti dello schema possono consumare risorse di server e archiviazione significative e vengono conservate fino alla scadenza (non sono più necessarie per eseguire letture delle versioni precedenti dei dati).
La tabella seguente mostra il tempo necessario a Spanner per aggiornare uno schema.
Operazione dello schema | Durata stimata |
---|---|
CREATE TABLE |
Minuti |
CREATE INDEX |
Da alcuni minuti a diverse ore, se la tabella di base viene creata prima dell'indice. Minuti, se l'istruzione viene eseguita contemporaneamente all'istruzione |
DROP TABLE |
Minuti |
DROP INDEX |
Minuti |
ALTER TABLE ... ADD COLUMN |
Minuti |
ALTER TABLE ... ALTER COLUMN |
Da minuti a ore, se è richiesta la convalida in background. Minuti, se la convalida dei precedenti non è richiesta. |
ALTER TABLE ... DROP COLUMN |
Minuti |
ANALYZE |
Da alcuni minuti a diverse ore, a seconda delle dimensioni del database. |
Modifiche ai tipi di dati e flussi di modifiche
Se modifichi il tipo di dati di una colonna monitorata da un stream di modifiche, il campo column_types
dei record dello stream di modifiche pertinenti successivi riflette il nuovo tipo, così come i dati JSON old_values
all'interno del campo mods
dei record.
Il valore new_values
del campo mods
di un record dello stream di modifiche corrisponde sempre al tipo corrente di una colonna. La modifica del tipo di dati di una colonna monitorata non influisce su alcun record del flusso di modifiche precedente alla modifica.
Nel caso particolare di una modifica da BYTES
a STRING
,
Spanner convalida i vecchi valori della colonna nell'ambito dell'aggiornamento dello schema.
Di conseguenza, Spanner ha decodificato in modo sicuro i vecchi valori di tipo BYTES
in stringhe al momento di scrivere eventuali record dello stream di modifiche successivi.
Best practice per gli aggiornamenti dello schema
Le sezioni seguenti descrivono le best practice per l'aggiornamento degli schemi.
Procedure da seguire prima di emettere l'aggiornamento dello schema
Prima di emettere un aggiornamento dello schema:
Verifica che tutti i dati esistenti nel database che stai modificando siano conformi ai limiti imposti dall'aggiornamento dello schema. Poiché il buon esito di alcuni tipi di aggiornamenti dello schema dipende dai dati nel database e non solo dal suo schema corrente, un aggiornamento dello schema di un database di test riuscito non garantisce un aggiornamento dello schema di un database di produzione. Ecco alcuni esempi comuni:
- Se aggiungi un'annotazione
NOT NULL
a una colonna esistente, controlla che la colonna non contenga valoriNOT NULL
esistenti.NULL
- Se accorci la lunghezza consentita di una colonna
STRING
oBYTES
, verifica che tutti i valori esistenti nella colonna soddisfino il vincolo di lunghezza.
- Se aggiungi un'annotazione
Se stai scrivendo in una colonna, una tabella o un indice in fase di aggiornamento dello schema, assicurati che i valori che stai scrivendo soddisfino i nuovi vincoli.
Se stai eliminando una colonna, una tabella o un indice, assicurati di non continuare a eseguire operazioni di lettura o scrittura su questo elemento.
Limitare la frequenza degli aggiornamenti dello schema
Se esegui troppi aggiornamenti dello schema in un breve periodo di tempo, Spanner potrebbe throttle
l'elaborazione degli aggiornamenti dello schema in coda. Questo perché Spanner limita la quantità di spazio per l'archiviazione delle versioni dello schema. L'aggiornamento dello schema potrebbe essere limitato se esistono troppe versioni precedenti dello schema nel periodo di conservazione. La frequenza massima delle modifiche allo schema dipende da molti fattori, tra cui il numero totale di colonne nel database. Ad esempio, un database con 2000 colonne (circa 2000 righe in INFORMATION_SCHEMA.COLUMNS
) è in grado di eseguire al massimo 1500 modifiche dello schema (meno se la modifica dello schema richiede più versioni) entro il periodo di conservazione. Per visualizzare lo stato degli aggiornamenti dello schema in corso, utilizza il comando gcloud spanner operations list
e filtra in base alle operazioni di tipo DATABASE_UPDATE_DDL
. Per annullare un
update dello schema in corso, utilizza il
comando
gcloud spanner operations cancel
e specifica l'ID operazione.
Il modo in cui le istruzioni DDL vengono raggruppate e il loro ordine all'interno di ogni batch possono influire sul numero di versioni dello schema risultanti. Per massimizzare il numero di aggiornamenti dello schema che puoi eseguire in un determinato periodo di tempo, devi utilizzare il batching che riduce al minimo il numero di versioni dello schema. Alcune regole generali sono descritte nella sezione Aggiornamenti di grandi dimensioni.
Come descritto in Versioni dello schema, alcune istruzioni DDL creano più versioni dello schema e sono importanti per la definizione del batching e dell'ordine all'interno di ogni batch. Esistono due tipi principali di istruzioni che potrebbero creare più versioni dello schema:
- Dichiarazioni che potrebbero richiedere il backfill dei dati dell'indice, ad esempio
CREATE INDEX
- Dichiarazioni che potrebbero richiedere la convalida dei dati esistenti, ad esempio l'aggiunta di
NOT NULL
Tuttavia, questi tipi di istruzioni non creano sempre più versioni dello schema. Spanner cercherà di rilevare quando questi tipi di istruzioni possono essere ottimizzati per evitare di utilizzare più versioni dello schema, il che dipende dal batching.
Ad esempio, un'istruzione CREATE INDEX
che si verifica nello stesso batch di un'istruzione CREATE TABLE
per la tabella di base dell'indice, senza istruzioni intermedie per altre tabelle, può evitare la necessità di eseguire il backfill dei dati dell'indice perché Spanner può garantire che la tabella di base sia vuota al momento della creazione dell'indice. La sezione Aggiornamenti di grandi dimensioni descrive come utilizzare questa proprietà per creare molti indici in modo efficiente.
Se non puoi raggruppare le istruzioni DDL per evitare di creare molte versioni dello schema, devi limitare il numero di aggiornamenti dello schema di un singolo database nel periodo di conservazione. Aumenta l'intervallo di tempo in cui esegui gli aggiornamenti dello schema per consentire a Spanner di rimuovere le versioni precedenti dello schema prima che vengano create nuove versioni.
- Per alcuni sistemi di gestione di database relazionali, esistono pacchetti software che eseguono una lunga serie di aggiornamenti dello schema di upgrade e downgrade al database in ogni deployment di produzione. Questi tipi di processi non sono consigliati per Spanner.
- Spanner è ottimizzato per utilizzare le chiavi principali per partizionare i dati per le soluzioni multi-tenancy. Le soluzioni multi-tenancy che utilizzano tabelle separate per ogni cliente possono generare un ampio backlog di operazioni di aggiornamento dello schema che richiedono molto tempo per essere completate.
- Gli aggiornamenti dello schema che richiedono convalida o il backfill dell'indice utilizzano più risorse del server perché ogni dichiarazione crea internamente più versioni dello schema.
Opzioni per gli aggiornamenti dello schema di grandi dimensioni
Il modo migliore per creare una tabella e un numero elevato di indici è creare contemporaneamente tutti gli elementi, in modo da generare una sola versione dello schema. È buona norma creare gli indici immediatamente dopo la tabella nell'elenco delle istruzioni DDL. Puoi creare la tabella e i relativi indici quando crei il database o in un unico batch di istruzioni di grandi dimensioni. Se devi creare molte tabelle, ciascuna con molti indici, puoi includere tutte le istruzioni in un unico batch. Puoi includere diverse migliaia di istruzioni in un singolo batch se tutte le istruzioni possono essere eseguite contemporaneamente utilizzando una singola versione dello schema.
Quando un'istruzione richiede il backfill dei dati dell'indice o l'esecuzione della convalida dei dati, non può essere eseguita in una singola versione dello schema. Questo accade per gli statement CREATE INDEX
quando la tabella di base dell'indice esiste già (perché è stata creata in un batch precedente di statement DDL o perché nel batch tra gli statement CREATE TABLE
e CREATE INDEX
era presente un statement che richiedeva più versioni dello schema). Spanner richiede che non siano presenti più di 10 di queste istruzioni in un singolo batch. La creazione di indici che richiedono il backfill, in particolare, utilizza diverse versioni dello schema per indice, pertanto è buona norma creare non più di tre nuovi indici che richiedono il backfill al giorno (indipendentemente da come vengono raggruppati, a meno che questo raggruppamento non possa evitare il backfill).
Ad esempio, questo batch di istruzioni utilizzerà una singola versione dello schema:
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);
Al contrario, questo batch utilizzerà molte versioni dello schema, perché UnrelatedIndex
richiede il backfill (poiché la relativa tabella di base deve già esistere) e questo
obbliga anche tutti i seguenti indici a richiedere il backfill (anche se
si trovano nello stesso batch delle relative tabelle di 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);
Per ridurre al minimo le versioni dello schema, sarebbe meglio spostare la creazione di UnrelatedIndex
alla fine del batch o in un altro batch.
Attendi il completamento delle richieste API
Quando effettui richieste projects.instances.databases.updateDdl
(API REST) o UpdateDatabaseDdl
(API RPC), utilizza rispettivamente projects.instances.databases.operations.get
(API REST) o GetOperation
(API RPC) per attendere il completamento di ogni richiesta prima di avviarne una nuova. L'attesa del completamento di ogni richiesta consente all'applicazione di monitorare l'avanzamento degli aggiornamenti dello schema. Inoltre, mantiene la coda degli aggiornamenti dello schema in attesa di dimensioni gestibili.
Caricamento collettivo
Se esegui il caricamento collettivo dei dati nelle tabelle dopo la creazione, in genere è più efficiente creare gli indici dopo aver caricato i dati. Se stai aggiungendo diversi indici, potrebbe essere più efficiente creare il database con tutte le tabelle e gli indici nello schema iniziale, come descritto nelle opzioni per gli aggiornamenti di grandi dimensioni.