Questo documento descrive le best practice per l'aggiornamento degli schemi.
Procedure da eseguire prima di iniziare l'aggiornamento dello schema
Prima di eseguire un aggiornamento dello schema:
Assicurati che tutti i dati esistenti nel database siano conformi ai vincoli introdotti dall'aggiornamento dello schema. Poiché alcuni aggiornamenti dello schema dipendono dai dati effettivi, non solo dallo schema attuale, un aggiornamento riuscito in un database di test non garantisce il successo in un database di produzione. Ecco alcuni esempi comuni:
- Se stai aggiungendo un'annotazione
NOT NULLa una colonna esistente, controlla che la colonna non contenga valoriNULLesistenti. - Se stai accorciando la lunghezza consentita di una colonna
STRINGoBYTES, controlla che tutti i valori esistenti in quella colonna soddisfino il vincolo di lunghezza.
- Se stai aggiungendo 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 sono presenti troppe versioni precedenti dello schema nel periodo di conservazione. La frequenza massima delle modifiche dello schema dipende da molti
fattori, uno dei quali è 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) nel 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 aggiornamento dello schema in corso,
utilizza il comando gcloud spanner operations
cancel e specifica
l'ID operazione.
La modalità di raggruppamento delle istruzioni DDL 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 batch che riduce al minimo il numero di versioni dello schema. Alcune linee guida sono descritte in Aggiornamenti di grandi dimensioni.
Come descritto in Versioni dello schema, alcune istruzioni DDL creano più versioni dello schema, che sono importanti quando si considerano il batch e l'ordine all'interno di ogni batch. Esistono due tipi principali di istruzioni che potrebbero creare più versioni dello schema:
- Istruzioni che potrebbero richiedere il backfill dei dati dell'indice, come
CREATE INDEX. - Istruzioni che forzano Spanner a convalidare i dati esistenti, ad esempio l'aggiunta di vincoli
NOT NULLo di lunghezza.
Tuttavia, questi tipi di istruzioni non creano sempre più versioni dello schema. Spanner tenterà di rilevare quando questi tipi di istruzioni possono essere ottimizzati per evitare l'utilizzo di più versioni dello schema, a seconda del batch. 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 di dover 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 allo schema di un singolo database nel relativo periodo di conservazione. Aumenta la finestra temporale in cui esegui gli aggiornamenti dello schema per consentire a Spanner di rimuovere le versioni precedenti dello schema prima della creazione di 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 del database a ogni deployment di produzione. Questi tipi di processi non sono consigliati per Spanner.
- Spanner è ottimizzato per utilizzare le chiavi primarie per partizionare i dati per soluzioni multi-tenant. Se utilizzi una soluzione multi-tenant che utilizza tabelle separate per ogni cliente, tieni presente che gli aggiornamenti dello schema per molti clienti contemporaneamente possono comportare un backlog elevato di operazioni di aggiornamento dello schema che richiedono molto tempo per essere completate.
- Gli aggiornamenti dello schema che richiedono la convalida o il backfill dell'indice utilizzano più risorse del server perché ogni istruzione crea internamente più versioni dello schema.
Ordine di esecuzione delle istruzioni nei batch
Se utilizzi Google Cloud CLI, l'API REST o l'API RPC, puoi eseguire un batch
di una o più CREATE, ALTER, o DROP istruzioni.
Spanner applica le istruzioni dello stesso batch in ordine, arrestandosi al primo errore. Se l'applicazione di un'istruzione genera un errore, l'istruzione viene eseguita il rollback. Non viene eseguito il rollback dei risultati delle istruzioni applicate in precedenza nel batch. Questa applicazione di istruzioni in ordine significa che, se vuoi che le istruzioni che richiedono un backfill inevitabile vengano eseguite in parallelo (ad esempio la creazione di più indici su tabelle esistenti di grandi dimensioni), devi inviare queste istruzioni in batch separati, perché ogni backfill potrebbe richiedere molto tempo. Se invece stai creando una nuova tabella con indici, la best practice è inserirli insieme (CREATE TABLE seguito da CREATE INDEX) in un singolo batch per evitare completamente il backfill.
Spanner potrebbe combinare e riordinare le istruzioni di batch diversi, potenzialmente combinando istruzioni di batch diversi in una singola modifica atomica applicata al database. All'interno di ogni modifica atomica, le istruzioni di batch diversi vengono eseguite in un ordine arbitrario. Ad esempio, se un batch
di istruzioni contiene ALTER TABLE table_name ALTER COLUMN column_name STRING(50)
e un altro batch di istruzioni contiene ALTER TABLE table_name ALTER COLUMN
column_name STRING(20), Spanner lascerà la colonna in uno di
questi due stati, ma lo stato in cui viene lasciata non è deterministico.
Opzioni per aggiornamenti di schemi di grandi dimensioni
Il modo migliore per creare una tabella e un numero elevato di indici per quella tabella è crearli tutti contemporaneamente, in modo che venga creata una sola versione dello schema. La best practice è 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 singolo batch di istruzioni di grandi dimensioni. Se devi creare molte tabelle, ognuna con molti indici, puoi includere tutte le istruzioni in un singolo batch. Puoi includere diverse migliaia di istruzioni in un singolo batch quando tutte le istruzioni possono essere eseguite insieme 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 le istruzioni CREATE INDEX quando la tabella di base dell'indice esiste già (perché è stata creata in un batch precedente di istruzioni DDL o perché nel batch era presente un'istruzione tra le istruzioni CREATE TABLE e CREATE INDEX che richiedeva più versioni dello schema). Spanner richiede che in un singolo batch non siano presenti più di 10 istruzioni di questo tipo. In particolare, la creazione di indici che richiede il backfill utilizza diverse versioni dello schema per indice, quindi è una buona regola creare non più di 3 nuovi indici che richiedono il backfill al giorno (indipendentemente dal modo in cui vengono raggruppati, a meno che il batch 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 tabella di base deve essere già esistente) e questo fa sì che anche tutti gli indici seguenti richiedano 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, è preferibile spostare la creazione di UnrelatedIndex alla fine del batch o in un batch diverso.
Attendere il completamento delle richieste API
Quando effettui richieste projects.instances.databases.updateDdl (API REST) o UpdateDatabaseDdl (API RPC), utilizza
projects.instances.databases.operations.get (API REST)
o GetOperation (API RPC), rispettivamente, per attendere
il completamento di ogni richiesta prima di iniziarne una nuova. L'attesa del completamento di ogni richiesta consente alla tua applicazione di monitorare l'avanzamento degli aggiornamenti dello schema. Inoltre, mantiene il backlog degli aggiornamenti dello schema in attesa a una dimensione gestibile.
Caricamento collettivo
Quando carichi collettivamente i dati in una nuova tabella, puoi creare indici secondari prima o dopo il caricamento dei dati. Il caricamento dei dati è più rapido se crei gli indici dopo il caricamento, ma questo significa che è necessario eseguire il backfill degli indici.
Se carichi prima i dati e poi crei gli indici, l'importazione dati è più rapida perché viene scritta solo la tabella e i backfill degli indici successivi possono scrivere i dati dell'indice in batch ottimizzati più efficienti rispetto alla scrittura dei dati dell'indice insieme ai dati della tabella. Tuttavia, il backfill degli indici richiede più versioni dello schema e presenta dei limiti; come indicato in Opzioni per aggiornamenti di grandi dimensioni, non devi creare più di 10 indici che richiedono il backfill in un singolo batch ed è preferibile creare non più di 3 indici di questo tipo al giorno.
In alternativa, puoi creare tabelle e indici nello stesso batch, come descritto in Opzioni per aggiornamenti di grandi dimensioni. In questo modo si evita il backfill dell'indice, ma il caricamento collettivo dei dati sarà più lento perché ogni indice deve essere aggiornato man mano che i dati vengono caricati.
La scelta migliore in una determinata situazione dipende dalla quantità di dati da caricare, dalle chiavi specifiche di tabella e indice, dal numero di indici necessari e dalla frequenza con cui saranno necessarie le operazioni di caricamento collettivo nello stesso database. In generale, è preferibile creare gli indici separatamente se è necessario caricare una grande quantità di dati in ogni tabella e sono necessari solo pochi indici.