API Datastore di App Engine per i servizi in bundle legacy

Questo documento descrive il modello dei dati per gli oggetti archiviati in Datastore, la struttura delle query con l'API e l'elaborazione delle transazioni.

Entità

Gli oggetti in Datastore sono denominati entità. Un'entità ha una o più proprietà denominate, ciascuna delle quali può avere uno o più valori. I valori delle proprietà possono appartenere a diversi tipi di dati, tra cui numeri interi, numeri a virgola mobile, stringhe, date e dati binari. Una query su una proprietà con più valori verifica se uno qualsiasi dei valori soddisfa i criteri della query. Ciò rende queste proprietà utili per il test di appartenenza.

Tipi, chiavi e identificatori

Ogni entità Datastore è di un tipo particolare,che classifica l'entità ai fini delle query; ad esempio, un'applicazione delle risorse umane potrebbe rappresentare ogni dipendente di un'azienda con un'entità di tipo Employee. Inoltre, ogni entità ha una propria chiave, che la identifica in modo univoco. La chiave è costituita dai seguenti componenti:

  • Il tipo di entità
  • Un identificatore, che può essere
    • una stringa del nome della chiave
    • un ID intero
  • Un percorso dell'antenato facoltativo che individua l'entità all'interno della gerarchia di Datastore

L'identificatore viene assegnato al momento della creazione dell'entità. Poiché fa parte della chiave dell'entità, è associato definitivamente all'entità e non può essere modificato. Può essere assegnato in due modi:

  • L'applicazione può specificare la propria stringa di nome della chiave per l'entità.
  • Puoi fare in modo che Datastore assegni automaticamente all'entità un ID numerico intero.

Percorsi predecessori

Le entità in Cloud Datastore formano uno spazio strutturato in modo gerarchico simile alla struttura di directory di un file system. Quando crei un'entità, puoi optionally designare un'altra entità come principale; la nuova entità è secondaria dell'entità principale (tieni presente che, a differenza di un file system, l'entità principale non deve necessariamente esistere). Un'entità senza un elemento padre è una root . L'associazione tra un'entità e la relativa entità è permanente e non può essere modificato dopo la creazione dell'entità. Cloud Datastore non assegnerà mai lo stesso ID numerico a due entità con lo stesso elemento principale o a due entità principali (quelle senza un elemento principale).

L'entità principale, l'entità principale dell'entità principale e così via in modo ricorsivo sono i suoi antenati; le sue entità figlio, le entità figlio delle entità figlio e così via sono i suoi discendenti. Un'entità principale e tutti i suoi discendenti appartengono allo stesso gruppo di entità. La sequenza di entità che inizia con una radice dell'entità e procedere da principale a figlio, portando a una determinata entità, costituiscono il percorso predecessore dell'entità. La chiave completa che identifica l'entità è composta da una sequenza di coppie tipo-identificatore che specifica predecessore e termina con quelli dell'entità stessa:

[Person:GreatGrandpa, Person:Grandpa, Person:Dad, Person:Me]

Per entità base, il percorso predecessore è vuoto e la chiave è costituita esclusivamente da tipo e identificatore dell'entità:

[Person:GreatGrandpa]

Questo concetto è illustrato dal seguente diagramma:

Mostra la relazione tra l'entità principale e le entità figlie nel gruppo di entità

Query e indici

Oltre a recuperare le entità da Datastore direttamente tramite le relative chiavi, un'applicazione può eseguire una query per recuperarle in base ai valori delle relative proprietà. La query opera su entità di un determinato tipo; può specificare filtri sui valori delle proprietà, sulle chiavi e sugli antenati delle entità e può restituire zero o più entità come risultati. Una query può anche specificare ordinare per sequenziare i risultati in base ai valori delle proprietà. I risultati includono tutte entità con almeno (possibilmente null) valore per ogni proprietà menzionata nei filtri e nell'ordinamento e le cui i valori delle proprietà soddisfano tutti i criteri di filtro specificati. La query può restituire intere entità, entità previste, o semplicemente entità chiavi.

Una query tipica include quanto segue:

Quando viene eseguita, la query recupera tutte le entità del tipo specificato che soddisfano tutti i filtri specificati, ordinati nell'ordine specificato. Le query vengono eseguite in sola lettura.

