Questa pagina descrive i concetti da considerare quando selezioni gli indici di Firestore in modalità Datastore per la tua app.
Firestore in modalità Datastore offre prestazioni elevate delle query utilizzando gli indici per tutte le query. Il rendimento della maggior parte delle query dipende dalle dimensioni del set di risultati e non dalle dimensioni totali del database.
Firestore in modalità Datastore definisce gli indici integrati per ogni proprietà di un'entità. Questi indici di singole proprietà supportano molte query semplici. Firestore in modalità Datastore supporta una funzionalità di unione degli indici che consente al database di unire gli indici integrati per supportare query aggiuntive. Per le query più complesse, devi definire in anticipo gli indici composti.
Questa pagina si concentra sulla funzionalità di unione degli indici, perché influisce su due importanti opportunità di ottimizzazione degli indici:
- Velocizzazione delle query
- Riduzione del numero di indici composti
L'esempio seguente mostra la funzionalità di unione degli indici.
Filtrare le entità Photo
Considera un database in modalità Datastore con entità di tipo Photo:
| Photo | ||
|---|---|---|
| Proprietà | Tipo di valore | Descrizione |
owner_id |
Stringa | ID utente |
tag |
Array di stringhe | Parole chiave tokenizzate |
size |
Numero intero |
Enumerazione:
|
coloration |
Numero intero |
Enumerazione:
|
Supponiamo che tu abbia bisogno di una funzionalità dell'app che consenta agli utenti di eseguire query sulle entità Photo in base a un AND logico di quanto segue:
Fino a tre filtri basati sulle proprietà:
owner_idsizecoloration
Una stringa di ricerca
tag. L'app tokenizza la stringa di ricerca in tag e aggiunge un filtro per ogni tag.Ad esempio, l'app trasforma la stringa di ricerca
outside, familynei filtri di querytag=outsideetag=family.
Utilizzando gli indici integrati e la funzionalità di unione degli indici di Firestore in modalità Datastore, puoi soddisfare i requisiti degli indici di questa funzionalità di filtro Photo senza aggiungere altri indici composti.
Gli indici integrati per le entità Photo supportano query con un solo filtro come:
Python
La funzionalità di filtro Photo richiede anche query che combinano più filtri di uguaglianza con un AND logico:
Python
Firestore in modalità Datastore può supportare queste query unendo gli indici integrati.
Unione degli indici
Firestore in modalità Datastore può utilizzare l'unione degli indici quando la query e gli indici soddisfano tutti i seguenti vincoli:
- La query utilizza solo filtri di uguaglianza (
=). - Non esiste un indice composto che corrisponda perfettamente ai filtri e all'ordinamento della query.
- Ogni filtro di uguaglianza corrisponde ad almeno un indice esistente con lo stesso ordinamento della query.
In questa situazione, Firestore in modalità Datastore può utilizzare gli indici esistenti per supportare la query anziché richiedere la configurazione di un indice composto aggiuntivo.
Quando due o più indici vengono ordinati in base agli stessi criteri, Firestore in modalità Datastore può unire i risultati di più scansioni di indici per trovare i risultati comuni a tutti questi indici. Firestore in modalità Datastore può unire gli indici integrati, perché tutti ordinano i valori in base alla chiave dell'entità.
Unendo gli indici integrati, Firestore in modalità Datastore supporta le query con filtri di uguaglianza su più proprietà:
Python
Firestore in modalità Datastore può anche unire i risultati degli indici di più sezioni dello stesso indice. Unendo le diverse sezioni dell'indice integrato per la proprietà tag, Firestore in modalità Datastore supporta le query che combinano più filtri tag in un AND logico:
Python
Le query supportate dagli indici integrati uniti completano il set di query richieste dalla funzionalità di filtro Photo. Tieni presente che il supporto della funzionalità di filtro Photo non ha richiesto indici composti aggiuntivi.
Quando selezioni gli indici ottimali per la tua app, è importante comprendere la funzionalità di unione degli indici. L'unione degli indici offre a Firestore in modalità Datastore una maggiore flessibilità delle query, ma con un possibile compromesso in termini di prestazioni. La sezione successiva descrive il rendimento dell'unione degli indici e come migliorarlo aggiungendo indici composti.
Trovare l'indice perfetto
L'indice viene ordinato prima per predecessore e poi per valori delle proprietà, nell'ordine specificato nella definizione dell'indice. L'indice composto perfetto per una query, che consente di eseguire la query nel modo più efficiente, è definito sulle seguenti proprietà, in ordine:
- Proprietà utilizzate nei filtri di uguaglianza
- Proprietà utilizzate negli ordini di ordinamento
- Proprietà utilizzate nel filtro
distinctOn - Proprietà utilizzate nei filtri di intervallo e di disuguaglianza (che non sono già inclusi negli ordini di ordinamento)
- Proprietà utilizzate in aggregazioni e proiezioni (che non sono già incluse negli ordini di ordinamento e nei filtri di intervallo e di disuguaglianza)
In questo modo, tutti i risultati per ogni possibile esecuzione della query vengono presi in considerazione. I database Firestore in modalità Datastore eseguono una query utilizzando un indice perfetto seguendo questi passaggi:
- Identifica l'indice corrispondente al tipo, alle proprietà del filtro, agli operatori di filtro e agli ordini di ordinamento della query.
- Esegue la scansione dall'inizio dell'indice alla prima entità che soddisfa tutte o un sottoinsieme delle condizioni di filtro della query.
- Continua a eseguire la scansione dell'indice, restituendo ogni entità che soddisfa tutte le condizioni di filtro, fino a quando
- non incontra un'entità che non soddisfa le condizioni di filtro oppure
- raggiunge la fine dell'indice oppure
- ha raccolto il numero massimo di risultati richiesti dalla query.
Ad esempio, considera la seguente query:
SELECT * FROM Task
WHERE category = 'Personal'
AND priority < 3
ORDER BY priority DESC
L'indice composto perfetto per questa query è un indice di chiavi per le entità di tipo Task, con colonne per i valori delle proprietà category e priority. L'indice viene ordinato prima in ordine crescente per category e poi in ordine decrescente per priority:
indexes:
- kind: Task
properties:
- name: category
direction: asc
- name: priority
direction: desc
Due query della stessa forma ma con valori di filtro diversi utilizzano lo stesso indice. Ad esempio, la seguente query utilizza lo stesso indice della query precedente:
SELECT * FROM Task
WHERE category = 'Work'
AND priority < 5
ORDER BY priority DESC
Per questo indice
indexes:
- kind: Task
properties:
- name: category
direction: asc
- name: priority
direction: asc
- name: created
direction: asc
L'indice precedente può soddisfare entrambe le seguenti query:
SELECT * FROM Task
WHERE category = 'Personal'
AND priority = 5
ORDER BY created ASC
e
SELECT * FROM Task
WHERE category = 'Work'
ORDER BY priority ASC, created ASC
Ottimizzare la selezione degli indici
Questa sezione descrive le caratteristiche di rendimento dell'unione degli indici e due opportunità di ottimizzazione correlate all'unione degli indici:
- Aggiungere indici composti per velocizzare le query che si basano su indici uniti
- Ridurre il numero di indici composti sfruttando gli indici uniti
Rendimento dell'unione degli indici
In un'unione degli indici, Firestore in modalità Datastore unisce in modo efficiente gli indici utilizzando un algoritmo di unione a zig-zag. Utilizzando questo algoritmo, la modalità Datastore unisce le potenziali corrispondenze di più scansioni di indici per produrre un set di risultati che corrisponde a una query. L'unione degli indici combina i componenti del filtro in fase di lettura anziché in fase di scrittura. A differenza della maggior parte delle query di Firestore in modalità Datastore, in cui il rendimento dipende solo dalle dimensioni del set di risultati, il rendimento delle query di unione degli indici dipende dai filtri della query e dal numero di potenziali corrispondenze considerate dal database.
Il rendimento migliore di un'unione degli indici si verifica quando ogni potenziale corrispondenza in un indice soddisfa i filtri della query. In questo caso, il rendimento è O(R * I)
dove R è la dimensione del set di risultati e I è il numero di indici
sottoposti a scansione.
Il rendimento peggiore si verifica quando il database deve considerare molte potenziali corrispondenze, ma poche di queste soddisfano i filtri della query. In questo caso,
il rendimento è O(S), dove S è la dimensione del set più piccolo
di potenziali entità da una singola scansione dell'indice.
Il rendimento effettivo dipende dalla forma dei dati. Il numero medio di entità considerate per ogni risultato restituito è O(S/(R * I)). Le query hanno un rendimento peggiore quando molte entità corrispondono a ogni scansione dell'indice, ma poche entità corrispondono alla query nel suo complesso, il che significa che R è piccolo e S è grande.
Quattro fattori mitigano questo rischio:
Il pianificatore di query non cerca un'entità finché non sa che l'entità corrisponde all'intera query.
L'algoritmo a zig-zag non deve trovare tutti i risultati per restituire il risultato successivo. Se richiedi i primi 10 risultati, paghi solo la latenza per trovare questi 10 risultati.
L'algoritmo a zig-zag salta ampie sezioni di risultati di falsi positivi. Il rendimento peggiore si verifica solo se i risultati di falsi positivi sono perfettamente intrecciati (in ordine di ordinamento) tra le scansioni.
La latenza dipende dal numero di entità trovate in ogni scansione dell'indice, non dal numero di entità che corrispondono a ogni filtro. Come mostrato nella sezione successiva, puoi aggiungere indici composti per migliorare il rendimento dell'unione degli indici.
Velocizzare una query di unione degli indici
Quando Firestore in modalità Datastore unisce gli indici, ogni scansione dell'indice spesso esegue il mapping a un singolo filtro nella query. Puoi migliorare il rendimento delle query aggiungendo indici composti che corrispondono a più filtri nella query.
Considera questa query:
Python
Ogni filtro esegue il mapping a una scansione dell'indice nei seguenti indici integrati:
Index(Photo, owner_id) Index(Photo, size) Index(Photo, tag)
Se aggiungi l'indice composto Index(Photo, owner_id, size), la query esegue il mapping a due scansioni dell'indice anziché tre:
# Satisfies both 'owner_id=username' and 'size=2'
Index(Photo, owner_id, size)
Index(Photo, tag)Considera uno scenario con molte immagini di grandi dimensioni, molte immagini in bianco e nero, ma poche immagini panoramiche di grandi dimensioni. Una query che filtra sia le immagini panoramiche sia quelle in bianco e nero sarà lenta se unisce gli indici integrati:
Python
Per migliorare il rendimento delle query, puoi ridurre il valore di S (il set più piccolo
di entità in una singola scansione dell'indice) in O(S/(R * I)) aggiungendo il seguente indice composto:
Index(Photo, size, coloration)
Rispetto all'utilizzo di due indici integrati, questo indice composto produce meno risultati potenziali per gli stessi due filtri di query. Questo approccio migliora notevolmente il rendimento a costo di un indice in più.
Ridurre il numero di indici composti con l'unione degli indici
Sebbene gli indici composti che corrispondono esattamente ai filtri di una query abbiano il rendimento migliore, non è sempre ottimale o possibile aggiungere un indice composto per ogni combinazione di filtri. Devi bilanciare gli indici composti con quanto segue:
Limiti degli indici composti:
Limite Importo Numero massimo di indici composti per un database -
200 se non hai abilitato la fatturazione per il tuo Google Cloud progetto.
Se hai bisogno di quote superiori, devi abilitare la fatturazione per il tuo Google Cloud progetto.
-
1000 se abiliti la fatturazione per il tuo Google Cloud progetto.
Puoi contattare l'assistenza per richiedere un aumento di questo limite.
Somma massima delle dimensioni delle voci dell'indice composto di un'entità 2 MiB Somma massima di quanto segue per un'entità: - numero di valori di proprietà indicizzati
- numero di voci dell'indice composto
20.000 -
- Costi di archiviazione di ogni indice aggiuntivo.
- Effetti sulla latenza di scrittura.
I problemi di indicizzazione si verificano spesso con i campi multivalore come la proprietà tag delle entità Photo.
Ad esempio, supponiamo che la funzionalità di filtro Photo debba ora supportare le clausole di ordinamento decrescente in base a quattro proprietà aggiuntive:
| Photo | ||
|---|---|---|
| Proprietà | Tipo di valore | Descrizione |
date_added |
Numero intero | Data/ora |
rating |
Numero in virgola mobile | Valutazione utente aggregata |
comment_count |
Numero intero | Numero di commenti |
download_count |
Numero intero | Numero di download |
Se ignori il campo tag, puoi selezionare gli indici composti che corrispondono a ogni combinazione di filtri Photo:
Index(Photo, owner_id, -date_added) Index(Photo, owner_id, -comments) Index(Photo, size, -date_added) Index(Photo, size, -comments) ... Index(Photo, owner_id, size, -date_added) Index(Photo, owner_id, size, -comments) ... Index(Photo, owner_id, size, coloration, -date_added) Index(Photo, owner_id, size, coloration, -comments)
Il numero totale di indici composti è:
2^(number of filters) * (number of different orders) = 2 ^ 3 * 4 = 32 composite indexes
Se provi a supportare fino a 3 filtri tag, il numero totale di indici composti è:
2 ^ (3 + 3 tag filters) * 4 = 256 indexes.
Gli indici che includono proprietà multivalore come
tag comportano anche
problemi di espansione degli indici che
aumentano i costi di archiviazione e la latenza di scrittura.
Per supportare i filtri nel campo tag per questa funzionalità, puoi ridurre il numero totale di indici affidandoti agli indici uniti. Il seguente set di indici composti è il minimo richiesto per supportare la funzionalità di filtro Photo con l'ordinamento:
Index(Photo, owner_id, -date_added) Index(Photo, owner_id, -rating) Index(Photo, owner_id, -comments) Index(Photo, owner_id, -downloads) Index(Photo, size, -date_added) Index(Photo, size, -rating) Index(Photo, size, -comments) Index(Photo, size, -downloads) ... Index(Photo, tag, -date_added) Index(Photo, tag, -rating) Index(Photo, tag, -comments) Index(Photo, tag, -downloads)
Il numero di indici composti definiti è:
(number of filters + 1) * (number of orders) = 7 * 4 = 28
L'unione degli indici offre anche i seguenti vantaggi:
- Consente a un'entità
Photodi supportare fino a 1000 tag senza limiti al numero di filtritagper query. - Riduce il numero totale di indici, il che riduce i costi di archiviazione e la latenza di scrittura.
Selezionare gli indici per la tua app
Puoi selezionare gli indici ottimali per il tuo database in modalità Datastore utilizzando due approcci:
Utilizzare l'unione degli indici per supportare query aggiuntive
- Richiede meno indici composti
- Riduce il costo di archiviazione per entità
- Migliora la latenza di scrittura
- Evita l'espansione degli indici
- Il rendimento dipende dalla forma dei dati
Definire un indice composto che corrisponda a più filtri in una query
- Migliora il rendimento delle query
- Rendimento delle query coerente che non dipende dalla forma dei dati
- Deve rimanere entro il limite degli indici composti
- Aumento del costo di archiviazione per entità
- Aumento della latenza di scrittura
Quando determini gli indici ottimali per la tua app, la risposta può cambiare man mano che la forma dei dati cambia. Il campionamento del rendimento delle query ti dà una buona idea delle query comuni della tua app e delle query lente. Con queste informazioni, puoi aggiungere indici per migliorare il rendimento delle query comuni e lente.