Comprendi le letture e le scritture su larga scala

Leggi questo documento per prendere decisioni informate sull'architettura delle tue applicazioni per garantire prestazioni elevate e affidabilità. Questo documento include argomenti avanzati su Cloud Firestore. Se hai appena iniziato a utilizzare Cloud Firestore, consulta invece la guida rapida .

Cloud Firestore è un database flessibile e scalabile per lo sviluppo di dispositivi mobili, Web e server da Firebase e Google Cloud. È molto semplice iniziare con Cloud Firestore e scrivere applicazioni ricche e potenti.

Per assicurarti che le tue applicazioni continuino a funzionare bene con l'aumento delle dimensioni del database e del traffico, è utile comprendere i meccanismi di lettura e scrittura nel backend Cloud Firestore. È inoltre necessario comprendere l'interazione delle 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 la tua applicazione.

Comprendere i componenti di alto livello

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

Componenti di alto livello

SDK Cloud Firestore e librerie client

Cloud Firestore supporta SDK e librerie client per diverse piattaforme. 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. Possono anche fornire funzionalità aggiuntive come accesso offline, cache e così via.

Front-end di Google (GFE)

Si tratta di un servizio di infrastruttura comune a tutti i servizi cloud di Google. Il GFE accetta le richieste in arrivo e le inoltra al servizio Google pertinente (servizio Firestore in questo contesto). Fornisce inoltre altre importanti funzionalità, inclusa la protezione contro gli attacchi 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, oltre a gestire 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 di Cloud Firestore è responsabile dell'archiviazione sia dei dati che dei metadati, nonché delle funzionalità del database associate fornite da Cloud Firestore. Le sezioni seguenti descrivono come sono organizzati i dati nel livello di archiviazione di Cloud Firestore e come il sistema è scalabile. Imparare 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 chiave e suddivisioni

Cloud Firestore è un database NoSQL orientato ai documenti. I dati vengono archiviati in documenti organizzati in gerarchie di raccolte . La gerarchia della raccolta e l'ID del documento vengono tradotti in un'unica chiave per ciascun documento. I documenti vengono archiviati logicamente e ordinati lessicograficamente tramite questa unica chiave. Usiamo il termine intervallo di chiavi per riferirci a un intervallo di chiavi lessicograficamente contiguo.

Un tipico database Cloud Firestore è troppo grande per adattarsi a un singolo computer fisico. Esistono anche scenari in cui il carico di lavoro sui dati è troppo pesante per essere gestito da una sola macchina. Per gestire carichi di lavoro di grandi dimensioni, Cloud Firestore suddivide i dati in parti separate che possono essere archiviate e servite 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 mantenerli disponibili anche quando una zona diventa inaccessibile. La replica coerente nelle diverse copie della divisione è gestita dall'algoritmo Paxos per il consenso. Una replica di ciascuna divisione viene eletta per fungere da leader di Paxos, responsabile della gestione delle scritture su quella divisione. La replica sincrona ti dà la possibilità di poter sempre leggere la versione più recente dei dati da Cloud Firestore.

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

Disposizione dei dati

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

  • Tabella documenti : i documenti vengono archiviati in questa tabella.
  • Tabella degli indici : in questa tabella vengono archiviate le voci dell'indice che consentono di ottenere risultati in modo efficiente e ordinate in base al valore dell'indice.

Il diagramma seguente mostra come potrebbero apparire le tabelle per un database Cloud Firestore con le suddivisioni. Le divisioni vengono replicate in tre zone diverse e ciascuna divisione ha un leader Paxos assegnato.

Disposizione dei dati

Singola regione o multiregione

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

Una singola posizione regionale è una posizione geografica specifica, come 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 posizione a più regioni è costituita da un insieme definito di regioni in cui sono archiviate le repliche del database. In una distribuzione multiregione di Cloud Firestore, due regioni dispongono di repliche complete di tutti i dati nel database. Una terza area dispone di una replica testimone che non mantiene un set completo di dati, ma partecipa alla replica. Replicando i dati tra più regioni, i dati possono essere scritti e letti anche con la perdita di un'intera regione.

Per ulteriori informazioni sulle posizioni di una regione, consulta Posizioni Cloud Firestore .

Singola regione o multiregione

Comprendi la durata di una scrittura in Cloud Firestore

Un client Cloud Firestore può scrivere dati creando, aggiornando o eliminando un singolo documento. Una scrittura su un singolo documento richiede l'aggiornamento atomico sia del documento che delle voci di indice associate nel livello di archiviazione. Cloud Firestore supporta anche operazioni atomiche costituite da più letture e/o scritture su 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 fornisce anche la serializzabilità , il che significa che tutte le transazioni appaiono come se fossero eseguite in un ordine seriale.

Passaggi di alto livello in una transazione di scrittura

