Comprende las operaciones de lectura y escritura a gran escala

Lee este documento para tomar decisiones fundamentadas sobre la arquitectura de tus aplicaciones para lograr un alto rendimiento y una alta confiabilidad. En este documento, se incluyen temas avanzados de Firestore. Si estás comenzando a usar Firestore, consulta la guía de inicio rápido.

Para garantizar que tus aplicaciones continúen funcionando bien a medida que el tamaño y el tráfico de tu base de datos aumentan, es útil comprender la mecánica de las operaciones de lectura y escritura en el backend de Firestore. También debes comprender la interacción entre tus operaciones de lectura y escritura con la capa de almacenamiento y las restricciones subyacentes que pueden afectar el rendimiento.

Consulta las siguientes secciones para conocer las prácticas recomendadas antes de diseñar tu aplicación.

Comprende los componentes de alto nivel

En el siguiente diagrama, se muestran los componentes de alto nivel que participan en una solicitud a la API de Firestore.

Componentes de alto nivel

SDK, bibliotecas cliente y controladores

Firestore admite SDK, bibliotecas cliente y controladores para diferentes plataformas.

Google Front End (GFE)

Este es un servicio de infraestructura común a todos los servicios de Google Cloud . GFE acepta solicitudes entrantes y las reenvía al servicio de Google relevante (en este contexto, el servicio de Firestore).

Servicio de Firestore

El servicio de Firestore realiza verificaciones, como de autenticación, autorización, verificaciones de cuota y reglas de seguridad, en la solicitud a la API, y también administra transacciones. Este servicio incluye un cliente de almacenamiento que interactúa con la capa que almacena las operaciones de lectura y escritura de datos.

Capa de almacenamiento de Firestore

La capa de almacenamiento de Firestore se encarga de almacenar los datos y metadatos, así como las funciones asociadas de bases de datos que proporciona Firestore. En las siguientes secciones, se describe cómo se organizan los datos en la capa de almacenamiento de Firestore y cómo escala el sistema. Aprender cómo están organizados los datos puede ayudarte a diseñar un modelo escalable y a comprender mejor las prácticas recomendadas en Firestore.

Rangos y divisiones clave

Firestore es una base de datos NoSQL orientada a documentos. Almacenas los datos en documentos que se organizan en colecciones. El nombre de la colección y el ID del documento forman una clave única para un documento. Los documentos de la misma colección se almacenan juntos en el espacio de claves. Dentro de ese espacio de claves, el ID del documento se hash. El término rango de claves hace referencia a un rango contiguo de claves en el almacenamiento.

Firestore particiona automáticamente los datos dentro de las colecciones en varios servidores de almacenamiento. Estas particiones se denominan divisiones.

Los documentos pueden generar entradas de índice que están ordenadas de forma lexicográfica y participan en el mismo tipo de división y ubicación que los datos del documento.

Replicación síncrona

Cada operación de escritura se replica de forma síncrona en la mayoría de las réplicas con Paxos. Una réplica por división se considera líder y coordina el proceso de replicación. En el caso de que falle el líder, se elige uno nuevo. Las réplicas se ubican en diferentes zonas para ser resilientes a posibles fallas de zona. El resultado general es un sistema escalable y con alta disponibilidad que proporciona latencias bajas para las operaciones de lectura y escritura a gran escala, independientemente de las cargas de trabajo pesadas.

Región única o multirregión

Cuando creas una base de datos, debes seleccionar una región o multirregión.

Una ubicación regional única es una ubicación geográfica específica, como us-west1. Las divisiones de datos de una base de datos de Firestore tienen réplicas en diferentes zonas dentro de la región seleccionada, como se explicó anteriormente.

Una ubicación multirregional consiste en un conjunto definido de regiones en las que se almacenan réplicas de la base de datos. En una implementación multirregional de Firestore, dos de las regiones tienen réplicas completas de todos los datos de la base de datos. Una tercera región tiene una réplica testigo que no mantiene un conjunto completo de datos, pero participa en la replicación. Cuando se replican los datos entre múltiples regiones, estos se pueden seguir escribiendo y leyendo, incluso con la pérdida de toda una región.

