Puedes usar las prácticas recomendadas que se indican aquí como referencia rápida de lo que debes tener en cuenta al crear una aplicación que use Firestore en modo Datastore. Si acabas de empezar a usar el modo de Datastore, puede que esta página no sea el mejor lugar para empezar, ya que no te enseña los conceptos básicos de cómo usar el modo de Datastore. Si eres un usuario nuevo, te recomendamos que empieces con la guía Primeros pasos con Firestore en el modo Datastore.
General
- Utiliza siempre caracteres UTF-8 para los nombres de espacios de nombres, nombres de tipos, nombres de propiedades y nombres de claves personalizadas. Los caracteres que no sean UTF-8 utilizados en estos nombres pueden interferir con la funcionalidad del modo Datastore. Por ejemplo, si el nombre de una propiedad contiene un carácter que no es UTF-8, no se podrá crear un índice que utilice esa propiedad.
- No utilice barras inclinadas (
/
) en los nombres de tipo ni en los nombres de clave personalizados. Las barras diagonales de estos nombres podrían interferir con funciones futuras. - No almacenes información sensible en un ID de proyecto de Cloud. Es posible que se conserve un ID de proyecto de Cloud más allá de la vida útil de tu proyecto.
Llamadas a la API
- Usa operaciones por lotes para las lecturas, escrituras y eliminaciones en lugar de operaciones individuales. Las operaciones por lotes son más eficientes porque realizan varias operaciones con la misma sobrecarga que una sola operación.
- Si una transacción falla, asegúrate de intentar revertirla. La reversión minimiza la latencia de reintento de una solicitud diferente que compite por los mismos recursos en una transacción. Ten en cuenta que la propia reversión puede fallar, por lo que la reversión debe ser solo un intento de la mejor manera posible.
- Utiliza llamadas asíncronas en lugar de llamadas síncronas cuando estén disponibles.
Las llamadas asíncronas minimizan el impacto de la latencia. Por ejemplo, supongamos que una aplicación necesita el resultado de una
lookup()
síncrona y los resultados de una consulta para poder renderizar una respuesta. Si ellookup()
y la consulta no tienen una dependencia de datos, no es necesario esperar de forma síncrona hasta que se complete ellookup()
antes de iniciar la consulta.
Entidades
No incluyas la misma entidad (por clave) varias veces en la misma confirmación. Incluir la misma entidad varias veces en la misma confirmación puede afectar a la latencia.
Consulta la sección sobre actualizaciones de una entidad.
Claves
- Los nombres de las claves se generan automáticamente si no se proporcionan al crear la entidad. Se asignan de forma que se distribuyan uniformemente en el espacio de claves.
- En el caso de las claves que usan un nombre personalizado, siempre debes usar caracteres UTF-8, excepto la barra diagonal (
/
). Los caracteres que no sean UTF-8 interfieren en varios procesos, como la importación de un archivo de exportación del modo Datastore en BigQuery. Una barra inclinada podría interferir con funciones futuras. - Si la llave usa un ID numérico, haz lo siguiente:
- No uses un número negativo para el ID. Un ID negativo podría interferir en la ordenación.
- No utilice el valor
0
(cero) para el ID. Si lo haces, se te asignará un ID automáticamente. - Si quieres asignar manualmente tus propios IDs numéricos a las entidades que crees, haz que tu aplicación obtenga un bloque de IDs con el método
allocateIds()
. De esta forma, se evitará que el modo Datastore asigne uno de tus IDs numéricos manuales a otra entidad.
Si asignas tu propio ID numérico manual o nombre personalizado a las entidades que creas, no utilices valores que aumenten de forma monótona, como los siguientes:
1, 2, 3, …, "Customer1", "Customer2", "Customer3", …. "Product 1", "Product 2", "Product 3", ….
Si una aplicación genera un gran volumen de tráfico, esta numeración secuencial podría provocar puntos de acceso que afecten a la latencia del modo Datastore. Para evitar el problema de los IDs numéricos secuenciales, obtén IDs numéricos del método
allocateIds()
. El métodoallocateIds()
genera secuencias bien distribuidas de IDs numéricos.Si especifica una clave o almacena el nombre generado, podrá realizar una
lookup()
en esa entidad más adelante sin tener que enviar una consulta para encontrarla.
Índices
- Si una propiedad nunca se va a necesitar para una consulta, exclúyala de los índices. Si indexas una propiedad innecesariamente, puede aumentar la latencia y los costes de almacenamiento de las entradas de índice.
- Evita tener demasiados índices compuestos. El uso excesivo de índices compuestos puede provocar un aumento de la latencia de escritura y de los costes de almacenamiento de las entradas de índice. Si necesitas ejecutar consultas ad hoc en conjuntos de datos de gran tamaño sin índices definidos previamente, usa BigQuery.
- No indexe propiedades con valores que aumenten de forma monótona (como una marca de tiempo
NOW()
). Mantener un índice de este tipo puede provocar puntos de acceso que afecten a la latencia del modo Datastore en aplicaciones con altas tasas de lectura y escritura. Para obtener más información sobre cómo gestionar las propiedades monotónicas, consulta la sección Tasas de lectura y escritura altas para un intervalo de claves reducido, que aparece más abajo.
Propiedades
- Usa siempre caracteres UTF-8 en las propiedades de tipo string. Si hay un carácter que no es UTF-8 en una propiedad de tipo cadena, podría interferir en las consultas. Si necesitas guardar datos con caracteres que no sean UTF-8, utiliza una cadena de bytes.
- No uses puntos en los nombres de las propiedades. Los puntos en los nombres de las propiedades interfieren con la indexación de las propiedades de entidades insertadas.
Consultas
- Si solo necesitas acceder a la clave de los resultados de la consulta, usa una consulta para obtener solo las claves. Una consulta de solo claves devuelve resultados con una latencia y un coste menores que si se recuperaran entidades completas.
- Si solo necesitas acceder a propiedades específicas de una entidad, utiliza una consulta de proyección. Una consulta de proyección devuelve resultados con una latencia y un coste menores que la recuperación de entidades completas.
- Del mismo modo, si solo necesita acceder a las propiedades que se incluyen en el filtro de consulta (por ejemplo, las que se indican en una cláusula
order by
), utilice una consulta de proyección. - No uses desplazamientos. En su lugar, usa cursores. Si solo usas un desplazamiento, se evitará que se devuelvan las entidades omitidas a tu aplicación, pero estas entidades se seguirán obteniendo internamente. Las entidades omitidas afectan a la latencia de la consulta y se te factura por las operaciones de lectura necesarias para recuperarlas.
Diseño para la escalabilidad
En las siguientes prácticas recomendadas se describe cómo evitar situaciones que provoquen problemas de contención.
Actualizaciones de una entidad
Cuando diseñes tu aplicación, ten en cuenta la rapidez con la que actualiza las entidades individuales. La mejor forma de caracterizar el rendimiento de tu carga de trabajo es realizar pruebas de carga. La frecuencia máxima exacta con la que una aplicación puede actualizar una sola entidad depende en gran medida de la carga de trabajo. Entre los factores se incluyen la tasa de escritura, la contención entre solicitudes y el número de índices afectados.
Una operación de escritura de entidades actualiza la entidad y los índices asociados, y Firestore en el modo Datastore aplica de forma síncrona la operación de escritura en un quórum de réplicas. Si las tasas de escritura son lo suficientemente altas, la base de datos empezará a tener problemas de contención, latencia más alta u otros errores.
Tasas de lectura/escritura altas en un intervalo de claves reducido
Evita tasas de lectura o escritura altas en documentos cercanos lexicográficamente, ya que tu aplicación experimentará errores de contención. Este problema se conoce como "hotspotting" y tu aplicación puede sufrirlo si hace alguna de las siguientes acciones:
Crea entidades a un ritmo muy alto y asigna sus propios IDs, que aumentan de forma monótona.
El modo Datastore asigna claves mediante un algoritmo de dispersión. No deberías tener problemas de puntos calientes en las escrituras si creas entidades con la asignación automática de IDs de entidad.
Crea entidades a un ritmo muy alto mediante la política de asignación de ID secuencial antiguo.
Crea entidades nuevas a un ritmo alto para un tipo con pocas entidades.
Crea entidades con una propiedad indexada y de valor creciente de forma monótona, como una marca de tiempo, a un ritmo muy alto.
Elimina entidades de un tipo a una velocidad alta.
Escribe en la base de datos a un ritmo muy alto sin aumentar el tráfico gradualmente.
Si se produce un aumento repentino de la tasa de escritura en un intervalo pequeño de claves, las escrituras pueden ser lentas debido a un punto de acceso. El modo Datastore acabará dividiendo el espacio de claves para admitir cargas elevadas.
El límite de lecturas suele ser mucho más alto que el de escrituras, a menos que leas de una sola clave a un ritmo elevado.
Los puntos calientes se pueden aplicar a los intervalos de claves que usan tanto las claves de entidad como los índices.
En algunos casos, un punto de acceso puede tener un impacto mayor en una aplicación que impedir lecturas o escrituras en un intervalo pequeño de claves. Por ejemplo, las teclas de acceso rápido se pueden leer o escribir durante el inicio de la instancia, lo que provoca que las solicitudes de carga fallen.
Si tienes una propiedad de clave o indexada que aumentará de forma monótona, puedes añadir un hash aleatorio para asegurarte de que las claves se fragmenten en varias tablets.
Del mismo modo, si necesitas consultar una propiedad que aumenta (o disminuye) de forma monótona mediante una ordenación o un filtro, puedes indexar una propiedad nueva. Para ello, añade al valor monótono un prefijo con un valor que tenga una cardinalidad alta en el conjunto de datos, pero que sea común a todas las entidades del ámbito de la consulta que quieras realizar. Por ejemplo, si quiere consultar entradas por marca de tiempo, pero solo necesita devolver resultados de un usuario a la vez, puede prefijar la marca de tiempo con el ID de usuario e indexar esa nueva propiedad. De esta forma, se seguirían permitiendo las consultas y los resultados ordenados para ese usuario, pero la presencia del ID de usuario aseguraría que el índice esté bien fragmentado.
Aumentar el tráfico
Aumenta gradualmente el tráfico a nuevos tipos o partes del espacio de claves.
Debe aumentar el tráfico a los nuevos tipos de forma gradual para que Firestore en modo Datastore tenga tiempo suficiente para prepararse para el aumento del tráfico. Recomendamos un máximo de 500 operaciones por segundo para un nuevo tipo y, a continuación, aumentar el tráfico en un 50% cada 5 minutos. En teoría, puedes llegar a 740.000 operaciones por segundo después de 90 minutos con esta programación de aumento. Asegúrate de que las escrituras se distribuyan de forma relativamente uniforme en todo el intervalo de claves. Nuestros ingenieros de fiabilidad de sitios web la denominan "regla 500/50/5".
Este patrón de aumento gradual es especialmente importante si cambias el código para dejar de usar el tipo A y empezar a usar el tipo B. Una forma sencilla de gestionar esta migración es cambiar el código para que lea el tipo B y, si no existe, que lea el tipo A. Sin embargo, esto podría provocar un aumento repentino del tráfico a un nuevo tipo con una parte muy pequeña del espacio de claves.
El mismo problema puede producirse si migras tus entidades para que usen otro intervalo de claves del mismo tipo.
La estrategia que uses para migrar entidades a un nuevo tipo o clave dependerá de tu modelo de datos. A continuación, se muestra un ejemplo de estrategia, conocida como "Lecturas paralelas". Deberá determinar si esta estrategia es eficaz para sus datos. Un aspecto importante que se debe tener en cuenta es el impacto económico de las operaciones paralelas durante la migración.
Lee primero la entidad o la clave antiguas. Si no está, puedes leer la nueva entidad o clave. Una tasa alta de lecturas de entidades inexistentes puede provocar un exceso de actividad, por lo que debes asegurarte de aumentar la carga gradualmente. Una estrategia mejor es copiar la entidad antigua en la nueva y, a continuación, eliminar la antigua. Aumenta las lecturas paralelas de forma gradual para asegurarte de que el nuevo espacio de claves esté bien dividido.
Una posible estrategia para aumentar gradualmente las lecturas o escrituras en un tipo nuevo es usar un hash determinista del ID de usuario para obtener un porcentaje aleatorio de usuarios que escriban entidades nuevas. Asegúrate de que el resultado del hash del ID de usuario no se vea afectado por tu función aleatoria ni por el comportamiento del usuario.
Mientras tanto, ejecuta un trabajo de Dataflow para copiar todos tus datos de las entidades o claves antiguas a las nuevas. Tu trabajo por lotes debe evitar las escrituras en claves secuenciales para evitar los puntos de acceso. Cuando se complete el trabajo por lotes, solo podrás leer desde la nueva ubicación.
Una mejora de esta estrategia es migrar pequeños grupos de usuarios a la vez. Añade un campo a la entidad de usuario que registre el estado de migración de ese usuario. Selecciona un lote de usuarios para migrar en función de un hash del ID de usuario. Una tarea de MapReduce o Dataflow migrará las claves de ese lote de usuarios. Los usuarios que tengan una migración en curso usarán lecturas paralelas.
Ten en cuenta que no podrás revertir fácilmente los cambios a menos que hagas escrituras duales de las entidades antiguas y nuevas durante la fase de migración. Esto aumentaría los costes del modo Datastore.
Eliminaciones
Evita eliminar un gran número de entidades en un intervalo reducido de claves.
Firestore en el modo de Datastore reescribe periódicamente sus tablas para eliminar las entradas eliminadas y reorganizar los datos de forma que las lecturas y las escrituras sean más eficientes. Este proceso se conoce como compactación.
Si eliminas un gran número de entidades del modo Datastore en un intervalo pequeño de claves, las consultas en esta parte del índice serán más lentas hasta que se complete la compactación. En casos extremos, es posible que tus consultas agoten el tiempo de espera antes de devolver resultados.
No es recomendable usar un valor de marca de tiempo para un campo indexado con el fin de representar la hora de vencimiento de una entidad. Para recuperar las entidades caducadas, debe consultar este campo indexado, que probablemente se encuentre en una parte superpuesta del espacio de claves con las entradas de índice de las entidades eliminadas más recientemente.
Puedes mejorar el rendimiento con las "consultas fragmentadas", que anteponen una cadena de longitud fija a la marca de tiempo de vencimiento. El índice se ordena por la cadena completa, de forma que las entidades con la misma marca de tiempo se encuentren en todo el intervalo de claves del índice. Ejecutas varias consultas en paralelo para obtener resultados de cada fragmento.
Una solución más completa para el problema de la marca de tiempo de vencimiento es usar un "número de generación", que es un contador global que se actualiza periódicamente. El número de generación se añade antes de la marca de tiempo de vencimiento para que las consultas se ordenen por número de generación, luego por fragmento y, por último, por marca de tiempo. La eliminación de entidades antiguas se produce en una generación anterior. El número de generación de las entidades que no se hayan eliminado debe incrementarse. Una vez que se haya completado la eliminación, podrás pasar a la siguiente generación. Las consultas en una generación anterior tendrán un rendimiento bajo hasta que se complete la compactación. Puede que tengas que esperar a que se completen varias generaciones antes de consultar el índice para obtener la lista de entidades que se van a eliminar. De esta forma, se reduce el riesgo de que falten resultados debido a la coherencia final.
Fragmentación y replicación
Usa la fragmentación o la replicación para gestionar los puntos de acceso.
Puedes usar la replicación si necesitas leer una parte del intervalo de claves a una velocidad superior a la que permite Firestore en el modo de Datastore. Con esta estrategia, almacenarías N copias de la misma entidad, lo que permitiría una tasa de lecturas N veces superior a la que admite una sola entidad.
Puedes usar el particionamiento si necesitas escribir en una parte del intervalo de claves a una velocidad superior a la que permite Firestore en el modo Datastore. La fragmentación divide una entidad en partes más pequeñas.
Algunos errores habituales al fragmentar son los siguientes:
Partición mediante un prefijo de tiempo. Cuando se pasa al siguiente prefijo, la nueva parte sin dividir se convierte en un punto de acceso. En su lugar, deberías migrar gradualmente una parte de tus escrituras al nuevo prefijo.
Particionando solo las entidades más activas. Si fragmentas una pequeña proporción del número total de entidades, es posible que no haya suficientes filas entre las entidades activas para asegurarte de que permanezcan en divisiones diferentes.
Siguientes pasos
- Consulta los límites de Firestore en modo Datastore.
- Consulta las prácticas recomendadas para empresas.