Nota: gli sviluppatori che creano nuove applicazioni sono vivamente invitati a utilizzare la libreria client NDB, che offre diversi vantaggi rispetto a questa libreria client, ad esempio la memorizzazione nella cache automatica delle entità tramite l'API Memcache. Se al momento utilizzi la libreria client DB precedente, leggi la guida alla migrazione da DB a NDB
Datastore offre disponibilità elevata, scalabilità e durabilità la distribuzione dei dati su molte macchine e l'uso la replicazione su un'ampia area geografica. Tuttavia, c'è un compromesso progettazione, ovvero la velocità effettiva di scrittura per ogni gruppo di entità è limitato a circa un commit al secondo ed esistono limitazioni per le query o le transazioni che in più gruppi di entità. Questa pagina descrive queste limitazioni in modo più dettagliato e illustra le best practice per strutturare i dati in modo da supportare una consistenza rigorosa, soddisfacendo al contempo i requisiti di throughput in scrittura dell'applicazione.
Le letture a elevata coerenza restituiscono sempre i dati correnti e, se eseguite all'interno di una transazione, sembrano provenire da un singolo snapshot coerente. Tuttavia, le query devono specificare un filtro antenato per essere fortemente coerenti o partecipare a una transazione e le transazioni possono coinvolgere al massimo 25 gruppi di entità. Le letture eventualmente coerenti non presentano queste limitazioni e sono adeguate in molti casi. L'utilizzo di letture coerenti alla fine consente di distribuire i dati tra un numero maggiore di gruppi di entità, in modo da di ottenere una velocità effettiva di scrittura maggiore eseguendo i commit in parallelo gruppi di entità diversi. Tuttavia, devi comprendere le caratteristiche delle letture eventualmente coerenti per determinare se sono adatte alla tua applicazione:
- I risultati di queste letture potrebbero non riflettere le transazioni più recenti. Questo perché queste letture non assicurano che la replica sia in esecuzione su sia aggiornato. Utilizzano invece qualsiasi dato disponibile su quella replica al momento dell'esecuzione della query. La latenza della replica è quasi sempre inferiore a qualche secondo.
- Una transazione impegnata che ha interessato più entità potrebbe sembrare è stato applicato ad alcune entità e non ad altre. Tieni però presente che una transazione non verrà mai visualizzata come applicata parzialmente all'interno di una singola entità.
- I risultati della query possono includere entità che non avrebbero dovuto essere incluse in base ai criteri del filtro e potrebbero escludere entità che dovrebbero avere inclusi. Questo può verificarsi perché gli indici potrebbero essere letti in una versione diversa rispetto a quella dell'entità stessa.
Per capire come strutturare i dati per una coerenza elevata, confronta due approcci diversi per una semplice applicazione guestbook. Il primo approccio crea una nuova entità base per ogni entità creata:
import webapp2
from google.appengine.ext import db
class Guestbook(webapp2.RequestHandler):
def post(self):
greeting = Greeting()
...
Esegue quindi una query sul tipo di entità Greeting
per i dieci saluti più recenti.
import webapp2
from google.appengine.ext import db
class MainPage(webapp2.RequestHandler):
def get(self):
self.response.out.write('<html><body>')
greetings = db.GqlQuery("SELECT * "
"FROM Greeting "
"ORDER BY date DESC LIMIT 10")
Tuttavia, poiché utilizzi una query non relativa all'antenato, la replica utilizzata per eseguire la query in questo schema potrebbe non aver visto il nuovo saluto al momento dell'esecuzione della query. Tuttavia, quasi tutte le scritture saranno disponibili per le query non relative agli antenati entro pochi secondi dal commit. Per molte applicazioni, soluzione che fornisce i risultati di una query non predecessore nel contesto della le modifiche dell'utente corrente sono in genere sufficienti per eseguire la replica completamente accettabili.
Se l'elevata coerenza è importante per la tua applicazione, un approccio alternativo è scrivere entità con un percorso dell'antenato che identifichi la stessa entità principale in tutte le entità che devono essere lette in una singola query sull'antenato fortemente coerente:
import webapp2
from google.appengine.ext import db
class Guestbook(webapp2.RequestHandler):
def post(self):
guestbook_name=self.request.get('guestbook_name')
greeting = Greeting(parent=guestbook_key(guestbook_name))
...
Potrai quindi eseguire una query da predecessore a elevata coerenza all'interno del gruppo di entità identificato dall'entità radice comune:
import webapp2
from google.appengine.ext import db
class MainPage(webapp2.RequestHandler):
def get(self):
self.response.out.write('<html><body>')
guestbook_name=self.request.get('guestbook_name')
greetings = db.GqlQuery("SELECT * "
"FROM Greeting "
"WHERE ANCESTOR IS :1 "
"ORDER BY date DESC LIMIT 10",
guestbook_key(guestbook_name))
Questo approccio garantisce elevata coerenza scrivendo a un singolo gruppo di entità per libro degli ospiti, ma limita anche le modifiche al libro degli ospiti a un 1 scrittura al secondo (il limite supportato per i gruppi di entità). Se la tua applicazione è probabile che riscontri un utilizzo più elevato delle scritture, potresti dover prendere in considerazione l'utilizzo di altri mezzi: ad esempio, potresti inserire i post recenti in un memcache con una scadenza e mostrare un mix di post recenti da memcache e Datastore oppure memorizzarli nella cache in un cookie, inserire un po' di stato nell'URL o qualcos'altro. L'obiettivo è trovare una soluzione di memorizzazione nella cache che fornisce i dati relativi all'utente corrente per il periodo di tempo in cui che l'utente sta pubblicando sulla tua applicazione. Ricorda che, se usi un comando GET, un predecessore query o qualsiasi operazione all'interno di una transazione, vedrai sempre dati scritti di recente.