Leggi questo documento per indicazioni sulla scalabilità della tua app serverless oltre migliaia di operazioni al secondo o centinaia di migliaia di utenti simultanei. Questo documento include argomenti avanzati per aiutarti a comprendere il sistema in modo approfondito. Se hai appena iniziato a utilizzare Cloud Firestore, consulta la guida rapida invece.
Cloud Firestore e gli SDK Firebase per dispositivi mobili/web forniscono un modello efficace per lo sviluppo di app serverless in cui il codice lato client accede direttamente al database. Gli SDK consentono ai client di ascoltare gli aggiornamenti dei dati in tempo reale. Puoi utilizzare gli aggiornamenti in tempo reale per creare app reattive che non richiedono un'infrastruttura server. Sebbene sia molto facile avviare un'app, è utile comprendere i vincoli dei sistemi che compongono Cloud Firestore in modo che l'app serverless possa scalare e funzionare correttamente quando il traffico aumenta.
Consulta le sezioni seguenti per suggerimenti sulla scalabilità dell'app.
Scegli una località del database vicina ai tuoi utenti
Il seguente diagramma mostra l'architettura di un'app in tempo reale:
Quando un'app in esecuzione sul dispositivo di un utente (mobile o web) stabilisce una
connessione a Cloud Firestore, la connessione viene instradata a un
Cloud Firestore server frontend nella stessa
regione in cui si trova il database. Ad esempio,
se il database si trova in us-east1, la connessione viene stabilita anche con un
Cloud Firestore frontend in us-east1. Queste connessioni sono
a lunga durata e rimangono aperte finché non vengono chiuse esplicitamente dall'app. Il
frontend legge i dati dai sistemi di archiviazione Cloud Firestore sottostanti.
La distanza tra la posizione fisica di un utente e la località del Cloud Firestore database influisce sulla latenza percepita dall'utente. Ad esempio, un utente in India la cui app comunica con un database in una regione Google Cloud in Nord America potrebbe trovare l'esperienza più lenta e l'app meno reattiva rispetto a se il database si trovasse più vicino, ad esempio in India o in un'altra parte dell'Asia.
Progettazione per l'affidabilità
I seguenti argomenti migliorano o influiscono sull'affidabilità dell'app:
Attiva la modalità offline
Gli SDK Firebase forniscono la persistenza dei dati offline. Se l' app sul dispositivo dell'utente non riesce a connettersi a Cloud Firestore, l'app rimane utilizzabile lavorando con i dati memorizzati nella cache locale. In questo modo, l'accesso ai dati è garantito anche quando gli utenti riscontrano connessioni internet instabili o perdono completamente l'accesso per diverse ore o giorni. Per maggiori dettagli sulla modalità offline, consulta Abilitare i dati offline.
Informazioni sui nuovi tentativi automatici
Gli SDK Firebase si occupano di riprovare le operazioni e ristabilire le connessioni interrotte. In questo modo è possibile aggirare gli errori temporanei causati dal riavvio dei server o da problemi di rete tra il client e il database.
Scegli tra località regionali e multiregionali
Esistono diversi compromessi quando si sceglie tra località regionali e multiregionali. La differenza principale è la modalità di replica dei dati. Questo determina le garanzie di disponibilità della tua app. Un'istanza multiregionale offre una maggiore affidabilità di pubblicazione e aumenta la durabilità dei dati, ma il compromesso è il costo.
Informazioni sul sistema di query in tempo reale
Le query in tempo reale, chiamate anche listener di snapshot, consentono all'app di ascoltare le modifiche nel database e ricevere notifiche a bassa latenza non appena i dati cambiano. Un'app può ottenere lo stesso risultato eseguendo periodicamente il polling del database per gli aggiornamenti, ma spesso è più lento, più costoso e richiede più codice. Per esempi su come configurare e utilizzare le query in tempo reale, consulta Ricevere aggiornamenti in tempo reale. Le sezioni seguenti forniscono dettagli sul funzionamento dei listener di snapshot e descrivono alcune delle best practice per scalare le query in tempo reale mantenendo le prestazioni.
Immagina due utenti che si connettono a Cloud Firestore tramite un'app di messaggistica creata con uno degli SDK per dispositivi mobili.
Il client A scrive nel database per aggiungere e aggiornare i documenti in una raccolta chiamata chatroom:
collection chatroom:
document message1:
from: 'Sparky'
message: 'Welcome to Cloud Firestore!'
document message2:
from: 'Santa'
message: 'Presents are coming'
Il client B ascolta gli aggiornamenti nella stessa raccolta utilizzando un listener di snapshot. Il client B riceve una notifica immediata ogni volta che qualcuno crea un nuovo messaggio. Il seguente diagramma mostra l'architettura di un listener di snapshot:
Quando il client B connette un listener di snapshot al database, si verifica la seguente sequenza di eventi:
- Il client B apre una connessione a Cloud Firestore e registra un
listener chiamando
onSnapshot(collection("chatroom"))tramite l'SDK Firebase. Questo listener può rimanere attivo per ore. - Il frontend Cloud Firestore esegue query sul sistema di archiviazione sottostante per eseguire il bootstrap del set di dati. Carica l'intero set di risultati dei documenti corrispondenti. Ci riferiamo a questa query come query di polling. Il sistema valuta quindi le regole di sicurezza di Firebase del database per verificare che l'utente possa accedere a questi dati. Se l'utente è autorizzato, il database restituisce i dati all'utente.
- La query del client B passa quindi alla modalità di ascolto. Il listener si registra con un gestore di abbonamenti e attende gli aggiornamenti dei dati.
- Il client A invia ora un'operazione di scrittura per modificare un documento.
- Il database esegue il commit della modifica del documento nel sistema di archiviazione.
- In modo transazionale, il sistema esegue il commit dello stesso aggiornamento in un log delle modifiche interno. Il log delle modifiche stabilisce un ordine rigoroso delle modifiche man mano che si verificano.
- Il log delle modifiche a sua volta distribuisce i dati aggiornati a un pool di gestori di abbonamenti.
- Viene eseguita una corrispondenza di query inversa per verificare se il documento aggiornato corrisponde a eventuali listener di snapshot attualmente registrati. In questo esempio, il documento corrisponde al listener di snapshot del client B. Come suggerisce il nome, puoi considerare la corrispondenza di query inversa come una normale query di database, ma eseguita al contrario. Anziché cercare i documenti che corrispondono a una query, cerca in modo efficiente le query che corrispondono a un documento in entrata. Una volta trovato un risultato, il sistema inoltra il documento in questione ai listener di snapshot. Il sistema valuta quindi le regole di sicurezza di Firebase del database per garantire che solo gli utenti autorizzati ricevano i dati.
- Il sistema inoltra l'aggiornamento del documento all'SDK sul dispositivo del client B e viene attivato il callback
onSnapshot. Se la persistenza locale è abilitata, l'SDK applica l'aggiornamento anche alla cache locale.
Una parte fondamentale della scalabilità di Cloud Firestore's dipende dal fan-out da l log delle modifiche ai gestori di abbonamenti e ai server frontend. Il fan-out consente a una singola modifica dei dati di propagarsi in modo efficiente per gestire milioni di query in tempo reale e utenti connessi. Eseguendo molte repliche di tutti questi componenti in più zone (o più regioni nel caso di un deployment multiregionale), Cloud Firestore raggiunge un'elevata affidabilità e scalabilità.
È importante notare che tutte le operazioni di lettura emesse dagli SDK per dispositivi mobili e web seguono il modello sopra riportato. Eseguono una query di polling seguita dalla modalità di ascolto per mantenere le garanzie di coerenza. Questo vale anche per i listener in tempo reale, le chiamate per recuperare un documento e le query una tantum. Puoi considerare i recuperi di singoli documenti e le query una tantum come listener di snapshot di breve durata con vincoli simili in termini di prestazioni.
Applica le best practice per scalare le query in tempo reale
Applica le seguenti best practice per progettare query in tempo reale scalabili.
Informazioni sul traffico di scrittura elevato nel sistema
Questa sezione ti aiuta a capire come il sistema risponde a un numero crescente di richieste di scrittura.
I log delle modifiche di Cloud Firestore che gestiscono le query in tempo reale vengono scalati automaticamente in orizzontale man mano che il traffico di scrittura aumenta. Man mano che la velocità di scrittura per un database aumenta oltre ciò che un singolo server può gestire, il log delle modifiche viene suddiviso su più server e l'elaborazione delle query inizia a utilizzare i dati di più gestori di abbonamenti anziché uno. Dal punto di vista del client e dell'SDK, tutto questo è trasparente e non è richiesta alcuna azione da parte dell'app quando si verificano le suddivisioni. Il seguente diagramma mostra come vengono scalate le query in tempo reale:
La scalabilità automatica ti consente di aumentare il traffico di scrittura senza limiti, ma man mano che il traffico aumenta, il sistema potrebbe impiegare un po' di tempo per rispondere. Segui i consigli della regola 5-5-5 per evitare di creare un hotspot di scrittura. Key Visualizer è uno strumento utile per analizzare gli hotspot di scrittura.
Molte app hanno una crescita organica prevedibile, che Cloud Firestore può gestire senza precauzioni. Tuttavia, i carichi di lavoro batch come l'importazione di un set di dati di grandi dimensioni possono aumentare le scritture troppo rapidamente. Quando progetti l'app, tieni presente da dove proviene il traffico di scrittura.
Informazioni su come interagiscono le scritture e le letture
Puoi considerare il sistema di query in tempo reale come una pipeline che collega le operazioni di scrittura con i lettori. Ogni volta che un documento viene creato, aggiornato o eliminato, la modifica si propaga dal sistema di archiviazione ai listener attualmente registrati. Cloud Firestore La struttura del log delle modifiche garantisce una coerenza elevata, il che significa che l'app non riceve mai notifiche di aggiornamenti fuori ordine rispetto a quando il database ha eseguito il commit delle modifiche dei dati. In questo modo, lo sviluppo dell'app viene semplificato rimuovendo i casi limite relativi alla coerenza dei dati.
Questa pipeline connessa significa che un'operazione di scrittura che causa hotspot o conflitti di blocco può influire negativamente sulle operazioni di lettura. Quando le operazioni di scrittura non riescono o subiscono limitazioni, una lettura potrebbe bloccarsi in attesa di dati coerenti dal log delle modifiche. Se ciò accade nella tua app, potresti notare sia operazioni di scrittura lente sia tempi di risposta lenti correlati per le query. Evitare gli hotspot è la chiave per evitare questo problema.
Mantieni i documenti e le operazioni di scrittura di piccole dimensioni
Quando crei app con listener di snapshot, in genere vuoi che gli utenti vengano informati rapidamente delle modifiche dei dati. Per farlo, cerca di mantenere le dimensioni ridotte. Il sistema può inviare rapidamente documenti di piccole dimensioni con decine di campi. I documenti più grandi con centinaia di campi e dati di grandi dimensioni richiedono più tempo per l'elaborazione.
Allo stesso modo, preferisci operazioni di commit e scrittura brevi e veloci per mantenere bassa la latenza. I batch di grandi dimensioni potrebbero offrire una maggiore velocità effettiva dal punto di vista dello scrittore, ma potrebbero effettivamente aumentare il tempo di notifica per i listener di snapshot. Questo è spesso controintuitivo rispetto all'utilizzo di altri sistemi di database in cui potresti utilizzare il batch per migliorare le prestazioni.
Utilizza listener efficienti
Man mano che le velocità di scrittura per il database aumentano, Cloud Firestore suddivide l'elaborazione dei dati su più server. Cloud Firestore's sharding algorithm tries to co-locate data from the same collection or collection group onto the same changelog server. Il sistema cerca di massimizzare la velocità effettiva di scrittura possibile mantenendo il numero di server coinvolti nell'elaborazione di una query il più basso possibile.
Tuttavia, alcuni pattern potrebbero comunque portare a un comportamento non ottimale per i listener di snapshot. Ad esempio, se la tua app archivia la maggior parte dei dati in una raccolta di grandi dimensioni, il listener potrebbe dover connettersi a molti server per ricevere tutti i dati di cui ha bisogno. Questo vale anche se applichi un filtro di query. La connessione a molti server aumenta il rischio di risposte più lente.
Per evitare queste risposte più lente, progetta lo schema e l'app in modo che il sistema possa gestire i listener senza dover accedere a molti server diversi. Potrebbe essere più efficace suddividere i dati in raccolte più piccole con velocità di scrittura inferiori.
Questo è simile a pensare alle query sulle prestazioni in un database relazionale che richiedono scansioni complete della tabella. In un database relazionale, una query che richiede una scansione completa della tabella è l'equivalente di un listener di snapshot che monitora una raccolta con un elevato tasso di modifiche. Potrebbe essere eseguita lentamente rispetto a una query che il database può gestire utilizzando un indice più specifico. Una query con un indice più specifico è come un listener di snapshot che monitora un singolo documento o una raccolta che cambia meno spesso. Dovresti eseguire test di carico sull'app per comprendere al meglio il comportamento e le esigenze del tuo caso d'uso.
Mantieni veloci le query di polling
Un'altra parte fondamentale delle query in tempo reale reattive consiste nel garantire che la query di polling per eseguire il bootstrap dei dati sia veloce ed efficiente. La prima volta che un nuovo listener di snapshot si connette, deve caricare l'intero set di risultati e inviarlo al dispositivo dell'utente. Le query lente rendono l'app meno reattiva. Sono incluse, ad esempio, le query che tentano di leggere molti documenti o le query che non utilizzano gli indici appropriati.
In alcune circostanze, un listener potrebbe anche passare da uno stato di ascolto a uno stato di polling. Questo avviene automaticamente ed è trasparente per gli SDK e la tua app. Le seguenti condizioni potrebbero attivare uno stato di polling:
- Il sistema ribilancia un log delle modifiche a causa di modifiche del carico.
- Gli hotspot causano scritture non riuscite o ritardate nel database.
- I riavvii temporanei del server influiscono temporaneamente sui listener.
Se le query di polling sono abbastanza veloci, uno stato di polling diventa trasparente per gli utenti dell'app.
Preferisci i listener a lunga durata
Aprire e mantenere attivi i listener il più a lungo possibile è spesso il modo più conveniente per creare un'app che utilizza Cloud Firestore. Quando utilizzi Cloud Firestore, ti vengono addebitati i documenti restituiti alla tua app e non il mantenimento di una connessione aperta. Un listener di snapshot a lunga durata legge solo i dati necessari per gestire la query durante il suo ciclo di vita. Sono incluse un'operazione di polling iniziale seguita dalle notifiche quando i dati cambiano effettivamente. Le query una tantum, d'altra parte, rileggono i dati che potrebbero non essere stati modificati dall'ultima esecuzione della query da parte dell'app.
Nei casi in cui l'app deve utilizzare una velocità di dati elevata, i listener di snapshot potrebbero non essere appropriati. Ad esempio, se il tuo caso d'uso invia molti documenti al secondo tramite una connessione per un periodo di tempo prolungato, potrebbe essere preferibile optare per query una tantum eseguite con una frequenza inferiore.
Passaggi successivi
- Scopri come utilizzare i listener di snapshot.
- Leggi ulteriori informazioni sulle best practice.