Para obtener más información sobre las ubicaciones de una región, consulta Ubicaciones de Firestore.

Región única frente a multirregión

Comprende el ciclo de vida de una operación de escritura

Para escribir datos, un controlador puede crear, actualizar o borrar un documento. Una operación de escritura en un solo documento requiere de la actualización atómica del documento y sus entradas de índice asociadas en la capa de almacenamiento. Firestore también admite operaciones atómicas que constan de varias operaciones de lectura o escritura en uno o más documentos.

Para todos los tipos de operaciones de escritura, Firestore proporciona las propiedades ACID (atomicidad, coherencia, aislamiento y durabilidad) de las bases de datos relacionales. Firestore también proporciona serialización, lo que significa que todas las transacciones aparecen como si se ejecutaran en serie.

Pasos de alto nivel en una transacción de escritura

Cuando el controlador envía una operación de escritura o confirma una transacción, a través de cualquiera de los métodos mencionados, esta se ejecuta de forma interna como una transacción de operaciones de lectura y escritura de la base de datos en la capa de almacenamiento. La transacción permite que Firestore proporcione las propiedades ACID mencionadas anteriormente.

En el primer paso de una transacción, Firestore lee el documento existente y determina las mutaciones que se deben realizar a los datos en el documento.

Esto también incluye realizar actualizaciones en los índices relevantes:

  • Los campos indexados que se agregan a los documentos necesitan las inserciones correspondientes en los índices.
  • Los campos indexados que se quitan de los documentos necesitan las eliminaciones correspondientes en los índices.
  • Los campos indexados que se modifican en los documentos necesitan eliminaciones (para valores anteriores) y, además, inserciones (para valores nuevos) en los índices.

Para calcular las mutaciones mencionadas antes, Firestore lee la configuración de indexación del proyecto. Esta configuración almacena información sobre los índices de un proyecto.

Una vez que se calculan las mutaciones, Firestore las recopila dentro de una transacción y, luego, las confirma.

Comprende una transacción de escritura en la capa de almacenamiento

Como se mencionó antes, una operación de escritura en Firestore implica una transacción de operaciones de lectura y escritura en la capa de almacenamiento. Según el diseño de los datos, una operación de escritura puede implicar una o más divisiones.

En el siguiente diagrama, la base de datos de Firestore tiene ocho divisiones (marcadas del 1 al 8) alojadas en tres servidores de almacenamiento diferentes dentro de una sola zona y cada una se replica en 3 o más zonas. ada división se replica en 3 o más zonas y tiene un líder de Paxos, que puede estar en una zona diferente para distintas divisiones.

División de la base de datos de Firestore

Considera una base de datos de Firestore que tenga la colección Restaurants de la siguiente manera:

Colección de restaurantes

El controlador solicita el siguiente cambio para un documento de la colección Restaurant mediante la actualización del valor del campo priceCategory.

Cambio para un documento de una colección

Los siguientes pasos de alto nivel describen lo que sucede como parte de la operación de escritura:

  1. Crea una transacción de lectura y escritura.
  2. Lee el documento restaurant1 de la colección Restaurants.
  3. Lee los índices del documento.
  4. Calcula las mutaciones que se realizarán en los datos. En este caso, hay cinco mutaciones:
    • M1: Actualiza la fila de restaurant1 para reflejar el cambio en el valor del campo priceCategory.
    • M2 y M3: Borran las entradas de índice anteriores de priceCategory.
    • M4 y M5: Agregan nuevas entradas de índice para priceCategory.
  5. Confirma estas mutaciones.