Quando il client Cloud Firestore emette una scrittura o esegue il commit di una transazione, utilizzando uno dei metodi menzionati in precedenza, questa viene eseguita internamente 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 modifiche 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 necessitano di inserti corrispondenti nella tabella Indici.
  • I campi che vengono rimossi dai documenti necessitano di eliminazioni corrispondenti nella tabella Indici.
  • I campi che vengono modificati nei documenti necessitano sia di eliminazioni (per vecchi valori) che di inserti (per nuovi valori) nella tabella Indici.

Per calcolare le mutazioni menzionate in precedenza, Cloud Firestore legge la configurazione di indicizzazione del progetto. La configurazione di indicizzazione memorizza le informazioni sugli indici per un progetto. Cloud Firestore utilizza due tipi di indici: a campo singolo e compositi. 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 la conferma.

Comprendere 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 dispone di otto suddivisioni (contrassegnate da 1 a 8) ospitate su tre diversi server di archiviazione in una singola zona e ciascuna suddivisione viene replicata in 3 (o più) zone diverse. Ogni split ha un leader Paxos, che potrebbe trovarsi in una zona diversa per split diversi.

Divisione del database Cloud Firestore

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

Collezione ristorante

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

Passare a un documento nella raccolta

I seguenti passaggi di alto livello descrivono cosa succede come parte della scrittura:

  1. Crea una transazione di lettura-scrittura.
  2. Leggi il documento restaurant1 nella raccolta Restaurants dalla tabella Documents dal livello di archiviazione.
  3. Leggere gli indici del documento dalla tabella Indici .
  4. Calcolare le mutazioni da apportare ai dati. In questo caso, ci sono cinque mutazioni:
    • M1: aggiorna la riga per restaurant1 nella tabella Documenti per riflettere la modifica del valore del campo priceCategory .
    • M2 e M3: elimina le righe per il vecchio valore di priceCategory nella tabella Indici per indici discendenti e ascendenti.
    • M4 e M5: inserire le righe per il nuovo valore di priceCategory nella tabella Indici per indici discendenti e ascendenti.
  5. Commetti queste mutazioni.

Il client di archiviazione nel servizio Cloud Firestore ricerca le suddivisioni che possiedono le chiavi delle righe da modificare. Consideriamo il caso in cui lo Split 3 serve M1 e lo Split 6 serve M2-M5. Esiste una transazione distribuita, che coinvolge tutte queste divisioni come partecipanti . Le suddivisioni dei partecipanti possono includere anche qualsiasi altra suddivisione da cui i dati sono stati letti in precedenza come parte della transazione di lettura-scrittura.

I passaggi seguenti descrivono cosa accade come parte del commit:

  1. Il client di archiviazione emette un commit. Il commit contiene le mutazioni M1-M5.
  2. Gli split 3 e 6 sono i partecipanti a questa transazione. Uno dei partecipanti viene scelto come coordinatore , come nel caso della divisione 3. Il compito del coordinatore è quello di assicurarsi che la transazione venga confermata o interrotta atomicamente tra tutti i partecipanti.
    • Le repliche leader di queste divisioni 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 raggiunto se la maggior parte delle repliche risponde con un ok to commit della risposta al leader.
    • Ogni partecipante poi avvisa il coordinatore quando è pronto (prima fase del commit a due fasi). Se un partecipante non riesce a confermare la transazione, l'intera transazione aborts .
  4. Una volta che il coordinatore sa che tutti i partecipanti, incluso se stesso, sono preparati, comunica l'esito della transazione accept a tutti i partecipanti (seconda fase del commit a due fasi). In questa fase, ciascun partecipante registra la decisione di commit nell'archivio stabile e la transazione viene confermata.
  5. Il coordinatore risponde al client di archiviazione in Cloud Firestore informando che la transazione è stata confermata. Parallelamente, il coordinatore e tutti i partecipanti applicano le mutazioni ai dati.

Ciclo di vita dell'impegno

Quando il database di Cloud Firestore è piccolo, può succedere che un singolo split possieda tutte le chiavi nelle mutazioni M1-M5. In tal caso, c'è un solo partecipante alla transazione e il commit in due fasi menzionato in precedenza non è richiesto, rendendo così le scritture più veloci.

Scrive in più regioni

In una distribuzione su più aree, la diffusione delle repliche tra aree aumenta la disponibilità, ma comporta un costo in termini di prestazioni. La comunicazione tra repliche in regioni diverse richiede tempi di andata e ritorno più lunghi. Pertanto, la latenza di base per le operazioni Cloud Firestore è leggermente superiore rispetto alle distribuzioni in una singola regione.

Configuriamo le repliche in modo che la leadership per le divisioni rimanga sempre nella regione primaria. La regione primaria è quella da cui il traffico arriva al server Cloud Firestore. Questa decisione della leadership riduce il ritardo di andata e ritorno nella comunicazione tra il client di archiviazione in Cloud Firestore e il leader della replica (o coordinatore per transazioni multi-split).

