Comprendi le operazioni di lettura e scrittura su larga scala

Leggi questo documento per prendere decisioni consapevoli su come progettare l'architettura delle tue applicazioni per ottenere prestazioni e affidabilità elevate. Questo documento include argomenti Cloud Firestore avanzati. Se hai appena iniziato a utilizzare Cloud Firestore, consulta la guida rapida.

Cloud Firestore è un database flessibile e scalabile per lo sviluppo di dispositivi mobili, web e server di Firebase e Google Cloud. È molto facile iniziare a utilizzare Cloud Firestore e scrivere applicazioni complete e potenti.

Per assicurarti che le tue applicazioni continuino a funzionare bene con l'aumento delle dimensioni e del traffico del database, è utile comprendere la dinamica delle letture e delle scritture nel Cloud Firestorebackend. Devi anche comprendere l'interazione tra operazioni di lettura e scrittura con il livello di archiviazione e i vincoli sottostanti che potrebbero influire sulle prestazioni.

Consulta le sezioni seguenti per le best practice prima di progettare l'architettura dell'applicazione.

Informazioni sui componenti di alto livello

Il seguente diagramma mostra i componenti di alto livello coinvolti in una richiesta all'API Cloud Firestore.

Componenti di alto livello

Cloud Firestore SDK e librerie client

Cloud Firestore supporta SDK e librerie client per piattaforme diverse. Sebbene un'app possa effettuare chiamate HTTP e RPC dirette all'API Cloud Firestore, le librerie client forniscono un livello di astrazione per semplificare l'utilizzo dell'API e implementare le best practice. Potrebbero anche fornire funzionalità aggiuntive come l'accesso offline, le cache e così via.

Google Front End (GFE)

Si tratta di un servizio di infrastruttura comune a tutti i servizi Google Cloud. Il GFE accetta le richieste in arrivo e le inoltra al servizio Google pertinente (servizio Cloud Firestore in questo contesto). Fornisce inoltre altre funzionalità importanti, tra cui la protezione contro gli attacchi di tipo Denial of Service.

Servizio Cloud Firestore

Il servizio Cloud Firestore esegue controlli sulla richiesta API, che includono autenticazione, autorizzazione, controlli delle quote e regole di sicurezza, nonché gestisce le transazioni. Questo servizio Cloud Firestore include un client di archiviazione che interagisce con il livello di archiviazione per le letture e le scritture dei dati.

Livello di archiviazione Cloud Firestore

Il livello di archiviazione Cloud Firestore è responsabile dell'archiviazione sia dei dati che dei metadati, nonché delle funzionalità di database associate fornite da Cloud Firestore. Le sezioni seguenti descrivono come vengono organizzati i dati nel livello di archiviazione Cloud Firestore e come il sistema si scala. Comprendere come sono organizzati i dati può aiutarti a progettare un modello di dati scalabile e a comprendere meglio le best practice in Cloud Firestore.

Intervalli e suddivisioni di chiavi

Cloud Firestore è un database NoSQL orientato ai documenti. Archivi i dati in documenti, organizzati in gerarchie di raccolte. La gerarchia delle raccolte e l'ID documento sono tradotti in un'unica chiave per ogni documento. I documenti vengono archiviati in modo logico e ordinati in ordine alfabetico in base a questa singola chiave. Utilizziamo il termine intervallo di chiavi per fare riferimento a un intervallo di chiavi contigue dal punto di vista lessicografico.

Un tipico database Cloud Firestore è troppo grande per essere ospitato su una singola macchina fisica. Esistono anche scenari in cui il carico di lavoro sui dati è troppo elevato per essere gestito da una sola macchina. Per gestire carichi di lavoro di grandi dimensioni, Cloud Firestore esegue il partizionamento dei dati in parti separate che possono essere archiviate e gestite da più macchine o server di archiviazione. Queste partizioni vengono create nelle tabelle del database in blocchi di intervalli di chiavi chiamati suddivisioni.

Replica sincrona