Nota: per risparmiare memoria e migliorare una query, quando possibile, deve specificare un limite al numero vengono restituiti i risultati.

Una query può includere anche un filtro di antenato che limita i risultati solo al gruppo di entità discendente da un antenato specificato. Questa query è nota come query sull'antenato. Per impostazione predefinita, le query sugli antenati restituiscono risultati molto coerenti, che sono garantiti aggiornati con le ultime modifiche ai dati. Le query non relative all'antenato, invece, possono interessare l'intero Datastore anziché un singolo gruppo di entità, ma sono solo coerenti in modo definitivo e potrebbero restituire risultati obsoleti. Se per la tua applicazione è importante l'elevata coerenza, potresti dover tenerne conto quando struttura i dati, posizionando entità correlate nello stesso gruppo di entità in modo che possano essere recuperate con una query predecessore anziché con una query non predecessore. consulta la sezione Strutturare i dati per una elevata coerenza per ulteriori informazioni.

App Engine predefinisce un indice semplice su ogni proprietà di un'entità. Un'applicazione App Engine può definire ulteriori indici personalizzati in un file di configurazione dell'indice denominato datastore-indexes.xml, che viene generato nella directory /war/WEB-INF/appengine-generated dell'applicazione. Il server di sviluppo esegue automaticamente aggiunge 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.

Transazioni

Ogni tentativo di inserire, aggiornare o eliminare un'entità avviene nel contesto di una transazione. Una singola transazione può includere un numero illimitato di operazioni di questo tipo. Per mantenere la coerenza dei dati, la transazione garantisce che tutte le operazioni che contiene vengano applicate a Datastore come unità o, in caso di esito negativo di una qualsiasi operazione, che nessuna di esse venga applicata.

Puoi eseguire più azioni su un'entità all'interno di una singola transazione. Ad esempio, per incrementare un campo contatore in un oggetto, devi leggere il valore del contatore, calcolare il nuovo valore e poi riarchiviarlo. Senza una transazione, è possibile che un altro processo incrementi il contatore tra il momento in cui leggi il valore e quello in cui lo aggiorni, facendo sì che l'applicazione sovrascriva il valore aggiornato. Eseguire la lettura, il calcolo e la scrittura in una singola transazione garantisce che nessun'altra procedura possa interferire con l'incremento.

Transazioni e gruppi di entità

All'interno di una transazione sono consentite solo query sugli antenati: in altre parole, ogni query transazionale deve essere limitata a un singolo gruppo di entità. La transazione stessa può essere applicata a più entità, che possono appartenere a un singolo gruppo di entità o (nel caso di una transazione tra gruppi) a un massimo di venticinque gruppi di entità diversi.

Datastore utilizza la contemporaneità ottimista per gestire le transazioni. Quando due o più transazioni tentano di modificare contemporaneamente lo stesso gruppo di entità (aggiornando le entità esistenti o creandone di nuove), la prima transazione per cui eseguire il commit avrà esito positivo e tutte le altre non andranno a buon fine al momento del commit. Queste altre transazioni possono essere ritentate con i dati aggiornati. Tieni presente che questo limita il numero di scritture simultanee che puoi eseguire su qualsiasi entità in un determinato gruppo di entità.

Transazioni tra gruppi

Una transazione su entità appartenenti a diversi gruppi di entità viene chiamata transazione tra gruppi (XG). La transazione può essere applicata a un massimo di venticinque gruppi di entità e andrà a buon fine a condizione che nessuna transazione simultanea riguardi i gruppi di entità a cui si applica. In questo modo, hai una maggiore flessibilità nell'organizzazione dei dati, perché non devi necessariamente raggruppare elementi eterogenei nello stesso antenato solo per eseguire scritture atomiche.

Come nel caso di una transazione a gruppo singolo, non puoi eseguire una query non precedente in una transazione XG. Tuttavia, puoi eseguire query predecessore su gruppi di entità separati. Le query non transazionali (non predecessori) potrebbero visualizzare tutti, alcuni o nessuno dei risultati di una transazione di cui è stato eseguito il commit in precedenza. Per informazioni di base su questo problema, consulta Scritture e visibilità dei dati in Datastore. Tuttavia, queste query non transazionali hanno maggiori probabilità di mostrare i risultati di una transazione XG parzialmente impegnata rispetto a quelli di una transazione a gruppo singolo parzialmente impegnata.

