Nota: Se recomienda encarecidamente a los desarrolladores que creen aplicaciones nuevas que usen la biblioteca de cliente de NDB, que ofrece varias ventajas en comparación con esta biblioteca de cliente, como el almacenamiento automático en caché de entidades mediante la API Memcache. Si actualmente usas la biblioteca de cliente de DB anterior, consulta la guía de migración de DB a NDB.
En este documento se describe el modelo de datos de los objetos almacenados en Datastore, cómo se estructuran las consultas mediante la API y cómo se procesan las transacciones.
Entidades
Los objetos de Datastore se denominan entidades. Una entidad tiene una o varias propiedades con nombre, cada una de las cuales puede tener uno o varios valores. Los valores de las propiedades pueden pertenecer a varios tipos de datos, como números enteros, números de punto flotante, cadenas, fechas o datos binarios. Las consultas que se realizan a una propiedad que dispone de varios valores comprueban si alguno de estos coincide con los criterios de la consulta. Por eso, estas propiedades son útiles para probar la pertenencia.
Tipos, claves e identificadores
Cada entidad de Datastore es de un tipo concreto,que clasifica la entidad para las consultas. Por ejemplo, una aplicación de recursos humanos puede representar a cada empleado de una empresa con una entidad de tipo Employee
. Además, cada entidad tiene su propia clave, que la identifica de forma única. La clave consta de los siguientes componentes:
- Tipo de entidad
- Un identificador, que puede ser uno de los siguientes:
- Una cadena key name
- un ID entero
- Una ruta de ancestro opcional que indica la ubicación de la entidad en la jerarquía de Datastore
El identificador se asigna cuando se crea la entidad. Como forma parte de la clave de la entidad, se asocia de forma permanente a la entidad y no se puede cambiar. Se puede asignar de dos formas:
- Tu aplicación puede especificar su propia cadena de nombre de clave para la entidad.
- Puedes hacer que Datastore asigne automáticamente a la entidad un ID numérico entero.
Rutas de ancestros
Las entidades de Cloud Datastore forman un espacio estructurado jerárquicamente similar a la estructura de directorios de un sistema de archivos. Cuando creas una entidad, puedes designar otra entidad como principal. La nueva entidad será una secundaria de la entidad principal (ten en cuenta que, a diferencia de lo que ocurre en un sistema de archivos, la entidad principal no tiene por qué existir). Una entidad sin elemento superior es una entidad raíz. La asociación entre una entidad y su elemento superior es permanente y no se puede cambiar una vez que se ha creado la entidad. Cloud Datastore nunca asignará el mismo ID numérico a dos entidades con el mismo elemento superior ni a dos entidades raíz (aquellas que no tienen un elemento superior).
El elemento superior de una entidad, el elemento superior del elemento superior, etc., son sus ancestros. Sus elementos secundarios, los elementos secundarios de los elementos secundarios, etc., son sus descendientes. Una entidad raíz y todos sus descendientes pertenecen al mismo grupo de entidades. La secuencia de entidades que empieza con una entidad raíz y continúa de la entidad superior a la secundaria hasta llegar a una entidad determinada constituye la ruta de ancestros de esa entidad. La clave completa que identifica la entidad consta de una secuencia de pares tipo-identificador que especifican su ruta de ancestros y termina con los de la propia entidad:
[Person:GreatGrandpa, Person:Grandpa, Person:Dad, Person:Me]
En el caso de una entidad raíz, la ruta de ancestro está vacía y la clave se compone únicamente del tipo y el identificador de la entidad:
[Person:GreatGrandpa]
Este concepto se ilustra en el siguiente diagrama:
Consultas e índices
Además de obtener entidades de Datastore directamente por sus claves, una aplicación puede realizar una consulta para obtenerlas por los valores de sus propiedades. La consulta opera en entidades de un tipo determinado. Puede especificar filtros en los valores de las propiedades, las claves y los antecesores de las entidades, y puede devolver cero o más entidades como resultados. Una consulta también puede especificar órdenes de clasificación para secuenciar los resultados por los valores de sus propiedades. Los resultados incluyen todas las entidades que tienen al menos un valor (posiblemente nulo) para cada propiedad cuyo nombre se indica en los filtros y los criterios de ordenación, y cuyos valores de propiedad cumplen todos los criterios de filtro especificados. La consulta puede devolver entidades completas, entidades proyectadas o solo claves de entidades.
Una consulta típica incluye lo siguiente:
- Un tipo de entidad al que se aplica la consulta
- Cero o más filtros basados en los valores de las propiedades, las claves y los antecesores de las entidades
- Cero o más órdenes de ordenación para secuenciar los resultados
Nota: Para ahorrar memoria y mejorar el rendimiento, las consultas deben especificar, siempre que sea posible, un límite en el número de resultados devueltos.
Una consulta también puede incluir un filtro de ancestro que limite los resultados al grupo de entidades que descienda de un ancestro especificado. Este tipo de consulta se conoce como consulta de ancestro. De forma predeterminada, las consultas de ancestros devuelven resultados fuertemente coherentes, que se actualizan con los últimos cambios en los datos. Por el contrario, las consultas que no son de ancestros pueden abarcar todo el almacén de datos en lugar de un solo grupo de entidades, pero solo tienen coherencia final y pueden devolver resultados obsoletos. Si la coherencia fuerte es importante para tu aplicación, puede que tengas que tenerlo en cuenta al estructurar tus datos. Coloca las entidades relacionadas en el mismo grupo de entidades para que se puedan recuperar con una consulta de ancestro en lugar de con una consulta de no ancestro. Consulta Estructurar datos para lograr una coherencia fuerte para obtener más información.
App Engine predefine un índice simple en cada propiedad de una entidad.
Una aplicación de App Engine puede definir más índices personalizados en un archivo de configuración de índices llamado
index.yaml
. El servidor de desarrollo añade automáticamente sugerencias a este archivo cuando detecta consultas que no se pueden ejecutar con los índices actuales.
Puedes ajustar estos índices manualmente modificando el archivo antes de subir la aplicación.
Nota: El mecanismo de consulta basado en índices admite una amplia gama de consultas y es adecuado para la mayoría de las aplicaciones. Sin embargo, no admite algunos tipos de consultas habituales en otras tecnologías de bases de datos. En concreto, las combinaciones y las consultas de agregación no se admiten en el motor de consultas de Datastore. Consulta la página Consultas de Datastore para ver las limitaciones de las consultas de Datastore.
Transacciones
Cada intento de insertar, actualizar o eliminar una entidad se lleva a cabo en el contexto de una transacción. Una sola transacción puede incluir cualquier número de estas operaciones. Para mantener la coherencia de los datos, la transacción asegura que todas las operaciones que contiene se apliquen a Datastore como una unidad o, si falla alguna de las operaciones, que no se aplique ninguna.
Puedes realizar varias acciones en una entidad en una sola transacción. Por ejemplo, para incrementar un campo de contador en un objeto, debes leer el valor del contador, calcular el nuevo valor y, a continuación, volver a almacenarlo. Sin una transacción, es posible que otro proceso incremente el contador entre el momento en que lees el valor y el momento en que lo actualizas, lo que provoca que tu aplicación sobrescriba el valor actualizado. Al realizar la lectura, el cálculo y la escritura en una sola transacción, se asegura de que ningún otro proceso pueda interferir en el incremento.
Transacciones y grupos de entidades
En una transacción solo se permiten consultas de ancestros, es decir, cada consulta transaccional debe limitarse a un solo grupo de entidades. La transacción en sí puede aplicarse a varias entidades, que pueden pertenecer a un solo grupo de entidades o (en el caso de una transacción entre grupos) a un máximo de veinticinco grupos de entidades diferentes.
Datastore usa la concurrencia optimista para gestionar las transacciones. Cuando dos o más transacciones intentan cambiar el mismo grupo de entidades al mismo tiempo (ya sea actualizando entidades o creando otras nuevas), la primera transacción que se confirme se completará correctamente y todas las demás fallarán. Estas otras transacciones se pueden volver a intentar con los datos actualizados. Ten en cuenta que esto limita el número de escrituras simultáneas que puedes hacer en cualquier entidad de un grupo de entidades determinado.
Transacciones entre grupos
Una transacción en entidades que pertenecen a diferentes grupos de entidades se denomina transacción entre grupos (XG). La transacción se puede aplicar a un máximo de 25 grupos de entidades y se completará siempre que ninguna transacción simultánea afecte a ninguno de los grupos de entidades a los que se aplica. De esta forma, tiene más flexibilidad para organizar sus datos, ya que no se ve obligado a colocar datos dispares en el mismo elemento antecesor solo para realizar escrituras atómicas en ellos.
Al igual que en una transacción de un solo grupo, no puedes realizar una consulta que no sea de ancestro en una transacción XG. Sin embargo, puedes realizar consultas de ancestros en grupos de entidades independientes. Las consultas no transaccionales (no antecesoras) pueden ver todos, algunos o ninguno de los resultados de una transacción confirmada anteriormente. Para obtener información general sobre este problema, consulta Escrituras en el almacén de datos y visibilidad de los datos. Sin embargo, es más probable que estas consultas no transaccionales vean los resultados de una transacción XG parcialmente confirmada que los de una transacción de un solo grupo parcialmente confirmada.
Una transacción XG que solo afecta a un grupo de entidades tiene exactamente el mismo rendimiento y coste que una transacción que no es XG y que afecta a un solo grupo. En una transacción XG que afecta a varios grupos de entidades, las operaciones cuestan lo mismo que si se realizaran en una transacción que no es XG, pero pueden experimentar una latencia mayor.
Escrituras de Datastore y visibilidad de los datos
Los datos se escriben en Datastore en dos fases:
- En la fase de confirmación, los datos de la entidad se registran en los registros de transacciones de la mayoría de las réplicas, y las réplicas en las que no se hayan registrado se marcan como que no tienen registros actualizados.
- La fase de aplicación se produce de forma independiente en cada réplica y consta de dos acciones que se realizan en paralelo:
- Los datos de la entidad se escriben en esa réplica.
- Las filas de índice de la entidad se escriben en esa réplica. Ten en cuenta que este proceso puede tardar más que la escritura de los datos en sí.
La operación de escritura se devuelve inmediatamente después de la fase de confirmación y la fase de aplicación se lleva a cabo de forma asíncrona, posiblemente en momentos diferentes en cada réplica y con retrasos de unos cientos de milisegundos o más desde la finalización de la fase de confirmación. Si se produce un error durante la fase de confirmación, se reintenta automáticamente. Sin embargo, si los errores continúan, Datastore devuelve un mensaje de error que tu aplicación recibe como una excepción. Si la fase de confirmación se completa correctamente, pero la fase de aplicación falla en una réplica concreta, la aplicación se completa en esa réplica cuando ocurre una de las siguientes situaciones:
- Las limpiezas periódicas de Datastore comprueban si hay trabajos de confirmación incompletos y los aplican.
- Determinadas operaciones (
get
,put
,delete
y consultas de ancestros) que usan el grupo de entidades afectado provocan que los cambios que se han confirmado, pero que aún no se han aplicado, se completen en la réplica en la que se están ejecutando antes de continuar con la nueva operación.
Este comportamiento de escritura puede tener varias implicaciones sobre cómo y cuándo se muestran los datos a tu aplicación en diferentes partes de las fases de confirmación y aplicación:
- Si una operación de escritura informa de un error de tiempo de espera, no se puede determinar (sin intentar leer los datos) si la operación se ha realizado correctamente o no.
- Como las consultas get y ancestor de Datastore aplican las modificaciones pendientes a la réplica en la que se ejecutan, estas operaciones siempre muestran una vista coherente de todas las transacciones anteriores que se han completado correctamente. Esto significa que una operación
get
(buscar una entidad actualizada por su clave) tiene garantizada la consulta de la versión más reciente de esa entidad. - Las consultas que no son de ancestros pueden devolver resultados obsoletos porque pueden ejecutarse en una réplica en la que aún no se han aplicado las últimas transacciones. Esto puede ocurrir incluso si se ha realizado una operación que tiene garantizada la aplicación de las transacciones pendientes, ya que la consulta puede ejecutarse en una réplica diferente a la de la operación anterior.
- El momento en el que se produzcan los cambios simultáneos puede afectar a los resultados de las consultas que no sean de ancestros. Si una entidad cumple inicialmente una consulta, pero más adelante se modifica de forma que deja de cumplirla, es posible que la entidad se siga incluyendo en el conjunto de resultados de la consulta si los cambios aún no se han aplicado a los índices de la réplica en la que se ha ejecutado la consulta.=
Estadísticas de Datastore
Datastore mantiene estadísticas sobre los datos almacenados de una aplicación, como el número de entidades de un tipo determinado o el espacio que ocupan los valores de propiedad de un tipo determinado. Puede ver estas estadísticas en la página Panel de control de Datastore de laGoogle Cloud consola. También puede usar la API Datastore para acceder a estos valores de forma programática desde la aplicación consultando entidades con nombres especiales. Consulte Estadísticas de Datastore en Python 2 para obtener más información.
Ejemplo de Datastore de Python 2
En la API de Python, un modelo describe un tipo de entidad, incluidos los tipos y la configuración de sus propiedades. Una aplicación define un modelo mediante una clase de Python, con atributos de clase que describen las propiedades. El nombre de la clase se convierte en el nombre del tipo de entidad. Las entidades del tipo indicado se representan mediante instancias de la clase de modelo, con atributos de instancia que representan los valores de las propiedades. Para crear una entidad, crea un objeto de la clase que quieras, define sus atributos y, a continuación, guárdalo (llamando a un método como put()
):
import datetime
from google.appengine.ext import db
from google.appengine.api import users
class Employee(db.Model):
name = db.StringProperty(required=True)
role = db.StringProperty(required=True,
choices=set(["executive", "manager", "producer"]))
hire_date = db.DateProperty()
new_hire_training_completed = db.BooleanProperty(indexed=False)
email = db.StringProperty()
e = Employee(name="John",
role="manager",
email=users.get_current_user().email())
e.hire_date = datetime.datetime.now().date()
e.put()
La API Datastore proporciona dos interfaces para las consultas: una interfaz de objeto de consulta y un lenguaje de consulta similar a SQL llamado GQL. Una consulta devuelve entidades en forma de instancias de las clases de modelo:
training_registration_list = ["Alfred.Smith@example.com",
"jharrison@example.com",
"budnelson@example.com"]
employees_trained = db.GqlQuery("SELECT * FROM Employee WHERE email IN :1",
training_registration_list)
for e in employees_trained:
e.new_hire_training_completed = True
db.put(e)