È importante notare che il database viene sempre replicato automaticamente e in modo sincrono. Le suddivisioni dei dati hanno repliche in zone diverse per mantenerle disponibili anche quando una zona diventa inaccessibile. La replica coerente alle diverse copie della suddivisione è gestita dall'algoritmo Paxos per il consenso. Una replica di ogni suddivisione viene eletta come leader Paxos, responsabile della gestione delle scritture nella suddivisione. La replica sincrona ti consente di leggere sempre la versione più recente dei dati da Cloud Firestore.

Il risultato complessivo è un sistema scalabile e altamente disponibile che offre latenze ridotte sia per le letture che per le scritture, indipendentemente dai carichi di lavoro elevati e su larga scala.

Layout dei dati

Cloud Firestore è un database di documenti senza schema. Tuttavia, internamente organizza i dati principalmente in due tabelle in stile database relazionale nel livello di archiviazione come segue:

  • Tabella Documenti: i documenti vengono archiviati in questa tabella.
  • Tabella Indici: in questa tabella vengono memorizzate le voci dell'indice che consentono di ottenere risultati in modo efficiente e ordinati in base al valore dell'indice.

Il seguente diagramma mostra come potrebbero essere le tabelle di un database Cloud Firestore con le suddivisioni. I tempi vengono replicati in tre zone diverse e a ogni divisione è assegnato un leader Paxos.

Layout dei dati

Regione singola o multiregione

Quando crei un database, devi selezionare una regione o più regioni.

Una singola località regionale rappresenta una località geografica specifica, ad esempio us-west1. Le suddivisioni dei dati di un database Cloud Firestore hanno repliche in zone diverse all'interno della regione selezionata, come spiegato in precedenza.

Una località multiregionale è costituita da un insieme definito di regioni in cui sono archiviate le repliche del database. In un deployment multi-regione di Cloud Firestore, due delle regioni hanno repliche complete dell'intero set di dati nel database. Una terza regione include una replica di test che non gestisce un set completo di dati, ma partecipa alla replica. Se li replichi in più regioni, i dati possono essere scritti e letti anche in caso di perdita di un'intera regione.

Per saperne di più sulle località di una regione, consulta Località Cloud Firestore.

Regione singola o più regioni

Informazioni sul ciclo di vita di una scrittura in Cloud Firestore

Un client Cloud Firestore può scrivere dati creando, aggiornando o eliminando un singolo documento. Una scrittura in un singolo documento richiede l'aggiornamento atomico sia del documento sia delle relative voci dell'indice nel livello di archiviazione. Cloud Firestore supporta anche le operazioni atomiche costituite da più letture e/o scritture in uno o più documenti.

Per tutti i tipi di scritture, Cloud Firestore fornisce le proprietà ACID (atomicità, coerenza, isolamento e durabilità) dei database relazionali. Cloud Firestore offre inoltre la serializzabilità, il che significa che tutte le transazioni appaiono come se fossero eseguite in un ordine seriale.

Passaggi generali in una transazione di scrittura

Quando il client Cloud Firestore esegue una scrittura o esegue il commit di una transazione utilizzando uno dei metodi sopra indicati, internamente viene eseguita come transazione di lettura/scrittura del database nel livello di archiviazione. La transazione consente a Cloud Firestore di fornire le proprietà ACID menzionate in precedenza.

Come primo passaggio di una transazione, Cloud Firestore legge il documento esistente e determina le mutazioni da apportare ai dati nella tabella Documenti.

Ciò include anche gli aggiornamenti necessari alla tabella Indici come segue:

  • I campi che vengono aggiunti ai documenti richiedono inserimenti corrispondenti nella tabella Indici.
  • I campi rimossi dai documenti richiedono eliminazioni corrispondenti nella tabella Indici.
  • I campi che vengono modificati nei documenti richiedono sia le eliminazioni (per i valori precedenti) sia le inserzioni (per i valori nuovi) nella tabella Indici.

Per calcolare le mutazioni menzionate in precedenza, Cloud Firestore legge la configurazione di indicizzazione del progetto. La configurazione dell'indicizzazione memorizza le informazioni sugli indici di un progetto. Cloud Firestore utilizza due tipi di indici: a campo singolo e composti. Per una comprensione dettagliata degli indici creati in Cloud Firestore, consulta Tipi di indici in Cloud Firestore.