Una transazione XG che interessa un solo gruppo di entità ha esattamente lo stesso rendimento e lo stesso costo di una transazione non XG a un solo gruppo. In una transazione XG che interessa più gruppi di entità, le operazioni hanno lo stesso costo di come se fossero state eseguite in una transazione non XG, ma potrebbero riscontrare una latenza maggiore.

Scritture in Datastore e visibilità dei dati

I dati vengono scritti in Datastore in due fasi:

  1. Nella fase di commit, i dati delle entità vengono registrati nei log delle transazioni della maggior parte delle repliche e le repliche in cui non sono stati registrati sono contrassegnate come non aggiornate.
  2. La fase di applicazione si svolge in modo indipendente in ogni replica ed è composta da due azioni eseguite in parallelo:
    • I dati dell'entità sono scritti in quella replica.
    • Le righe dell'indice per l'entità vengono scritte in quella replica. Tieni presente che questa operazione può richiedere più tempo rispetto alla scrittura dei dati stessi.

L'operazione di scrittura viene restituita immediatamente dopo la fase di commit e la fase di applicazione viene eseguita in modo asincrono, eventualmente in momenti diversi in ogni replica e con ritardi di alcune centinaia di millisecondi o più dal completamento della fase di commit. Se si verifica un errore durante la fase di commit, vengono eseguiti tentativi automatici; ma se gli errori persistono, Datastore restituisce un messaggio di errore che l'applicazione riceve come eccezione. Se la fase di commit ha esito positivo, ma l'applicazione non riesce in una determinata replica, viene eseguito il rollback dell'applicazione al completamento nella replica quando si verifica una delle seguenti condizioni:

  • Le analisi periodiche di Datastore controllano la presenza di job di commit non completati e li applicano.
  • Alcune operazioni (get, put, delete e query predecessore) che utilizzano il gruppo di entità interessato causano il completamento delle modifiche di cui è stato eseguito il commit ma non ancora applicate nella replica in cui vengono eseguite prima di procedere con la nuova operazione.

Questo comportamento di scrittura può avere diverse implicazioni su come e quando i dati sono visibili all'applicazione in parti diverse delle fasi di commit e applicazione:

  • Se un'operazione di scrittura segnala un errore di timeout, non è possibile determinare (senza tentare di leggere i dati) se l'operazione è riuscita o meno.
  • Poiché le query di recupero e di individuazione dell'antenato di Datastore applicano eventuali modifiche in sospeso alla replica su cui vengono eseguite, queste operazioni mostrano sempre una visione coerente di tutte le transazioni riuscite precedenti. Ciò significa che un'operazione get (ricerca di un'entità aggiornata in base alla relativa chiave) garantisce la visualizzazione della versione più recente dell'entità.
  • Le query non predecessori potrebbero restituire risultati inattivi perché potrebbero essere in esecuzione su una replica su cui non sono ancora state applicate le ultime transazioni. Questo può verificarsi anche se è stata eseguita un'operazione per la quale è garantito che vengano applicate transazioni in sospeso, poiché la query potrebbe essere eseguita su una replica diversa rispetto all'operazione precedente.
  • I tempi delle modifiche simultanee possono influire sui risultati delle query non predecessori. Se un'entità soddisfa una query inizialmente, ma in seguito viene modificata in modo da non farlo più, l'entità può comunque essere inclusa nel set di risultati della query se le modifiche non erano ancora state applicate agli indici nella replica su cui è stata eseguita la query.=

Statistiche di Datastore

Datastore gestisce le statistiche sui dati archiviati per un'applicazione, come quante entità esistono di un determinato tipo o quanto spazio viene utilizzato in base ai valori delle proprietà di un determinato tipo. Puoi visualizzare queste statistiche nel Console Google Cloud Pagina Dashboard di Datastore. Puoi anche utilizzare l'API Datastore per accedere a questi valori in modo programmatico dall'interno dell'applicazione eseguendo query per entità con nomi speciali. Per ulteriori informazioni, consulta Statistiche di Datastore in Java 8.