Indici Datastore

Nota: Gli sviluppatori che creano nuove applicazioni sono vivamente incoraggiati a utilizzare il Libreria client NDB, che offre numerosi vantaggi rispetto a questa libreria client, come la memorizzazione automatica nella cache delle entità tramite tramite Google Cloud CLI o tramite l'API Compute Engine. Se al momento utilizzi la libreria client DB precedente, leggi la guida alla migrazione da DB a NDB

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 indice supporta un'ampia gamma di query ed è adatto a per la maggior parte delle applicazioni. Tuttavia, non supporta alcuni tipi di query comuni in altre tecnologie di database: in particolare, le unioni e le 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 in un elenco di proprietà di un determinato tipo di entità, con un ordine corrispondente (crescente o decrescente) per ogni proprietà. Per l'utilizzo con le query sugli antenati, l'indice può facoltativamente includere anche gli antenati di un'entità.

Una tabella dell'indice contiene una colonna per ogni proprietà denominata nella definizione di Kubernetes. Ogni riga della tabella rappresenta un'entità in Datastore che è un potenziale risultato per le query basate sul di Google. Un'entità viene inclusa nell'indice solo se ha un valore indicizzato per ogni proprietà utilizzata nell'indice. Se la definizione dell'indice fa riferimento a una proprietà per la quale l'entità non ha un valore, l'entità non verrà visualizzata nell'indice e, di conseguenza, non verrà mai restituita come risultato per qualsiasi query basata sull'indice.

Nota: Datastore distingue tra una persona giuridica che non possiede una proprietà e una che possiede con un valore nullo (None). Se assegni esplicitamente un valore nullo alla proprietà di un'entità, quest'ultima potrebbe essere inclusa nei risultati. di una query che fa riferimento a quella proprietà.

Nota: gli indici composti da più proprietà richiedono che ogni singola proprietà non sia impostata su Non indicizzato.

Le righe di una tabella di indice vengono ordinate prima per predecessore e poi per proprietà dei valori, 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 ordini di ordinamento

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

  1. Identifica l'indice corrispondente al tipo, alle proprietà di filtro e operatori di filtro e ordinare gli ordini.
  2. Esegue la scansione dall'inizio dell'indice fino alla prima entità che soddisfa tutte 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 (indicata in GQL):

SELECT * FROM Person WHERE last_name = "Smith"
                       AND height < 72
                  ORDER BY height DESC

L'indice perfetto per questa query è una tabella di chiavi per le entità di tipo Person, con colonne per i valori delle proprietà last_name e height. L'indice viene ordinato per primo in ordine crescente per last_name e poi in ordine decrescente per height.

Per generare questi indici, configurali come segue:

indexes:
- kind: Person
  properties:
  - name: last_name
    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 last_name = "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 last_name = "Friedkin"
                       AND first_name = "Damian"
                     ORDER BY height ASC

e

SELECT * FROM Person WHERE last_name = "Blair"
                  ORDER BY first_name, height ASC

Configurazione degli indici

Per impostazione predefinita, Datastore predefinisce automaticamente un indice per ogni di ogni tipo di entità. Questi indici predefiniti sono sufficienti molte query semplici, ad esempio quelle che presentano solo l'uguaglianza e le semplici disuguaglianze query. 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 con un'eccezione NeedIndexError.

Datastore crea indici automatici per le query dei seguenti tipi:

  • 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 su una 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 delle chiavi in ordine decrescente
  • Query con più ordini di ordinamento

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à dello stesso nome, ma tipi di valore diversi, un indice della proprietà ordina le entità innanzitutto in base al tipo di valore e poi in base a un ordinamento secondario appropriato per ogni tipo. Ad esempio, se due entità hanno ciascuna una proprietà denominata age, uno con un valore intero e l'altro con un valore stringa, l'entità il valore intero precede sempre quello con il valore stringa quando viene ordinato. dalla proprietà age, a prescindere 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 numero intero il valore 38 è ordinato prima di uno con il valore con virgola mobile 37.5.