Una volta calcolate le mutazioni, Cloud Firestore le raccoglie all'interno di una transazione e poi le esegue.

Informazioni su una transazione di scrittura nel livello di archiviazione

Come discusso in precedenza, una scrittura in Cloud Firestore comporta una transazione di lettura/scrittura nel livello di archiviazione. A seconda del layout dei dati, una scrittura potrebbe comportare una o più suddivisioni, come mostrato nel layout dei dati.

Nel diagramma seguente, il database Cloud Firestore ha otto suddivisioni (contrassegnate da 1 a 8) ospitate su tre diversi server di archiviazione in una singola zona e ogni suddivisione viene replicata in 3(o più) zone diverse. Ogni suddivisione ha un leader Paxos, che potrebbe trovarsi in una zona diversa per suddivisioni diverse.

<span class=Suddivisione del database Cloud Firestore">

Considera un database Cloud Firestore con la raccolta Restaurants come segue:

Raccolta di ristoranti

Il cliente Cloud Firestore richiede la seguente modifica a un documento nella raccolta Restaurant aggiornando il valore del campo priceCategory.

Modificare un documento nella raccolta

I seguenti passaggi generali descrivono cosa succede durante la scrittura:

  1. Crea una transazione di lettura/scrittura.
  2. Leggi il documento restaurant1 nella raccolta Restaurants dalla tabella Documenti del livello di archiviazione.
  3. Leggi gli indici del documento dalla tabella Indici.
  4. Calcola le mutazioni da apportare ai dati. In questo caso, sono presenti cinque mutazioni:
    • M1: aggiorna la riga relativa a restaurant1 nella tabella Documenti in modo che rifletta la variazione di valore del campo priceCategory.
    • M2 e M3: elimina le righe per il valore precedente di priceCategory nella tabella Indici per gli indici in ordine decrescente e in ordine crescente.
    • M4 e M5: inserisci le righe per il nuovo valore di priceCategory nella tabella Indici per gli indici decrescenti e crescenti.
  5. Esegui il commit di queste mutazioni.

Il client di archiviazione nel servizio Cloud Firestore cerca le suddivisioni proprietarie delle chiavi delle righe da modificare. Supponiamo che il segmento 3 serva M1 e il segmento 6 M2-M5. Esiste una transazione distribuita che coinvolge tutte queste suddivisioni come partecipanti. Le suddivisioni dei partecipanti possono anche includere qualsiasi altra suddivisione da cui i dati sono stati letti in precedenza come parte della transazione di lettura-scrittura.

I passaggi seguenti descrivono cosa succede durante il commit:

  1. Il client di archiviazione esegue un commit. Il commit contiene le mutazioni M1-M5.
  2. Le suddivisioni 3 e 6 sono i partecipanti a questa transazione. Uno dei partecipanti viene scelto come coordinatore, ad esempio il gruppo 3. Il compito del coordinatore è assicurarsi che la transazione venga confermata o interrotta a livello atomico tra tutti i partecipanti.
    • Le repliche leader di queste suddivisioni sono responsabili del lavoro svolto dai partecipanti e dai coordinatori.
  3. Ogni partecipante e coordinatore esegue un algoritmo Paxos con le rispettive repliche.
    • Il leader esegue un algoritmo Paxos con le repliche. Il quorum viene ottenuto se la maggior parte delle repliche risponde con una risposta ok to commit al leader.
    • Ogni partecipante comunica al coordinatore quando è pronto (prima fase del commit a due fasi). Se un partecipante non può impegnare la transazione, l'intera transazione aborts.
  4. Una volta che il coordinatore sa che tutti i partecipanti, incluso lui stesso, sono pronti, comunica l'esito della transazione accept a tutti i partecipanti (seconda fase del commit a due fasi). In questa fase, ogni partecipante registra la decisione di commit nello spazio di archiviazione stabile e la transazione viene eseguita.
  5. Il coordinatore risponde al client di archiviazione in Cloud Firestore che la transazione è stata eseguita. In parallelo, il coordinatore e tutti i partecipanti applicano le mutazioni ai dati.

Ciclo di vita del commit

