best practice

Usa le best practice qui elencate come riferimento rapido quando crei un'applicazione che usa Firestore.

Località del database

Quando crei l'istanza di database, seleziona la località del database più vicina ai tuoi utenti e alle risorse di calcolo. Gli hop di rete di ampia portata sono più soggetti a errori e aumentano la latenza delle query.

Per massimizzare la disponibilità e la durabilità della tua applicazione, seleziona una località con più regioni e posiziona le risorse di calcolo critiche in almeno due regioni.

Seleziona una località a livello di regione per ridurre i costi, per ridurre la latenza di scrittura se la tua applicazione è sensibile alla latenza o per la co-location con altre risorse Google Cloud.

ID documento

  • Evita gli ID documento . e ...
  • Evita di utilizzare / barre in avanti negli ID documento.
  • Non utilizzare ID documento in aumento monotonico, ad esempio:

    • Customer1, Customer2, Customer3 e...
    • Product 1, Product 2, Product 3 e...

    Questi ID sequenziali possono portare a hotspot che influiscono sulla latenza.

Nomi campi

  • Evita i seguenti caratteri nei nomi dei campi, perché richiedono un carattere di escape extra:

    • . punto
    • [ parentesi quadra aperta
    • ] parentesi quadra chiusa
    • * asterisco
    • `: accento grave

Indici

Riduci la latenza di scrittura

Il fanout dell'indice è il principale fattore che contribuisce alla latenza di scrittura. Le best practice per ridurre il fanout dell'indice sono:

  • Imposta le esenzioni dell'indice a livello di raccolta. Un'impostazione predefinita semplice è disabilitare l'indicizzazione in ordine decrescente e in array. La rimozione dei valori indicizzati non utilizzati ridurrà anche i costi di archiviazione.

  • Riduci il numero di documenti in una transazione. Per la scrittura di un numero elevato di documenti, valuta l'utilizzo di uno strumento di scrittura collettiva anziché atomico batch writer.

Esenzioni indice

Per la maggior parte delle app, puoi utilizzare l'indicizzazione automatica e i link di eventuali messaggi di errore per gestire gli indici. Tuttavia, ti consigliamo di aggiungere esenzioni a campo singolo nei seguenti casi:

Caso Descrizione
Campi stringa grandi

Se hai un campo stringa che spesso contiene valori stringa lunghi che non utilizzi per le query, puoi ridurre i costi di archiviazione esenzione il campo dall'indicizzazione.

Velocità di scrittura elevate in una raccolta contenente documenti con valori sequenziali

Se indicizzi un campo che aumenta o diminuisce in sequenza tra i documenti di una raccolta, ad esempio un timestamp, la frequenza di scrittura massima nella raccolta è di 500 scritture al secondo. Se non esegui una query in base al campo con valori sequenziali, puoi escludere il campo dall'indicizzazione per bypassare questo limite.

In un caso d'uso IoT con un'elevata frequenza di scrittura, ad esempio, una raccolta contenente documenti con un campo timestamp potrebbe avvicinarsi al limite di 500 scritture al secondo.

Campi TTL

Se utilizzi i criteri TTL (time-to-live), tieni presente che il campo TTL deve contenere un timestamp. L'indicizzazione sui campi TTL è abilitata per impostazione predefinita e può influire sulle prestazioni a frequenze di traffico più elevate. Come best practice, aggiungi esenzioni a campo singolo per i campi TTL.

Matrice grande o mappa campi

I campi a array o sulla mappa di grandi dimensioni possono avvicinarsi al limite di 40.000 voci di indice per documento. Se non esegui query basate su un array o un campo della mappa di grandi dimensioni, dovresti esentarli dall'indicizzazione.

Operazioni di lettura e scrittura

  • La frequenza massima esatta con cui un'app può aggiornare un singolo documento dipende fortemente dal carico di lavoro. Per ulteriori informazioni, vedi Aggiornamenti a un singolo documento.

  • Se possibile, utilizza le chiamate asincrone anziché quelle sincrone. Le chiamate asincrone riducono al minimo l'impatto della latenza. Ad esempio, prima di visualizzare una risposta, prendi in considerazione un'applicazione che necessita del risultato di una ricerca di documenti e dei risultati di una query. Se la ricerca e la query non hanno una dipendenza dai dati, non è necessario attendere in modo sincrono il completamento della ricerca prima di avviare la query.

  • Non utilizzare offset. Utilizza invece cursors. L'uso di un offset evita solo di restituire i documenti ignorati all'applicazione, ma questi documenti vengono comunque recuperati internamente. I documenti ignorati influiscono sulla latenza della query e all'applicazione vengono addebitate le operazioni di lettura necessarie per recuperarli.

Nuovi tentativi transazioni

Gli SDK e le librerie client di Firestore riprovano automaticamente le transazioni non riuscite per gestire gli errori temporanei. Se la tua applicazione accede a Firestore tramite le API REST o RPC direttamente anziché tramite un SDK, l'applicazione deve implementare i nuovi tentativi di transazione per aumentare l'affidabilità.

Aggiornamenti in tempo reale

Per le best practice relative agli aggiornamenti in tempo reale, consulta Comprendere le query in tempo reale su larga scala.

Progettare per la scalabilità

Le seguenti best practice descrivono come evitare situazioni che creano problemi di contesa.

Aggiornamenti a un singolo documento

Durante la progettazione dell'app, valuta la velocità con cui l'app aggiorna i singoli documenti. Il modo migliore per caratterizzare le prestazioni del carico di lavoro è eseguire i test del carico. La frequenza massima esatta con cui un'app può aggiornare un singolo documento dipende in gran parte dal carico di lavoro. I fattori includono la frequenza di scrittura, la contesa tra le richieste e il numero di indici interessati.

Un'operazione di scrittura di documenti aggiorna il documento e gli eventuali indici associati e Firestore applica in modo sincrono l'operazione di scrittura su un quorum di repliche. Con velocità di scrittura sufficientemente elevate, il database inizia a riscontrare contese, latenza più elevata o altri errori.

Velocità elevate di lettura, scrittura ed eliminazione in un intervallo di documenti ristretto

Evita alte frequenze di lettura o scrittura per chiudere i documenti lessicograficamente, altrimenti la tua applicazione riscontrerà errori di contesa. Questo problema è noto come hotspotting e l'applicazione può essere soggetta a hotspot se si verifica una delle seguenti condizioni:

  • Crea nuovi documenti a una frequenza molto elevata e alloca i propri ID con aumento monotonico.

    Firestore alloca gli ID documento utilizzando un algoritmo a dispersione. Non dovresti rilevare hotspot durante le scritture se crei nuovi documenti utilizzando ID documento automatici.

  • Crea rapidamente nuovi documenti in una raccolta con pochi documenti.

  • Crea nuovi documenti con un campo che aumenta monotonicamente, come un timestamp, con una frequenza molto elevata.

  • Elimina con frequenza elevata i documenti di una raccolta.

  • Scrive nel database a una velocità molto elevata senza aumentare gradualmente il traffico.

Evita di saltare i dati eliminati

Evita query che saltano i dati eliminati di recente. Una query potrebbe dover saltare un numero elevato di voci di indice se i risultati delle query iniziali sono stati eliminati di recente.

Un esempio di carico di lavoro che potrebbe dover saltare molti dati eliminati è quello che tenta di trovare gli elementi di lavoro in coda meno recenti. La query potrebbe avere il seguente aspetto:

docs = db.collection('WorkItems').order_by('created').limit(100)
delete_batch = db.batch()
for doc in docs.stream():
  finish_work(doc)
  delete_batch.delete(doc.reference)
delete_batch.commit()

Ogni volta che questa query viene eseguita, verifica nelle voci di indice il campo created in tutti i documenti eliminati di recente. Questo rallenta le query.

Per migliorare le prestazioni, usa il metodo start_at per trovare il punto di partenza migliore. Ad esempio:

completed_items = db.collection('CompletionStats').document('all stats').get()
docs = db.collection('WorkItems').start_at(
    {'created': completed_items.get('last_completed')}).order_by(
        'created').limit(100)
delete_batch = db.batch()
last_completed = None
for doc in docs.stream():
  finish_work(doc)
  delete_batch.delete(doc.reference)
  last_completed = doc.get('created')

if last_completed:
  delete_batch.update(completed_items.reference,
                      {'last_completed': last_completed})
  delete_batch.commit()

NOTA: l'esempio riportato sopra utilizza un campo che aumenta monotonicamente e funge da anti-pattern per frequenze di scrittura elevate.

Aumento del traffico

Devi aumentare gradualmente il traffico verso nuove raccolte o chiudere i documenti in modo lessicografico per concedere a Firestore il tempo sufficiente per preparare i documenti per l'aumento del traffico. Ti consigliamo di iniziare con un massimo di 500 operazioni al secondo su una nuova raccolta, per poi aumentare il traffico del 50% ogni 5 minuti. Puoi aumentare in modo simile il traffico in scrittura, ma tieni presente i limiti standard di Firestore. Assicurati che le operazioni siano distribuite in modo relativamente uniforme nell'intervallo di chiavi. Si tratta della cosiddetta regola "500/50/5".

Migrazione del traffico a una nuova raccolta

L'aumento graduale è particolarmente importante se esegui la migrazione del traffico delle app da una raccolta a un'altra. Un modo semplice per gestire questa migrazione è leggere dalla raccolta precedente e, se il documento non esiste, leggere dalla nuova raccolta. Tuttavia, questo potrebbe causare un improvviso aumento del traffico alla chiusura lessicografica dei documenti nella nuova raccolta. Firestore potrebbe non essere in grado di preparare in modo efficiente la nuova raccolta per l'aumento del traffico, soprattutto se contiene pochi documenti.

Un problema simile può verificarsi se modifichi gli ID documento di molti documenti all'interno della stessa raccolta.

La strategia migliore per la migrazione del traffico a una nuova raccolta dipende dal modello dei dati. Di seguito è riportato un esempio di strategia nota come letture parallele. Dovrai determinare se questa strategia è efficace o meno per i tuoi dati e una considerazione importante sarà l'impatto sui costi delle operazioni parallele durante la migrazione.

Letture parallele

Per implementare le letture parallele durante la migrazione del traffico a una nuova raccolta, leggi prima dalla raccolta precedente. Se il documento non è presente, leggilo dalla nuova raccolta. Un'elevata frequenza di letture di documenti inesistenti può portare all'hotspotting, quindi assicurati di aumentare gradualmente il carico sulla nuova raccolta. La strategia migliore è copiare il vecchio documento nella nuova raccolta ed eliminare il vecchio documento. Incrementa le letture parallele gradualmente per assicurarti che Firestore possa gestire il traffico verso la nuova raccolta.

Una possibile strategia per incrementare gradualmente le letture o le scritture in una nuova raccolta consiste nell'utilizzare un hash deterministico dell'ID utente per selezionare una percentuale casuale di utenti che tentano di scrivere nuovi documenti. Assicurati che il risultato dell'hash dell'ID utente non sia alterato dalla tua funzione o dal comportamento dell'utente.

Nel frattempo, esegui un job batch che copi tutti i dati dai vecchi documenti alla nuova raccolta. Il job batch dovrebbe evitare le scritture negli ID documento sequenziali per prevenire gli hotspot. Al termine del job batch, potrai leggere solo dalla nuova raccolta.

Un perfezionamento di questa strategia consiste nell'eseguire la migrazione di piccoli gruppi di utenti alla volta. Aggiungi un campo al documento utente per tenere traccia dello stato della migrazione dell'utente. Seleziona un gruppo di utenti di cui eseguire la migrazione in base a un hash dell'ID utente. Utilizza un job batch per eseguire la migrazione dei documenti per quel batch di utenti e utilizza le letture parallele per gli utenti durante la migrazione.

Tieni presente che non puoi facilmente eseguire il rollback se non esegui la doppia scrittura delle entità vecchie e nuove durante la fase di migrazione. Ciò aumenterebbe i costi di Firestore sostenuti.

Privacy

  • Evita di archiviare informazioni sensibili in un ID progetto Cloud. Un ID progetto cloud potrebbe essere conservato oltre la durata del progetto.
  • Come best practice per la conformità dei dati, consigliamo di non archiviare informazioni sensibili nei nomi e nei nomi dei campi dei documenti.