Proprietà non indicizzate

Se sai che non dovrai mai filtrare o ordinare i dati in base a una determinata proprietà, possiamo indicare a Datastore di non mantenere le voci di indice corrispondenti dichiarando la proprietà unindexed. In questo modo, si riduce il costo di esecuzione della tua 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 erano impostati: le query con un filtro o un ordinamento nella proprietà non indicizzata verranno non corrisponderà mai a quell'entità.

Nota: se una proprietà viene visualizzata in un indice composto da più proprietà e impostandola su Non indicizzato, non verrà indicizzata nell'indice composto.

Ad esempio, supponiamo che un'entità abbia le proprietà a e b e che tu voglia 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 Datastore non creerà voci di indice per a e b, pertanto la query WHERE a="bike" and b="red" non verrà al lavoro. Affinché Datastore crei voci per gli indici a e b, sia a che b devono essere indicizzati.

Dichiari una proprietà non indicizzata impostando indexed=False nel costruttore di proprietà:

class Person(db.Model):
  name = db.StringProperty()
  age = db.IntegerProperty(indexed=False)

In un secondo momento puoi ripristinare l'indice della proprietà chiamando di nuovo il costruttore con indexed=True:

class Person(db.Model):
  name = db.StringProperty()
  age = db.IntegerProperty(indexed=True)

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 in base alla proprietà non restituiranno queste entità esistenti, perché non sono state scritte nell'indice della query al momento della loro creazione. Per rendere le entità accessibili dalle query future, devi riscriverle in Datastore in modo che vengano inserite negli indici appropriati. In altre parole, devi eseguire le seguenti operazioni per ogni entità esistente:

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

Analogamente, la modifica di una proprietà da indicizzata a non indicizzata interessa solo le entità successivamente scritte 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 per gli indici

Datastore impone dei limiti al numero e la dimensione complessiva delle voci di indice che possono essere associate a una singola entità. Questi limiti sono elevati e la maggior parte delle applicazioni non è interessata. Tuttavia, esistono situazioni in cui potresti riscontrare dei limiti.

Come descritto sopra, Datastore crea una voce in un indice predefinito per ogni proprietà di ogni entità, tranne per stringhe di testo lunghe (Text) e stringhe di byte lunghe (Blob) e quelle che hai dichiarato esplicitamente 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à con un singolo valore per ogni entità, ogni possibile valore deve essere memorizzato una sola volta per entità nell'indice predefinito della proprietà. Tuttavia, è possibile che un'entità con un numero elevato di proprietà con un solo valore superi il limite di dimensioni o di voci dell'indice. Analogamente, un'entità che può avere più valori per la stessa proprietà richiede una voce dell'indice distinta per ogni valore. Anche in questo caso, se il numero di valori possibili è elevato, un'entità di questo tipo 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à, ognuna con più valori, possono "esplodere" combinatoricamente, richiedendo un numero elevato di voci per una con un numero relativamente ridotto di possibili valori di 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
class Widget(db.Expando):
  pass

e2 = Widget()
e2.x = [1, 2, 3, 4]
e2.y = ['red', 'green', 'blue']
e2.date = datetime.datetime.now()
e2.put()

crea un'entità con quattro valori per la proprietà x, tre valori per la proprietà y e date impostati 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 (come la query definita in questo esempio), Datastore genererà un indice enorme. 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 BadRequestError un'eccezione. Il testo del un'eccezione descrive quale limite 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. Per risolvere gli indici nello stato Error:

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

  2. Esegui questo comando dalla directory in cui si trova index.yaml per rimuovere quell'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 il seguente comando dalla directory in cui si trova index.yaml per creare l'indice in Datastore:

    gcloud datastore indexes create index.yaml
    

Per evitare l'esplosione degli indici, evita le query che richiederebbero una configurazione utilizzare una proprietà list. Come descritto sopra, sono incluse le query con query o ordini di ordinamento multipli con una combinazione di filtri di uguaglianza e disuguaglianza.