El cliente de almacenamiento en el servicio de Firestore busca las divisiones que poseen las claves de las filas que se cambiarán. Consideremos un caso en el que la División 3 entrega la M1 y la División 6 entrega de la M2 a la M5. De esta forma, se genera una transacción distribuida que incluye todas estas divisiones como participantes. Las divisiones de los participantes también pueden incluir cualquier otra división de la que se hayan leído datos anteriormente como parte de la transacción de lectura y escritura.

En los siguientes pasos, se describe lo que sucede como parte de la confirmación:

  1. El cliente de almacenamiento emite una confirmación. La confirmación contiene mutaciones de la M1 a la M5.
  2. Las divisiones 3 y 6 son los participantes de esta transacción. Se elige a uno de los participantes como el coordinador, como en la División 3. El trabajo del coordinador consiste en asegurarse de que la transacción se confirme o se anule de forma automática en todos los participantes.
    • Las réplicas líderes de estas divisiones son responsables del trabajo que realizan los participantes y coordinadores.
  3. Cada participante y coordinador ejecuta un algoritmo Paxos con sus respectivas réplicas.
    • El líder ejecuta un algoritmo Paxos con las réplicas. El quórum se logra si la mayoría de las réplicas responden con una respuesta ok to commit al líder.
    • Luego, cada participante notifica al coordinador cuando esté preparado (primera fase de la confirmación en dos fases). Si algún participante no puede confirmar la transacción, se anula toda la transacción (aborts).
  4. Una vez que el coordinador conoce a todos los participantes, incluido él mismo, está preparado para comunicar el resultado de la transacción de accept a todos los participantes (segunda fase de la confirmación en dos fases). En esta fase, cada participante registra la decisión de confirmación en un almacenamiento estable para confirmarla.
  5. El coordinador responde al cliente de almacenamiento en Firestore que se confirmó la transacción. Al mismo tiempo, el coordinador y todos los participantes aplican las mutaciones a los datos.

Confirmación del ciclo de vida

Cuando la base de datos de Firestore es pequeña, puede suceder que una sola división posea todas las claves de las mutaciones M1 a la M5. En ese caso, solo hay un participante en la transacción y no se requiere la confirmación en dos fases mencionada anteriormente, por lo que las operaciones de escritura se agilizan.

Operaciones de escritura multirregionales

En una implementación multirregional, la distribución de réplicas entre regiones aumenta la disponibilidad, aunque esto conlleva un costo de rendimiento. La comunicación entre réplicas en diferentes regiones ralentiza más los tiempos de ida y vuelta. Por lo tanto, la latencia del modelo de referencia para las operaciones de Firestore es ligeramente mayor en comparación con las implementaciones de una sola región.

Configuramos las réplicas de modo que el liderazgo de las divisiones siempre permanezca en la región principal. La región principal es la que recibe tráfico del servidor de Firestore. Esta decisión de liderazgo reduce el retraso de ida y vuelta en la comunicación entre el cliente de almacenamiento en Firestore y el líder de réplica (o coordinador de las transacciones de división múltiple).

Comprende el ciclo de vida de una operación de lectura

En esta sección, se profundiza en las operaciones de lectura de Firestore. Las consultas, en particular, consisten en una combinación de operaciones de lectura de documentos y de entradas de índice.

Las operaciones de lectura de datos de la capa de almacenamiento se realizan internamente mediante una transacción de base de datos para garantizar lecturas coherentes. Sin embargo, a diferencia de las transacciones que se usan para las operaciones de escritura, estas no aceptan bloqueos. En su lugar, funcionan eligiendo una marca de tiempo y, luego, ejecutando todas las lecturas en esa marca de tiempo. Dado que no adquieren bloqueos, no bloquean las transacciones simultáneas de lectura y escritura. Para ejecutar esta transacción, el cliente de almacenamiento en Firestore especifica un límite de marca de tiempo que le indica a la capa de almacenamiento cómo elegir una marca de tiempo de lectura. El tipo de límite de marca de tiempo que elige el cliente de almacenamiento en Firestore se determina mediante las opciones de lectura para la solicitud de lectura.