Quando il database Cloud Firestore è piccolo, può accadere che una singola suddivisione possieda tutte le chiavi nelle mutazioni M1-M5. In questo caso, la transazione ha un solo partecipante e l'commit a due fasi menzionato in precedenza non è necessario, il che rende le scritture più veloci.

Scritture in più regioni

In un deployment multi-regione, la distribuzione delle repliche nelle regioni aumenta la disponibilità, ma comporta un costo in termini di prestazioni. La comunicazione tra le repliche in regioni diverse richiede tempi di percorrenza più lunghi. Di conseguenza, la latenza di riferimento per le operazioni Cloud Firestore è leggermente superiore rispetto ai deployment in una singola regione.

Configuramo le repliche in modo che il ruolo di leader per le suddivisioni rimanga sempre nella regione principale. La regione principale è quella da cui proviene il traffico in entrata sul server Cloud Firestore. Questa decisione del team di gestione riduce il ritardo di andata e ritorno nella comunicazione tra il client di archiviazione in Cloud Firestore e il leader della replica (o il coordinatore per le transazioni con più suddivisioni).

Ogni scrittura in Cloud Firestore comporta anche un'interazione con il motore in tempo reale in Cloud Firestore. Per saperne di più sulle query in tempo reale, consulta Informazioni sulle query in tempo reale su larga scala.

Informazioni sul ciclo di vita di una lettura in Cloud Firestore

Questa sezione illustra le letture autonome e non in tempo reale in Cloud Firestore. Internamente, il server Cloud Firestore gestisce la maggior parte di queste query in due fasi principali:

  1. Una singola scansione di intervallo sulla tabella Indici
  2. Ricerche per punto nella tabella Documenti in base ai risultati della scansione precedente
In Cloud Firestore potrebbero esserci determinate query che richiedono meno o più elaborazione (ad es. query IN).

Le letture dei dati dal livello di archiviazione vengono eseguite internamente utilizzando una transazione di database per garantire letture coerenti. Tuttavia, a differenza delle transazioni utilizzate per le scritture, queste transazioni non richiedono blocchi. Funzionano invece scegliendo un timestamp, quindi eseguendo tutte le letture in quel timestamp. Poiché non acquisiscono blocchi, non bloccano le transazioni simultanee di lettura e scrittura. Per eseguire questa transazione, il client di archiviazione in Cloud Firestore specifica un limite di timestamp, che indica al livello di archiviazione come scegliere un timestamp di lettura. Il tipo di limite di timestamp scelto dal client di archiviazione in Cloud Firestore è determinato dalle opzioni di lettura per la richiesta di lettura.

Informazioni su una transazione di lettura nel livello di archiviazione

Questa sezione descrive i tipi di letture e il modo in cui vengono elaborati nel livello di archiviazione in Cloud Firestore.

Letture efficaci

Per impostazione predefinita, le letture di Cloud Firestore sono a elevata coerenza. Questa elevata coerenza significa che una lettura Cloud Firestore restituisce la versione più recente dei dati che riflette tutte le scritture di cui è stato eseguito il commit fino all'inizio della lettura.

Lettura con suddivisione singola

Il client di archiviazione in Cloud Firestore cerca le suddivisioni che possiedono le chiavi delle righe da leggere. Supponiamo che debba eseguire una lettura dalla partizione 3 della sezione precedente. Il client invia la richiesta di lettura alla replica più vicina per ridurre la latenza di round trip.

A questo punto, a seconda della replica scelta, potrebbero verificarsi i seguenti casi:

  • La richiesta di lettura viene inviata a una replica leader (zona A).
    • Poiché il leader è sempre aggiornato, la lettura può procedere direttamente.
  • La richiesta di lettura va a una replica non leader (ad esempio, Zona B)
    • Il partizionamento 3 potrebbe sapere dal proprio stato interno di avere informazioni sufficienti per eseguire la lettura e lo fa.
    • Il segmento 3 non è sicuro di aver visto i dati più recenti. Invia un messaggio al leader per chiedere il timestamp dell'ultima transazione da applicare per eseguire la lettura. Una volta applicata la transazione, la lettura può procedere.

Cloud Firestore restituisce quindi la risposta al client.

Lettura multi-diviso

