Indici Datastore

App Engine predefinisce un indice semplice per ogni proprietà di un'entità. Un'applicazione App Engine può definire ulteriori indici personalizzati in un indice di configurazione del deployment denominato index.yaml. Il server di sviluppo aggiunge automaticamente suggerimenti a questo file quando rileva query che non possono essere eseguite con gli indici esistenti. Puoi ottimizzare gli indici manualmente modificando il file prima di caricare l'applicazione.

Nota: il meccanismo di query basato su indici supporta una vasta gamma di query ed è adatto per la maggior parte delle applicazioni. Tuttavia, non supporta alcuni tipi di query comuni in altre tecnologie di database: in particolare, join e query aggregate non sono supportate nel motore di query di Datastore. Consulta: Pagina Query Datastore per conoscere i limiti delle query Datastore.

Definizione e struttura dell'indice

Un indice è definito da un elenco di proprietà di un dato tipo di entità, con nell'ordine corrispondente (crescente o decrescente) di ogni proprietà. Da utilizzare con delle query dei predecessori, l'indice può anche includere i predecessori di un'entità.

Una tabella di indici contiene una colonna per ogni proprietà denominata nella definizione dell'indice. Ogni riga della tabella rappresenta un'entità in Datastore che è un potenziale risultato per le query basate sull'indice. Un'entità è inclusa nell'indice solo se ha un valore indicizzato impostato per ogni proprietà usata nell'indice; se la definizione dell'indice si riferisce a una per la quale l'entità non ha valore, l'entità non verrà visualizzata l'indice e quindi non verrà mai restituito come risultato per qualsiasi query basata sul di Google.

Le righe di una tabella di indici vengono ordinate prima per antenato e poi per valori proprietà, nell'ordine specificato nella definizione dell'indice. L'indice perfetto per una query, che consente di eseguirla in modo più efficiente, è definito sulle seguenti proprietà, in ordine:

  1. Proprietà utilizzate nei filtri di uguaglianza
  2. Proprietà utilizzata in un filtro di disuguaglianza (di cui non possono esistere non più di one)
  3. Proprietà utilizzate negli ordinamenti

In questo modo, tutti i risultati per ogni possibile esecuzione della query vengono visualizzati in righe consecutive della tabella. Datastore esegue una query utilizzando un indice perfetto:

  1. Identifica l'indice corrispondente al tipo di query, alle proprietà di filtro, agli operatori di filtro e agli ordini di ordinamento.
  2. Esegue la scansione dall'inizio dell'indice alla prima entità che soddisfa tutti i le condizioni di filtro della query.
  3. Continua a scansionare l'indice, restituendo ogni entità alla volta, finché non
    • rileva un'entità che non soddisfa le condizioni del filtro oppure
    • raggiunge la fine dell'indice o
    • ha raccolto il numero massimo di risultati richiesti dalla query.

Ad esempio, considera la seguente query:

SELECT * FROM Person WHERE LastName = "Smith"
                       AND Height < 72
                  ORDER BY Height DESC

L'indice perfetto per questa query è una tabella di chiavi per entità di tipo Person, con colonne per i valori di LastName e Height proprietà. L'indice viene ordinato prima in ordine crescente per LastName e poi in ordine decrescente per Height.

Per generare questi indici, configurali nel seguente modo:

indexes:
- kind: Person
  properties:
  - name: LastName
    direction: asc
  - name: Height
    direction: desc

Due query dello stesso tipo, ma con valori di filtro diversi, utilizzano lo stesso indice. Ad esempio, la seguente query utilizza lo stesso indice di quella precedente:

SELECT * FROM Person WHERE LastName = "Jones"
                       AND Height < 63
                     ORDER BY Height DESC

Anche le due query seguenti utilizzano lo stesso indice, nonostante i loro moduli:

SELECT * FROM Person WHERE LastName = "Friedkin"
                       AND FirstName = "Damian"
                     ORDER BY Height ASC

e

SELECT * FROM Person WHERE LastName = "Blair"
                  ORDER BY FirstName, Height ASC

Configurazione degli indici

