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 API. Se al momento utilizzi la libreria client DB precedente, leggi il Guida alla migrazione da database a NDB

App Engine predefinisce un indice semplice su 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 suggerimenti a questo file quando rileva query che non possono essere eseguite con gli indici esistenti. Puoi ottimizzare manualmente gli indici 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, 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 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à è 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.

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 deve essere impostato su unindexed.

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 può essere eseguita in modo più efficiente, è definita le seguenti proprietà, in ordine:

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

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:

  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 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 di filtro
    • 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 entità di tipo Person, con colonne per i valori di last_name e height proprietà. L'indice viene ordinato per primo in ordine crescente per last_name e poi in ordine decrescente per height.

Per generare questi indici, configurali nel seguente modo:

indexes:
- kind: Person
  properties:
  - name: last_name
    direction: asc
  - name: height
    direction: desc

Due query dello stesso modulo, ma con valori di filtro diversi, utilizzano lo stesso di Google. 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 possono essere eseguite con gli indici disponibili (predefiniti o specificato nel file di configurazione dell'indice), la query avrà esito negativo con un'eccezione NeedIndexError.

Datastore crea indici automatici per le query nei seguenti formati:

  • Query Kindless con filtri solo dei predecessori e delle chiavi
  • Query che utilizzano solo filtri dei predecessori e di uguaglianza
  • Query che utilizzano solo filtri di disuguaglianza (che sono limitati a un singolo proprietà)
  • Query che utilizzano solo filtri dei predecessori, filtri di uguaglianza sulle proprietà e filtri di disuguaglianza nelle 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 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à 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, indipendentemente dai valori delle proprietà stesse.

Ciò è particolarmente degno di nota nel caso di numeri interi e rappresentazioni in virgola mobile. numeri, che vengono 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. 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 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à 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 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 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 seguito puoi reimpostare la proprietà in indicizzata chiamando il costruttore di nuovo 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 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. Ciò significa che devi procedere come segue per ciascuna di queste entità esistenti:

  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à 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 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 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 stringhe di testo lunghe (Text) e stringhe di byte lunghi (Blob) e quelli che hai esplicitamente dichiarato come non indicizzato. 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à 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à, ognuno dei 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. Richiede 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, puoi aggira l'esplosione dell'indice configurando manualmente un indice al suo interno di configurazione del deployment:

indexes:
- kind: Widget
  properties:
  - name: x
  - name: date
- kind: Widget
  properties:
  - name: y
  - name: date
In questo modo si riduce il numero di voci necessarie a solo (|x| * |date| + |y| * |date|), oppure 7 voci invece di 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 di un indice limite non andrà a buon fine Un BadRequestError un'eccezione. Il testo del indica quale limite era superato ("Too many indexed properties" o "Index entries too large") e la causa dell'indice personalizzato. Se crei un nuovo indice superando limiti per qualsiasi entità quando viene creata, le query sull'indice avranno esito negativo l'indice verrà visualizzato nello stato Error nella console Google Cloud. A risolvere gli indici nello stato Error:

  1. Rimuovi l'indice in 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 questo 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.