Se le letture devono essere eseguite da più suddivisioni, viene utilizzato lo stesso meccanismo per tutte le suddivisioni. Una volta che i dati sono stati restituiti da tutte le suddivisioni, il client di archiviazione in Cloud Firestore combina i risultati. Cloud Firestore risponde quindi al proprio cliente con questi dati.

Letture non aggiornate

Le letture efficaci sono la modalità predefinita in Cloud Firestore. Tuttavia, comporta una potenziale latenza più elevata a causa della comunicazione che potrebbe essere richiesta con il leader. Spesso l'applicazione Cloud Firestore non ha bisogno di leggere la versione più recente dei dati e la funzionalità funziona bene con dati che potrebbero essere obsoleti di alcuni secondi.

In questo caso, il client può scegliere di ricevere letture non aggiornate utilizzando le opzioni di lettura read_time. In questo caso, le letture vengono eseguite perché i dati si trovavano in read_time ed è molto probabile che la replica più vicina abbia già verificato di disporre dei dati per il valore read_time specificato. Per un rendimento notevolmente migliore, 15 secondi sono un valore di inattività ragionevole. Anche per le letture non aggiornate, le righe restituite sono coerenti tra loro.

Evita gli hotspot

Le suddivisioni in Cloud Firestore vengono suddivise automaticamente in parti più piccole per distribuire il lavoro di invio del traffico a più server di archiviazione, se necessario o quando lo spazio delle chiavi si espande. Le suddivisioni create per gestire il traffico in eccesso vengono conservate per circa 24 ore anche se il traffico scompare. Pertanto, se si verificano picchi di traffico ricorrenti, le suddivisioni vengono mantenute e vengono introdotte altre suddivisioni, se necessario. Questi meccanismi consentono ai database Cloud Firestore di eseguire la scalabilità automatica in caso di aumento del carico del traffico o delle dimensioni del database. Tuttavia, ci sono alcune limitazioni da tenere presente, come spiegato di seguito.

La suddivisione dello spazio di archiviazione e del carico richiede tempo e l'aumento troppo rapido del traffico può causare una latenza elevata o errori di scadenza superata, comunemente noti come hotspot, durante l'adattamento del servizio. La best practice è distribuire le operazioni nell'intervallo di chiavi, aumentando al contempo il traffico su una raccolta in un database con 500 operazioni al secondo. Dopo questo aumento graduale, aumenta il traffico fino al 50% ogni cinque minuti. Questo processo è chiamato regola 500/50/5 e consente di scalare il database in modo ottimale per soddisfare il carico di lavoro.

Anche se le suddivisioni vengono create automaticamente con l'aumento del carico, Cloud Firestore può suddividere un intervallo di chiavi solo fino a quando non gestisce un singolo documento utilizzando un set dedicato di server di archiviazione replicati. Di conseguenza, volumi elevati e sostenuti di operazioni simultanee su un singolo documento possono portare a un hotspot in quel documento. Se riscontri latenze elevate e prolungate su un singolo documento, ti consigliamo di modificare il modello di dati per suddividere o replicare i dati in più documenti.

Gli errori di contesa si verificano quando più operazioni tentano di leggere e/o scrivere lo stesso documento contemporaneamente.

Un altro caso speciale di hotspot si verifica quando viene utilizzata una chiave in aumento/diminuzione sequenziale come ID documento in Cloud Firestore e il numero di operazioni al secondo è molto elevato. In questo caso, la creazione di più suddivisioni non è utile, poiché l'aumento del traffico si sposta semplicemente nella suddivisione appena creata. Poiché Cloud Firestore indicizza automaticamente tutti i campi del documento per impostazione predefinita, questi hotspot in movimento possono essere creati anche nello spazio dell'indice per un campo del documento che contiene un valore in aumento/diminuzione sequenziale, ad esempio un timestamp.

Tieni presente che, seguendo le pratiche descritte sopra, Cloud Firestore può scalare per gestire carichi di lavoro arbitrariamente grandi senza che tu debba modificare alcuna configurazione.

Risoluzione dei problemi

Cloud Firestore fornisce Key Visualizer come strumento di diagnostica progettato per analizzare i pattern di utilizzo e risolvere i problemi di hotspot.

Passaggi successivi