Per impostazione predefinita, Datastore predefinisce automaticamente un indice per ogni proprietà di ogni tipo di entità. Questi indici predefiniti sono sufficienti per eseguire molte query semplici, ad esempio query solo di uguaglianza e query di semplice disuguaglianza. Per tutte le altre query, l'applicazione deve definire gli indici di cui ha bisogno in una configurazione di indice denominato index.yaml. Se l'applicazione tenta di eseguire una query che non può essere eseguita con gli indici disponibili (predefiniti o specificati nel file di configurazione dell'indice), la query non andrà a buon fine.

Datastore crea indici automatici per le query nei seguenti formati:

  • Query Kindless che utilizzano solo filtri dei predecessori e delle chiavi
  • Query che utilizzano solo filtri di eguaglianza e antenato
  • Query che utilizzano solo filtri di disuguaglianza (che sono limitati a un singolo proprietà)
  • Query che utilizzano solo filtri di antenati, filtri di uguaglianza sulle proprietà e filtri di disuguaglianza sulle chiavi
  • Query senza filtri e con un solo ordinamento per proprietà, crescente o decrescente

Altre forme di query richiedono che i rispettivi indici siano specificati nell'indice di configurazione del deployment, tra cui:

  • Query con filtri dei predecessori e della disuguaglianza
  • Query con uno o più filtri di disuguaglianza su una proprietà e uno o più filtri di uguaglianza su altre proprietà
  • Query con un ordinamento sulle chiavi in ordine decrescente
  • Query con più ordinamenti

Indici e proprietà

Di seguito sono riportate alcune considerazioni speciali da tenere presenti sugli indici e su come riguarda le proprietà delle entità in Datastore:

Proprietà con tipi di valori misti

Quando due entità hanno proprietà con lo stesso nome ma tipi di valori diversi, dell'indice della proprietà ordina le entità prima per tipo di valore seguito da un ordine secondario appropriati per ogni tipo. Ad esempio, se due entità hanno ciascuna una proprietà age, una con un valore intero e una con un valore di stringa, l'entità con il valore intero precede sempre quella con il valore di stringa quando vengono ordinate in base alla proprietà age, indipendentemente dai valori delle proprietà stesse.

Ciò è particolarmente degno di nota nel caso di numeri interi e rappresentazioni in virgola mobile. numeri, che sono trattati come tipi separati da Datastore. Poiché tutti i numeri interi vengono ordinati prima di tutti i numeri in virgola mobile, una proprietà con il valore intero 38 viene ordinata prima di una con il valore in virgola mobile 37.5.

Proprietà non indicizzate

Se sai che non dovrai mai filtrare o ordinare in base a una determinata proprietà, puoi indicare a Datastore di non gestire le voci dell'indice per quella proprietà dichiarandola non indicizzata. Questo riduce i costi di gestione dell'applicazione diminuendo il numero di scritture di Datastore che deve eseguire. Un'entità con una proprietà non indicizzata si comporta come se la proprietà non fosse impostata: le query con un filtro o un ordine di ordinamento per la proprietà non indicizzata non corrisponderanno mai a quell'entità.

Nota: Se una proprietà compare in un indice composto da più proprietà, l'impostazione a non indicizzato ne impedirà l'indicizzazione nell'indice composto.

Ad esempio, supponiamo che un'entità abbia le proprietà a e b e creare un indice in grado di soddisfare query come WHERE a ="bike" and b="red". Supponiamo anche che non ti interessi sulle query WHERE a="bike" e WHERE b="red". Se imposti a su Non indicizzato e crei un indice per a e b, il datastore non creerà voci di indice per gli indici a e b e, di conseguenza, la query WHERE a="bike" and b="red" non funzionerà. Affinché Datastore crei voci per gli indici a e b, sia a che b devono essere indicizzati.

Dichiari una proprietà non indicizzata impostando noindex nel tag del campo struct:

type Person struct {
	Name string
	Age  int `datastore:",noindex"`
}

Tieni presente, tuttavia, che la modifica di una proprietà da non indicizzata a indicizzata influire su eventuali entità esistenti create prima della modifica. Le query che filtrano la proprietà non restituiranno queste entità esistenti, perché le entità non sono state scritte nell'indice della query quando erano è stato creato. Per rendere le entità accessibili per query future, devi riscrivere a Datastore in modo che vengano inseriti nel indici di appartenenza. In altre parole, devi eseguire le seguenti operazioni per ogni entità esistente:

  1. Recupera (ottieni) l'entità da Datastore.
  2. Scrivi (put) di nuovo l'entità in Datastore.

Analogamente, la modifica di una proprietà da indicizzata a non indicizzata interessa solo le entità scritte successivamente in Datastore. Le voci di indice per qualsiasi le entità esistenti con quella proprietà continueranno a esistere finché le entità vengono aggiornati o eliminati. Per evitare risultati indesiderati, devi eliminare definitivamente il codice di tutte le query che filtrano o ordinano in base alla proprietà (ora non indicizzata).

Limiti indice

Datastore impone limiti al numero e alle dimensioni complessive delle voci dell'indice che possono essere associate a una singola entità. Questi limiti sono elevati e la maggior parte delle applicazioni non ne è interessato. Tuttavia, potresti riscontrare questi limiti.

Come descritto sopra, Datastore crea una voce in un indice predefinito per ogni di ogni entità ad eccezione di []byte e quelli che hai esplicitamente dichiarate come non indicizzate. La proprietà possono anche essere inclusi in altri indici personalizzati dichiarati nel tuo Configurazione di index.yaml un file YAML. Se un'entità non ha proprietà elenco, avrà al massimo una voce in ciascun indice personalizzato (per gli indici non predecessori) o una per ciascuno dei predecessori dell'entità (per gli indici dei predecessori). Ognuna di queste voci di indice va aggiornata ogni volta che cambia il valore della proprietà.

Per una proprietà che ha un singolo valore per ogni entità, ogni valore possibile deve essere archiviato una sola volta per entità nell'indice predefinito della proprietà. Uniforme Pertanto, è possibile che un'entità abbia un elevato numero per superare il limite di dimensioni o la voce di indice. Analogamente, un'entità può avere più valori per la stessa proprietà; richiede una voce di indice separata per ogni valore; sempre, se il numero di valori possibili è elevato, l'entità può superare il limite di voci.

La situazione peggiora nel caso di entità con più proprietà, ciascuna delle quali può assumere più valori. Per includere tale entità, l'indice deve includere una voce per ogni possibile combinazione di valori delle proprietà. Gli indici personalizzati che fanno riferimento a più proprietà, ciascuna con più valori, possono "esplodere" in modo combinatorio, richiedendo un numero elevato di voci per un'entità con solo un numero relativamente ridotto di possibili valori delle proprietà. Tale l'esplosione degli indici può aumentare notevolmente il costo di scrittura di un'entità in Datastore, a causa dell'elevato numero di voci di indice che devono essere aggiornato e può anche causare facilmente il superamento della voce di indice o dimensioni massime consentite.

Considera la query

SELECT * FROM Widget WHERE X=1 AND Y=2 ORDER BY Date

che fa sì che l'SDK suggerisca il seguente indice:

indexes:
- kind: Widget
  properties:
  - name: X
  - name: Y
  - name: Date
Questo indice richiederà un totale di |X| * |Y| * |Date| voci per ogni entità (dove |X| indica il numero di valori associati all'entità per proprietà X). Ad esempio, il seguente codice
type Widget struct {
	X    []int
	Y    []string
	Date time.Time
}

func f(ctx context.Context) {
	e2 := &Widget{
		X:    []int{1, 2, 3, 4},
		Y:    []string{"red", "green", "blue"},
		Date: time.Now(),
	}

	k := datastore.NewIncompleteKey(ctx, "Widget", nil)
	if _, err := datastore.Put(ctx, k, e2); err != nil {
		// Handle error.
	}
}

crea un'entità con quattro valori per la proprietà x, tre valori per la proprietà y e date impostato sulla data corrente. Ciò richiederà 12 voci di indice, una per ogni possibile combinazione di valori delle proprietà:

(1, "red", <now>) (1, "green", <now>) (1, "blue", <now>)

(2, "red", <now>) (2, "green", <now>) (2, "blue", <now>)

(3, "red", <now>) (3, "green", <now>) (3, "blue", <now>)

(4, "red", <now>) (4, "green", <now>) (4, "blue", <now>)

Quando la stessa proprietà viene ripetuta più volte, Datastore può rilevare indici in esplosione e suggerire un indice alternativo. Tuttavia, in tutte le altre circostanze (ad esempio la query definita in questo esempio), Datastore genererà un indice esplosivo. In questo caso, puoiaggirare l'indice esplosivo configurando manualmente un indice nel file di configurazione dell'indice:

indexes:
- kind: Widget
  properties:
  - name: X
  - name: Date
- kind: Widget
  properties:
  - name: Y
  - name: Date
In questo modo, il numero di voci necessarie si riduce a (|X| * |Date| + |Y| * |Date|), ovvero 7 voci anziché 12:

(1, <now>) (2, <now>) (3, <now>) (4, <now>)

("red", <now>) ("green", <now>) ("blue", <now>)

Qualsiasi operazione put che comporterebbe il superamento della voce o della dimensione dell'indice limite non andrà a buon fine un errore. Il testo del descrive quale limite era superato ("Too many indexed properties" o "Index entries too large") e la causa dell'indice personalizzato. Se crei un nuovo indice che superi i limiti per qualsiasi entità al momento della creazione, le query sull'indice non andranno a buon fine e l'indice verrà visualizzato nello stato Error nella console Google Cloud. A risolvere gli indici nello stato Error:

  1. Rimuovi l'indice nello stato Error dal file index.yaml.

  2. Esegui il seguente comando dalla directory in cui si trova index.yaml per rimuovere l'indice da Datastore:

    gcloud datastore indexes cleanup index.yaml
    
  3. Risolvi la causa dell'errore. Ad esempio:

    • Riformula la definizione dell'indice e le query corrispondenti.
    • Rimuovi le entità che causano l'esplosione dell'indice.
  4. Aggiungi di nuovo l'indice al file index.yaml.

  5. Esegui questo comando dalla directory in cui si trova index.yaml per creare l'indice in Datastore:

    gcloud datastore indexes create index.yaml
    

Puoi evitare di creare indici esplosi evitando le query che richiederebbero un indice personalizzato utilizzando una proprietà elenco. Come descritto sopra, sono incluse le query con più ordini di ordinamento o con una combinazione di filtri di uguaglianza e disuguaglianza.