Ogni scrittura in Cloud Firestore comporta anche una certa interazione con il motore in tempo reale in Cloud Firestore. Per ulteriori informazioni sulle query in tempo reale, consulta Comprendere le query in tempo reale su larga scala .

Comprendi la vita di una lettura in Cloud Firestore

Questa sezione approfondisce 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. Scansione di un singolo intervallo sulla tabella degli indici
  2. Ricerca punti nella tabella Documenti in base al risultato della scansione precedente
Potrebbero esserci alcune query che richiedono meno o più elaborazione (ad esempio, query IN) in Cloud Firestore.

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 accettano blocchi. Funzionano invece scegliendo un timestamp, quindi eseguendo tutte le letture in quel timestamp. Poiché non acquisiscono blocchi, non bloccano le transazioni di lettura-scrittura simultanee. 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 temporale scelto dal client di archiviazione in Cloud Firestore è determinato dalle opzioni di lettura per la richiesta di lettura.

Comprendere 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 forti

Per impostazione predefinita, le letture di Cloud Firestore sono fortemente coerenti . Questa elevata coerenza significa che una lettura di Cloud Firestore restituisce la versione più recente dei dati che riflette tutte le scritture eseguite fino all'inizio della lettura.

Lettura divisa singola

Il client di archiviazione in Cloud Firestore cerca le suddivisioni che possiedono le chiavi delle righe da leggere. Supponiamo che sia necessario eseguire una lettura dallo Split 3 della sezione precedente. Il client invia la richiesta di lettura alla replica più vicina per ridurre la latenza di andata e ritorno.

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

  • La richiesta di lettura va 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 (come la Zona B)
    • La divisione 3 può sapere dal suo stato interno di avere informazioni sufficienti per servire la lettura e la divisione lo fa.
    • Split 3 non è sicuro di aver visto i dati più recenti. Invia un messaggio al leader per chiedere il timestamp dell'ultima transazione che deve applicare per servire la lettura. Una volta applicata la transazione, la lettura può procedere.

Cloud Firestore restituisce quindi la risposta al proprio client.

Lettura multisplit

Nella situazione in cui le letture devono essere eseguite da più suddivisioni, lo stesso meccanismo avviene in tutte le suddivisioni. Una volta restituiti i dati da tutte le suddivisioni, il client di archiviazione in Cloud Firestore combina i risultati. Cloud Firestore risponde quindi al suo client con questi dati.

Letture stantie

Le letture efficaci sono la modalità predefinita in Cloud Firestore. Tuttavia, ciò comporta un costo di latenza potenzialmente maggiore 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 per alcuni secondi.

In tal caso, il client può scegliere di ricevere letture non aggiornate utilizzando le opzioni di lettura read_time . In questo caso, le letture vengono eseguite poiché i dati erano al read_time ed è molto probabile che la replica più vicina abbia già verificato la presenza di dati al read_time specificato. Per prestazioni notevolmente migliori, 15 secondi è un valore ragionevole. Anche per le letture non aggiornate, le righe restituite sono coerenti tra loro.

Evita gli hotspot

Le suddivisioni in Cloud Firestore vengono automaticamente suddivise in parti più piccole per distribuire il lavoro di gestione del traffico su più server di archiviazione quando 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 ulteriori suddivisioni quando necessario. Questi meccanismi aiutano i database Cloud Firestore a scalare automaticamente in caso di aumento del carico di traffico o delle dimensioni del database. Tuttavia, ci sono alcune limitazioni di cui tenere conto, come spiegato di seguito.

La suddivisione dello spazio di archiviazione e del carico richiede tempo e l'aumento del traffico troppo rapido può causare un'elevata latenza o errori di superamento della scadenza, comunemente definiti hotspot , mentre il servizio si adegua. La procedura migliore consiste nel 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 posiziona il database in modo da scalarlo in modo ottimale per soddisfare il carico di lavoro.

Sebbene le suddivisioni vengano create automaticamente con l'aumento del carico, Cloud Firestore può suddividere un intervallo di chiavi solo finché non serve 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 punto critico su quel documento. Se riscontri latenze elevate e prolungate su un singolo documento, dovresti prendere in considerazione la modifica del modello di dati per suddividere o replicare i dati su più documenti.

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

Un altro caso speciale di hotspot si verifica quando una chiave di aumento/diminuzione sequenziale viene utilizzata come ID documento in Cloud Firestore e si verifica un numero considerevolmente elevato di operazioni al secondo. Creare più suddivisioni non aiuta in questo caso poiché l'aumento del traffico si sposta semplicemente sulla suddivisione appena creata. Poiché Cloud Firestore indicizza automaticamente tutti i campi nel documento per impostazione predefinita, tali hotspot mobili possono anche essere creati nello spazio dell'indice per un campo del documento che contiene un valore crescente/decrescente in sequenza come un timestamp.

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

Risoluzione dei problemi

Firestore fornisce Key Visualizer come strumento diagnostico progettato specificamente per analizzare i modelli di utilizzo e risolvere i problemi di hotspot.

Qual è il prossimo