Comprende una transacción de lectura en la capa de almacenamiento

En esta sección, se describen los tipos de operaciones de lectura y cómo se procesan en la capa de almacenamiento de Firestore.

Operaciones de lectura sólidas

De forma predeterminada, las operaciones de lectura de Firestore tienen una coherencia sólida. Esto significa que una operación de lectura de Firestore muestra la versión más reciente de los datos que reflejan todas las operaciones de escritura confirmadas antes del inicio de la operación de lectura.

Operaciones de lectura única divididas

El cliente de almacenamiento en Firestore busca las divisiones que poseen las claves de las filas que se leerán. Supongamos que necesita realizar una operación de lectura de la División 3 de la sección anterior. El cliente envía la solicitud de lectura a la réplica más cercana para reducir la latencia de ida y vuelta.

En este punto, pueden ocurrir los siguientes casos según la réplica elegida:

  • La solicitud de lectura se dirige a una réplica líder (Zona A).
    • Dado que el líder está siempre actualizado, la operación de lectura puede continuar directamente.
  • La solicitud de lectura se dirige a una réplica no líder (como la zona B).
    • Debido al estado interno de la división 3, se puede saber que contiene suficiente información para entregar la operación de lectura, y así lo hace.
    • La División 3 no está segura de haber visto los datos más recientes. Le envía un mensaje al líder solicitando la marca de tiempo de la última transacción que debe aplicar para entregar la lectura. Una vez que se aplica la transacción, la operación de lectura puede continuar.

Luego, Firestore muestra la respuesta a su cliente.

Operaciones de lectura de división múltiple

En caso de que las operaciones de lectura se deban realizar desde varias divisiones, se produce el mismo mecanismo en todas ellas. Una vez que se muestran los datos de todas las divisiones, el cliente de almacenamiento en Firestore combina los resultados. Luego, Firestore responde a su cliente con estos datos.

Evita los hotspots

Las divisiones en Firestore se separan automáticamente en partes más pequeñas de modo que puedan distribuir el trabajo de entregar tráfico a más servidores de almacenamiento cuando sea necesario o cuando se expande el espacio de claves. Las divisiones creadas para manejar el exceso de tráfico se retienen durante aproximadamente 24 horas, incluso si el tráfico desaparece. Por lo tanto, si hay aumentos repentinos de tráfico recurrentes, se mantienen las divisiones y se agregan más divisiones siempre que sea necesario. Estos mecanismos ayudan a las bases de datos de Firestore a escalar automáticamente cuando se aumenta la carga de tráfico o el tamaño de la base de datos. Sin embargo, existen algunas limitaciones que debes tener en cuenta.

Dividir el almacenamiento y la carga lleva tiempo, y aumentar el tráfico demasiado rápido puede causar errores de latencia alta o errores de vencimiento excedido, lo que comúnmente se conoce como hotspots, mientras se ajusta el servicio. La práctica recomendada es distribuir las operaciones dentro del rango de claves, mientras se aumenta el tráfico gradualmente en una colección de una base de datos.

Aunque las divisiones se crean automáticamente a medida que crece la carga, Firestore puede dividir un rango de claves solo hasta que entregue un solo documento con un conjunto exclusivo de servidores de almacenamiento replicado. Como resultado, los volúmenes altos y constantes de las operaciones simultáneas en un solo documento pueden llevar a un hotspot en ese documento. Si encuentras latencias altas constantes en un documento, puedes modificar el modelo de datos para dividir o replicar los datos en múltiples documentos.

Los errores de contención se producen cuando múltiples operaciones intentan realizar operaciones de lectura o escritura en el mismo documento de forma simultánea.

Ten en cuenta que, si sigues las prácticas que se describen en esta página, Firestore se puede escalar para entregar cargas de trabajo arbitrariamente grandes sin que tengas que ajustar ninguna configuración.