Questa pagina descrive le transazioni in Spanner e introduce le interfacce delle transazioni DML partizionate, di lettura-scrittura e di sola lettura di Spanner.
Una transazione in Spanner è un insieme di letture e scritture che vengono eseguite in modo atomico in un unico punto logico nel tempo su colonne, righe e tabelle di un database.
Una sessione viene utilizzata per eseguire transazioni in un database Spanner. Una sessione rappresenta un canale di comunicazione logico con il servizio di database Spanner. Le sessioni possono eseguire una o più transazioni alla volta. Per ulteriori informazioni, consulta la sezione Sessioni.
Tipi di transazioni
Spanner supporta i seguenti tipi di transazione, ognuno progettato per pattern di interazione con i dati specifici:
Lettura/scrittura:queste transazioni utilizzano il blocco pessimistico e, se necessario, un commit in due fasi. Potrebbero non riuscire e richiedere dei tentativi. Sebbene siano limitati a un singolo database, possono modificare i dati in più tabelle all'interno di quel database.
Sola lettura:queste transazioni garantiscono la coerenza dei dati in più operazioni di lettura, ma non consentono modifiche ai dati. Vengono eseguiti in corrispondenza di un timestamp determinato dal sistema per garantire la coerenza oppure in corrispondenza di un timestamp passato configurato dall'utente. A differenza delle transazioni di lettura/scrittura, non richiedono un'operazione di commit o blocchi, anche se potrebbero essere messe in pausa per attendere il completamento delle operazioni di scrittura in corso.
DML partizionato:questo tipo di transazione esegue istruzioni DML come operazioni DML partizionate. È ottimizzato per aggiornamenti ed eliminazioni di dati su larga scala, come la pulizia dei dati o l'inserimento bulk di dati. Per numerose operazioni di scrittura che non richiedono una transazione atomica, valuta l'utilizzo di operazioni di scrittura in batch. Per maggiori dettagli, consulta Modificare i dati utilizzando le scritture batch.
Transazioni di lettura/scrittura
Utilizza le transazioni di lettura/scrittura con blocco per leggere, modificare e scrivere dati in modo atomico in qualsiasi punto di un database. Questo tipo di transazione è coerente esternamente.
Ridurre al minimo il tempo in cui una transazione è attiva. Durate delle transazioni più brevi
aumentano la probabilità di un commit riuscito e riducono la contesa.
Spanner tenta di mantenere attivi i blocchi di lettura finché la transazione continua a eseguire letture e non è terminata tramite operazioni sessions.commit
o sessions.rollback
.
Se il client rimane inattivo per lunghi periodi, Spanner potrebbe
rilasciare i blocchi della transazione e interromperla.
A livello concettuale, una transazione di lettura/scrittura è costituita da zero o più letture o istruzioni SQL
seguite da sessions.commit
. In qualsiasi momento prima del giorno sessions.commit
,
il cliente può inviare una richiesta sessions.rollback
per interrompere la transazione.
Per eseguire un'operazione di scrittura che dipende da una o più operazioni di lettura, utilizza una transazione di lettura/scrittura con blocco:
- Se devi eseguire una o più operazioni di scrittura in modo atomico, esegui queste scritture all'interno della stessa transazione di lettura/scrittura. Ad esempio, se trasferisci 200 $ dall'account A all'account B, esegui entrambe le operazioni di scrittura (riduzione dell'account A di 200 $e aumento dell'account B di 200 $) e le letture dei saldi iniziali degli account all'interno della stessa transazione.
- Se vuoi raddoppiare il saldo dell'account A, esegui le operazioni di lettura e scrittura all'interno della stessa transazione. In questo modo, il sistema legge il saldo prima di raddoppiarlo e poi aggiornarlo.
- Se potresti eseguire una o più operazioni di scrittura che dipendono dai risultati di una o più operazioni di lettura, esegui queste operazioni di scrittura e lettura nella stessa transazione di lettura/scrittura, anche se le operazioni di scrittura non vengono eseguite. Ad esempio, se vuoi trasferire 200 $dal conto A al conto B solo se il saldo attuale del conto A è superiore a 500 $, includi la lettura del saldo del conto A e le operazioni di scrittura condizionali nella stessa transazione, anche se il trasferimento non avviene.
Per eseguire operazioni di lettura, utilizza un singolo metodo di lettura o una transazione di sola lettura:
- Se esegui solo operazioni di lettura e puoi esprimere l'operazione di lettura utilizzando un singolo metodo di lettura, utilizza questo singolo metodo di lettura o una transazione di sola lettura. A differenza delle transazioni di lettura/scrittura, le singole letture non acquisiscono blocchi.
Interfaccia
Le librerie client Spanner forniscono un'interfaccia per l'esecuzione di un corpo di lavoro all'interno di una transazione di lettura/scrittura, con nuovi tentativi per gli interruzioni della transazione. Una transazione Spanner potrebbe richiedere più tentativi prima di essere eseguita.
L'interruzione delle transazioni può essere causata da diverse situazioni. Ad esempio, se due transazioni tentano di modificare i dati contemporaneamente, potrebbe verificarsi un deadlock. In questi casi, Spanner interrompe una transazione per consentire all'altra di procedere. Meno frequentemente, anche gli eventi temporanei all'interno di Spanner possono causare interruzioni delle transazioni.
Poiché le transazioni sono atomiche, una transazione interrotta non
influisce sul database. Riprova la transazione nella stessa sessione per migliorare
i tassi di successo. Ogni nuovo tentativo che genera un errore ABORTED
aumenta la priorità di blocco della transazione.
Quando utilizzi una transazione in una libreria client Spanner, definisci il corpo della transazione come oggetto funzione. Questa funzione incapsula le letture e le scritture eseguite su una o più tabelle del database. La libreria client Spanner esegue questa funzione ripetutamente finché la transazione non viene eseguita correttamente o non si verifica un errore che non può essere riprovato.
Esempio
Supponiamo di avere una colonna MarketingBudget
nella
tabella Albums
:
CREATE TABLE Albums ( SingerId INT64 NOT NULL, AlbumId INT64 NOT NULL, AlbumTitle STRING(MAX), MarketingBudget INT64 ) PRIMARY KEY (SingerId, AlbumId);
Il tuo reparto marketing ti chiede di trasferire 200.000 € dal budget di Albums
(2, 2)
a Albums (1, 1)
, ma solo se i fondi sono disponibili nel budget
dell'album. Per questa operazione devi utilizzare una transazione di lettura/scrittura con blocco,
perché la transazione potrebbe eseguire scritture a seconda del risultato di una lettura.
Di seguito viene mostrato come eseguire una transazione di lettura/scrittura:
C++
C#
Go
Java
Node.js
PHP
Python
Ruby
Semantica
Questa sezione descrive la semantica delle transazioni di lettura/scrittura in Spanner.
Proprietà
Una transazione di lettura-scrittura in Spanner esegue un insieme di letture e scritture in modo atomico. Il timestamp in cui vengono eseguite le transazioni di lettura/scrittura corrisponde al tempo trascorso. L'ordine di serializzazione corrisponde a questo ordine di timestamp.
Le transazioni di lettura/scrittura forniscono le proprietà ACID dei database relazionali. Le transazioni di lettura/scrittura di Spanner offrono proprietà più solide rispetto alle transazioni ACID tipiche.
Grazie a queste proprietà, in qualità di sviluppatore di applicazioni, puoi concentrarti sulla correttezza di ogni transazione singolarmente, senza preoccuparti di come proteggere la sua esecuzione da altre transazioni che potrebbero essere eseguite contemporaneamente.
Isolamento per le transazioni di lettura/scrittura
Dopo aver eseguito correttamente il commit di una transazione che contiene una serie di letture e scritture, visualizzi quanto segue:
- La transazione restituisce valori che riflettono uno snapshot coerente all'ora del commit della transazione.
- Le righe o gli intervalli vuoti rimangono vuoti al momento del commit.
- La transazione esegue il commit di tutte le scritture al timestamp di commit della transazione.
- Nessuna transazione può vedere le scritture fino a quando non viene eseguito il commit.
I driver client Spanner includono una logica di ripetizione dei tentativi di transazione che maschera gli errori temporanei eseguendo nuovamente la transazione e convalidando i dati osservati dal client.
L'effetto è che tutte le letture e le scritture sembrano essere avvenute in un unico momento, sia dal punto di vista della transazione stessa sia dal punto di vista di altri lettori e autori del database Spanner. Ciò significa che le letture e le scritture si verificano nello stesso timestamp. Per un esempio, consulta Serializzabilità e coerenza esterna.
Isolamento per le transazioni di lettura
Quando una transazione di lettura/scrittura esegue solo operazioni di lettura, fornisce garanzie di coerenza simili a una transazione di sola lettura. Tutte le letture all'interno della transazione restituiscono dati da un timestamp coerente, inclusa la conferma delle righe inesistenti.
Una differenza si verifica quando una transazione di lettura/scrittura viene eseguita senza eseguire un'operazione di scrittura. In questo scenario, non è garantito che i dati letti all'interno della transazione siano rimasti invariati nel database tra l'operazione di lettura e il commit della transazione.
Per garantire l'aggiornamento dei dati e verificare che non siano stati modificati dall'ultimo recupero, è necessaria una lettura successiva. Questa rilettura può essere eseguita all'interno di un'altra transazione di lettura/scrittura o con una lettura coerente.
Per un'efficienza ottimale, se una transazione esegue esclusivamente letture, utilizza una transazione di sola lettura anziché una transazione di lettura/scrittura.
Atomicità, coerenza, durabilità
Oltre all'isolamento, Spanner fornisce le altre garanzie delle proprietà ACID:
- Atomicità. Una transazione viene considerata atomica se tutte le sue operazioni vengono completate correttamente o nessuna. Se un'operazione all'interno di una transazione non va a buon fine, l'intera transazione viene ripristinata allo stato originale, garantendo l'integrità dei dati.
- Coerenza. Una transazione deve mantenere l'integrità delle regole e dei vincoli del database. Al termine di una transazione, il database deve trovarsi in uno stato valido, rispettando le regole predefinite.
- Durabilità. Una volta eseguito il commit di una transazione, le modifiche vengono archiviate in modo permanente nel database e persistono in caso di errori di sistema, interruzioni di corrente o altri problemi.
Serializzabilità e coerenza esterna
Spanner offre solide garanzie transazionali, tra cui serializzabilità e coerenza esterna. Queste proprietà garantiscono che i dati rimangano coerenti e che le operazioni avvengano in un ordine prevedibile, anche in un ambiente distribuito.
La serializzabilità garantisce che tutte le transazioni vengano eseguite una dopo l'altra in un unico ordine sequenziale, anche se vengono elaborate contemporaneamente. Spanner lo fa assegnando timestamp di commit alle transazioni, riflettendo l'ordine in cui sono state eseguite.
Spanner offre una garanzia ancora più efficace nota come coerenza esterna. Ciò significa che non solo le transazioni vengono eseguite in un ordine riflesso dai timestamp di commit, ma questi timestamp sono anche allineati all'ora del mondo reale. In questo modo puoi confrontare i timestamp dei commit con il tempo reale, fornendo una visualizzazione coerente e ordinata a livello globale dei tuoi dati.
In sostanza, se una transazione Txn1
viene eseguita prima di un'altra transazione Txn2
in
tempo reale, il timestamp di commit di Txn1
è precedente a quello di Txn2
.
Considera l'esempio seguente:
In questo scenario, durante la sequenza temporale t
:
- La transazione
Txn1
legge i datiA
, esegue la preparazione della scrittura inA
e poi viene eseguita correttamente. - La transazione
Txn2
inizia dopo l'avvio diTxn1
. Legge i datiB
e poi legge i datiA
.
Anche se Txn2
è iniziato prima del completamento di Txn1, Txn2
osserva le modifiche
apportate da Txn1
a A
. Questo perché Txn2
legge A
dopo che Txn1
ha eseguito
la scrittura in A
.
Sebbene Txn1
e Txn2
possano sovrapporsi nel tempo di esecuzione, i timestamp di commit, c1
e c2
rispettivamente, impongono un ordine lineare delle transazioni. Ciò
significa:
- Tutte le letture e le scritture all'interno di
Txn1
sembrano essere avvenute in un unico momento,c1
. - Tutte le letture e le scritture all'interno di
Txn2
sembrano essere avvenute in un unico momento,c2
. - Fondamentalmente,
c1
è precedente ac2
per le scritture di commit, anche se le scritture sono avvenute su macchine diverse. SeTxn2
esegue solo letture,c1
è precedente o contemporaneo ac2
.
Questo ordinamento rigido significa che se un'operazione di lettura successiva osserva gli
effetti di Txn2
, osserva anche gli effetti di Txn1
. Questa proprietà è
vera per tutte le transazioni di cui è stato eseguito il commit.
Garanzie di lettura e scrittura in caso di errore della transazione
Se una chiamata per eseguire una transazione non va a buon fine, le garanzie di lettura e scrittura che hai dipendono dall'errore con cui non è andata a buon fine la chiamata di commit sottostante.
Ad esempio, un errore come "Riga non trovata" o "Riga già esistente" significa che la scrittura delle mutazioni memorizzate nel buffer ha riscontrato un errore, ad esempio una riga che il client sta tentando di aggiornare non esiste. In questo caso, le letture sono garantite coerenti, le scritture non vengono applicate e la non esistenza della riga è garantita per essere coerente anche con le letture.
Garanzie di lettura e scrittura in caso di errore della transazione
Quando una transazione Spanner non va a buon fine, le garanzie che ricevi per
letture e scritture dipendono dall'errore specifico riscontrato durante l'operazione commit
.
Ad esempio, un messaggio di errore come "Riga non trovata" o "Riga già esistente" indica un problema durante la scrittura delle mutazioni memorizzate nel buffer. Ciò può verificarsi se, ad esempio, una riga che il client sta tentando di aggiornare non esiste. In questi scenari:
- Le letture sono coerenti: tutti i dati letti durante la transazione sono garantiti come coerenti fino al punto dell'errore.
- Le scritture non vengono applicate:le mutazioni tentate dalla transazione non vengono commitate nel database.
- Coerenza delle righe:l'inesistenza (o lo stato esistente) della riga che ha attivato l'errore è coerente con le letture eseguite all'interno della transazione.
Puoi annullare le operazioni di lettura asincrone in Spanner in qualsiasi momento senza influire su altre operazioni in corso all'interno della stessa transazione. Questa flessibilità è utile se un'operazione di livello superiore viene annullata o se decidi di interrompere una lettura in base ai risultati iniziali.
Tuttavia, è importante capire che la richiesta di annullamento di una lettura non ne garantisce l'interruzione immediata. Dopo una richiesta di annullamento, l'operazione di lettura potrebbe ancora:
- Completamento riuscito:la lettura potrebbe terminare l'elaborazione e restituire i risultati prima che l'annullamento diventi effettivo.
- Errore per un altro motivo:la lettura potrebbe terminare a causa di un errore diverso, ad esempio un'interruzione.
- Restituisci risultati incompleti:la lettura potrebbe restituire risultati parziali, che vengono poi convalidati nell'ambito della procedura di commit della transazione.
È inoltre importante notare la distinzione con le operazioni di transazione commit
: l'annullamento di una commit
interrompe l'intera transazione, a meno che non sia già stata eseguita o non sia andata a buon fine per un altro motivo.
Prestazioni
Questa sezione descrive i problemi che influiscono sulle prestazioni delle transazioni di lettura/scrittura.
Controllo della contemporaneità basato su blocchi
Spanner consente a più client di interagire contemporaneamente con lo stesso database. Per mantenere la coerenza dei dati in queste transazioni simultanee, Spanner dispone di un meccanismo di blocco che utilizza blocchi condivisi ed esclusivi.
Quando una transazione esegue un'operazione di lettura, Spanner acquisisce blocchi di lettura condivisi sui dati pertinenti. Queste serrature condivise consentono ad altre operazioni di lettura simultanee di accedere agli stessi dati. Questa concorrenza viene mantenuta finché la transazione non si prepara a eseguire il commit delle modifiche.
Durante la fase di commit, man mano che vengono applicate le scritture, la transazione tenta di eseguire l'upgrade dei blocchi a blocchi esclusivi. Per farlo, esegue le seguenti operazioni:
- Blocca tutte le nuove richieste di blocco in lettura condiviso sui dati interessati.
- Attende il rilascio di tutti i blocchi di lettura condivisi esistenti sui dati.
- Dopo che tutti i blocchi di lettura condivisi sono stati eliminati, viene inserito un blocco esclusivo, che concede l'accesso esclusivo ai dati per la durata della scrittura.
Note sulle serrature:
- Granularità:Spanner applica i blocchi a livello di riga e colonna. Ciò significa che se la transazione
T1
blocca la colonnaA
della rigaalbumid
, la transazioneT2
può comunque scrivere contemporaneamente nella colonnaB
della stessa rigaalbumid
senza conflitti. - Scritture senza letture: per le scritture senza letture, Spanner non richiede un blocco esclusivo. Utilizza invece un blocco condiviso di scrittura. Questo perché l'ordine di applicazione per le scritture senza letture è determinato dai timestamp di commit, consentendo a più autori di operare sullo stesso elemento contemporaneamente senza conflitti. Un blocco esclusivo è necessario solo se la transazione legge prima i dati che intende scrivere.
- Indici secondari per le ricerche di righe: quando esegui ricerche di righe all'interno di una transazione di lettura/scrittura, l'utilizzo di indici secondari può migliorare significativamente le prestazioni. Utilizzando gli indici secondari per limitare le righe scansionate a un intervallo più piccolo, Spanner blocca meno righe nella tabella, consentendo così una maggiore modifica simultanea delle righe al di fuori di questo intervallo specifico.
- Accesso esclusivo alle risorse esterne:i blocchi interni di Spanner sono progettati per la coerenza dei dati all'interno del database Spanner stesso. Non utilizzarli per garantire l'accesso esclusivo alle risorse al di fuori di Spanner. Spanner può interrompere le transazioni per vari motivi, tra cui ottimizzazioni interne del sistema come lo spostamento dei dati tra le risorse di calcolo. Se viene eseguito un nuovo tentativo di transazione (in modo esplicito dal codice dell'applicazione o implicito dalle librerie client come il driver JDBC di Spanner), è garantito che i blocchi siano stati mantenuti solo durante il tentativo di commit riuscito.
- Statistiche sui blocchi:per diagnosticare e analizzare i conflitti di blocco all'interno del database, puoi utilizzare lo strumento di introspezione Statistiche sui blocchi.
Rilevamento deadlock
Spanner rileva quando più transazioni potrebbero essere in deadlock
e forza l'interruzione di tutte le transazioni tranne una. Considera questo scenario:
Txn1
mantiene un blocco sul record A
ed è in attesa di un blocco sul record B
, mentre
Txn2
mantiene un blocco sul record B
ed è in attesa di un blocco sul record A
. Per
risolvere il problema, una delle transazioni deve essere interrotta, rilasciando il blocco e
consentendo all'altra di procedere.
Spanner utilizza l'algoritmo wound-wait standard per il rilevamento del deadlock. Dietro le quinte, Spanner tiene traccia dell'età di ogni transazione che richiede blocchi in conflitto. Consente alle transazioni meno recenti di interrompere quelle più recenti. Una transazione meno recente è quella la cui prima lettura, query o commit è avvenuta prima.
Dando la priorità alle transazioni meno recenti, Spanner garantisce che ogni transazione acquisisca i blocchi dopo che è diventata abbastanza vecchia da avere una priorità più elevata. Ad esempio, una transazione meno recente che necessita di un blocco condiviso con l'autore può interrompere una transazione più recente che detiene un blocco condiviso con il lettore.
Esecuzione distribuita
Spanner può eseguire transazioni su dati che si estendono su più server, anche se questa funzionalità comporta un costo in termini di prestazioni rispetto alle transazioni su un singolo server.
Quali tipi di transazioni potrebbero essere distribuiti? Spanner può distribuire la responsabilità delle righe del database su più server. In genere, una riga e le righe della tabella interleaved corrispondenti vengono servite dallo stesso server, così come due righe nella stessa tabella con chiavi vicine. Spanner può eseguire transazioni tra righe su server diversi. Tuttavia, in generale, le transazioni che interessano molte righe collocate nella stessa posizione sono più veloci ed economiche di quelle che interessano molte righe sparse nel database o una tabella di grandi dimensioni.
Le transazioni più efficienti in Spanner includono solo le letture e le scritture che devono essere applicate in modo atomico. Le transazioni sono più veloci quando tutte le letture e le scritture accedono ai dati nella stessa parte dello spazio delle chiavi.
Transazioni di sola lettura
Oltre a bloccare le transazioni di lettura/scrittura, Spanner offre transazioni di sola lettura.
Utilizza una transazione di sola lettura quando devi eseguire più di una lettura allo stesso timestamp. Se puoi esprimere la lettura utilizzando uno dei metodi di lettura singola di Spanner, devi utilizzare questo metodo. Le prestazioni dell'utilizzo di una singola chiamata di lettura dovrebbero essere paragonabili a quelle di una singola lettura eseguita in una transazione di sola lettura.
Se stai leggendo una grande quantità di dati, valuta la possibilità di utilizzare le partizioni per leggere i dati in parallelo.
Poiché le transazioni di sola lettura non scrivono, non mantengono blocchi e non bloccano altre transazioni. Le transazioni di sola lettura osservano un prefisso coerente della cronologia dei commit delle transazioni, in modo che la tua applicazione riceva sempre dati coerenti.
Interfaccia
Spanner fornisce un'interfaccia per l'esecuzione di un insieme di operazioni nel contesto di una transazione di sola lettura, con nuovi tentativi in caso di interruzione della transazione.
Esempio
Il seguente esempio mostra come utilizzare una transazione di sola lettura per ottenere dati coerenti per due letture con lo stesso timestamp:
C++
C#
Go
Java
Node.js
PHP
Python
Ruby
Semantica
Questa sezione descrive la semantica per le transazioni di sola lettura.
Transazioni di sola lettura dello snapshot
Quando una transazione di sola lettura viene eseguita in Spanner, esegue tutte le letture in un unico punto logico nel tempo. Ciò significa che sia la transazione di sola lettura sia qualsiasi altro lettore e writer simultaneo vedono un'istantanea coerente del database in quel momento specifico.
Queste transazioni di sola lettura dello snapshot offrono un approccio più semplice per letture coerenti rispetto al blocco delle transazioni di lettura/scrittura. Ecco perché:
- Nessun blocco:le transazioni di sola lettura non acquisiscono blocchi. Funzionano invece selezionando un timestamp Spanner ed eseguendo tutte le letture rispetto a quella versione storica dei dati. Poiché non utilizzano blocchi, non bloccano le transazioni di lettura/scrittura simultanee.
- Nessun annullamento:queste transazioni non vengono mai annullate. Anche se potrebbero non riuscire se il timestamp di lettura scelto viene sottoposto a garbage collection, la normale policy di garbage collection di Spanner è in genere abbastanza generosa da non causare questo problema nella maggior parte delle applicazioni.
- Nessun commit o rollback:le transazioni di sola lettura non richiedono chiamate a
sessions.commit
osessions.rollback
e viene impedito di farlo.
Per eseguire una transazione snapshot, il client definisce un limite di timestamp, che indica a Spanner come selezionare un timestamp di lettura. I tipi di limiti del timestamp includono:
- Letture coerenti:queste letture garantiscono che vedrai gli effetti di tutte le transazioni di cui è stato eseguito il commit prima dell'inizio della lettura. Tutte le righe all'interno di una singola lettura sono coerenti. Tuttavia, le letture coerenti non sono ripetibili, anche se restituiscono un timestamp e la lettura di nuovo allo stesso timestamp è ripetibile. Due transazioni di sola lettura consecutive potrebbero produrre risultati diversi a causa di scritture simultanee. Le query sui modifiche in tempo reale devono utilizzare questo limite. Per maggiori dettagli, vedi TransactionOptions.ReadOnly.strong.
- Stalezza esatta:questa opzione esegue le letture in un timestamp specificato, come timestamp assoluto o come durata di stalezza relativa all'ora corrente. Garantisce di osservare un prefisso coerente della cronologia delle transazioni globali fino a quel timestamp e blocca le transazioni in conflitto che potrebbero essere eseguite con un timestamp inferiore o uguale al timestamp di lettura. Sebbene leggermente più veloce delle modalità di obsolescenza limitata, potrebbe restituire dati meno recenti. Per maggiori dettagli, vedi TransactionOptions.ReadOnly.read_timestamp e TransactionOptions.ReadOnly.exact_staleness.
- Bounded staleness: Spanner seleziona il timestamp più recente entro un limite di non aggiornamento definito dall'utente, consentendo l'esecuzione nella replica disponibile più vicina senza blocchi. Tutte le righe restituite sono coerenti. Come le letture coerenti, la non aggiornatezza vincolata non è ripetibile, poiché letture diverse potrebbero essere eseguite in timestamp diversi anche con lo stesso limite. Queste letture operano in due fasi (negoziazione del timestamp, quindi lettura) e di solito sono leggermente più lente della non aggiornamento esatto, ma spesso restituiscono risultati più recenti e hanno maggiori probabilità di essere eseguite in una replica locale. Questa modalità è disponibile solo per le transazioni di sola lettura monouso perché la negoziazione del timestamp richiede di sapere in anticipo quali righe verranno lette. Per maggiori dettagli, vediTransactionOptions.ReadOnly.max_staleness e TransactionOptions.ReadOnly.min_read_timestamp.
Transazioni DML partizionate
Puoi utilizzare DML partizionato per eseguire
istruzioni UPDATE
e DELETE
su larga scala senza riscontrare limiti di transazione
o bloccare un'intera tabella. Spanner lo fa partizionando lo spazio delle chiavi ed eseguendo le istruzioni DML su ogni partizione all'interno di una transazione di lettura/scrittura separata.
Per utilizzare il DML non partizionato, esegui istruzioni all'interno di transazioni di lettura/scrittura che crei esplicitamente nel codice. Per maggiori dettagli, vedi Utilizzo di DML.
Interfaccia
Spanner fornisce l'interfaccia TransactionOptions.partitionedDml per l'esecuzione di una singola istruzione DML partizionata.
Esempi
Il seguente esempio di codice aggiorna la colonna MarketingBudget
della tabella Albums
.
C++
Utilizzi la funzione ExecutePartitionedDml()
per eseguire un'istruzione DML partizionata.
C#
Utilizzi il metodo ExecutePartitionedUpdateAsync()
per eseguire un'istruzione DML partizionata.
Vai
Utilizzi il metodo PartitionedUpdate()
per eseguire un'istruzione DML partizionata.
Java
Utilizzi il metodo executePartitionedUpdate()
per eseguire un'istruzione DML partizionata.
Node.js
Utilizzi il metodo runPartitionedUpdate()
per eseguire un'istruzione DML partizionata.
PHP
Utilizzi il metodo executePartitionedUpdate()
per eseguire un'istruzione DML partizionata.
Python
Utilizzi il metodo execute_partitioned_dml()
per eseguire un'istruzione DML partizionata.
Ruby
Utilizzi il metodo execute_partitioned_update()
per eseguire un'istruzione DML partizionata.
Il seguente esempio di codice elimina le righe dalla tabella Singers
in base alla
colonna SingerId
.
C++
C#
Go
Java
Node.js
PHP
Python
Ruby
Semantica
Questa sezione descrive la semantica per DML partizionato.
Informazioni sull'esecuzione di DML partizionato
Puoi eseguire una sola istruzione DML partizionata alla volta, sia che utilizzi un metodo della libreria client sia Google Cloud CLI.
Le transazioni partizionate non supportano i commit o i rollback. Spanner esegue e applica immediatamente l'istruzione DML. Se annulli l'operazione o l'operazione non va a buon fine, Spanner annulla tutte le partizioni in esecuzione e non avvia quelle rimanenti. Tuttavia, Spanner non esegue il rollback delle partizioni già eseguite.
Strategia di acquisizione dei blocchi DML partizionati
Per ridurre il conflitto di blocco, DML partizionato acquisisce blocchi di lettura solo sulle righe che
corrispondono alla clausola WHERE
. Anche le transazioni più piccole e indipendenti utilizzate per ogni
partizione mantengono i blocchi per un periodo di tempo inferiore.
Limiti delle transazioni per sessione
Ogni sessione in Spanner può avere una sola transazione attiva alla volta. Sono incluse letture e query autonome, che internamente utilizzano una transazione e vengono conteggiate ai fini di questo limite. Una volta completata una transazione, la sessione può essere riutilizzata immediatamente per la transazione successiva. Non è necessario creare una nuova sessione per ogni transazione.
Timestamp di lettura precedenti e garbage collection delle versioni
Spanner esegue la garbage collection delle versioni per raccogliere i dati eliminati o sovrascritti e recuperare spazio di archiviazione. Per impostazione predefinita, i dati precedenti a un'ora vengono
recuperati. Spanner non può eseguire letture in corrispondenza di timestamp precedenti al VERSION_RETENTION_PERIOD
configurato, che per impostazione predefinita è di un'ora, ma può essere configurato fino a una settimana. Quando le letture diventano troppo vecchie durante l'esecuzione, non vanno a buon fine e restituiscono l'errore FAILED_PRECONDITION
.
Query sui modifiche in tempo reale
Uno stream di modifiche è un oggetto schema che puoi configurare per monitorare le modifiche ai dati in un intero database, in tabelle specifiche o in un insieme definito di colonne all'interno di un database.
Quando crei un flusso di modifiche, Spanner definisce una
funzione TVF (Table-Valued Function) SQL corrispondente. Puoi utilizzare questa TVF per eseguire query sui
record di modifica nel flusso di modifiche associato con il
metodo sessions.executeStreamingSql
. Il nome della TVF viene generato dal nome dello stream di modifiche e inizia sempre
con READ_
.
Tutte le query sulle funzioni TVF di flusso di modifiche devono essere eseguite utilizzando l'API sessions.executeStreamingSql
all'interno di una transazione di sola lettura monouso con un timestamp_bound
di sola lettura avanzato. Il TVF del flusso di modifiche consente di
specificare start_timestamp
e end_timestamp
per l'intervallo di tempo. Tutti i record
di modifica nel periodo di conservazione sono accessibili utilizzando questo timestamp_bound
di sola lettura
sicuro. Tutti gli altri
TransactionOptions
non sono
validi per le query di modifica in tempo reale.
Inoltre, se
TransactionOptions.read_only.return_read_timestamp
è impostato su true
, il
messaggio Transaction
che descrive
la transazione restituisce un valore speciale di 2^63 - 2
anziché un timestamp di lettura
valido. Devi ignorare questo valore speciale e non utilizzarlo per
query successive.
Per saperne di più, vedi Flusso di lavoro delle query sugli stream di modifiche.
Transazioni inattive
Una transazione viene considerata inattiva se non ha letture o query SQL in sospeso
e non ne ha avviata una negli ultimi 10 secondi. Spanner può interrompere
le transazioni inattive per impedire loro di mantenere i blocchi a tempo indeterminato. Se una transazione inattiva viene interrotta, il commit non va a buon fine e viene restituito un errore ABORTED
.
L'esecuzione periodica di una piccola query, ad esempio SELECT 1
, all'interno della transazione
può impedirne l'inattività.