Questo documento descrive le best practice per l'aggiornamento degli schemi.
Procedure 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, verifica che la colonna non contenga valoriNULLesistenti. - Se riduci la lunghezza consentita di una colonna
STRINGoBYTES, verifica che tutti i valori esistenti in quella colonna rispettino 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 velocità massima di modifica 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 allo schema (meno se la modifica allo 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 aggiornamento 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 in batch 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 negli aggiornamenti di grandi dimensioni.
Come descritto in Versioni dello schema, alcune istruzioni DDL creano più versioni dello schema, che sono importanti quando si prendono in considerazione il batching 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 riempimento dei dati dell'indice, ad esempio
CREATE INDEX. - Istruzioni che forzano Spanner a convalidare i dati esistenti, ad esempio l'aggiunta di vincoli
NOT NULLo di lunghezza.
Questi tipi di istruzioni non sempre creano 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 a un singolo schema di database entro il periodo di conservazione. Aumenta la finestra temporale in cui apporti gli aggiornamenti dello schema per consentire a Spanner di rimuovere le versioni precedenti dello schema prima di creare nuove versioni.
- Per alcuni sistemi di gestione di database relazionali, esistono pacchetti software che eseguono una lunga serie di aggiornamenti e downgrade dello schema 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 le 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 riempimento 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 emettere 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 annullata. I risultati di eventuali istruzioni applicate in precedenza nel batch non vengono ripristinati. 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 inviarle 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 unico batch per evitare completamente il backfill.
Spanner potrebbe combinare e riordinare le istruzioni di batch diversi, potenzialmente mescolando le 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 la tabella è crearli tutti contemporaneamente, in modo che venga creata una sola versione dello schema. La best practice consiste nel 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, ognuna con molti indici, puoi includere tutte le istruzioni in un unico 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 riempimento dei dati dell'indice o l'esecuzione della convalida dei dati,
non può essere eseguita in una singola versione dello schema. Ciò si verifica 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 non ci siano più di 10 istruzioni di questo tipo in un singolo batch. La creazione di indici che
richiedono il riempimento, in particolare, utilizza diverse versioni dello schema per indice, quindi
è una buona regola generale creare non più di
tre nuovi indici che richiedono il riempimento al giorno (indipendentemente
da come vengono raggruppati, a meno che questo raggruppamento non possa evitare il riempimento).
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 riempimento (poiché la tabella di base deve già esistere) e ciò
impone che anche tutti gli indici seguenti richiedano il riempimento (anche se
si trovano nello stesso batch delle 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);
Sarebbe meglio spostare la creazione di UnrelatedIndex alla fine del batch o in un batch diverso per ridurre al minimo le versioni dello schema.
Attendi 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 iniziare una nuova richiesta. 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 in blocco 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 ciò significa che gli indici devono essere riempiti.
Se carichi prima i dati e poi crei gli indici, l'importazione dati è più veloce perché viene scritta solo la tabella e i riempimenti successivi degli indici possono scrivere i dati degli indici in batch ottimizzati più efficienti rispetto alla scrittura dei dati degli indici insieme ai dati della tabella. Tuttavia, il riempimento 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 riempimento in un singolo batch ed è consigliabile non creare 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 backfilling 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 tabelle e indici, dal numero di indici necessari e dalla frequenza con cui saranno necessarie le operazioni di caricamento collettivo nello stesso database. Una regola generale è che è meglio creare gli indici separatamente se è necessario caricare una grande quantità di dati in ogni tabella e sono necessari solo pochi indici.