Optimiza índices

En esta página, se describen los conceptos que debes tener en cuenta a la hora de seleccionar índices de Firestore en modo Datastore para tu app.

Firestore en modo Datastore ofrece un alto rendimiento de consultas mediante el uso de índices para todas las consultas. El rendimiento de la mayoría de las consultas depende del tamaño del conjunto de resultados y no del tamaño total de la base de datos.

Firestore en modo Datastore define los índices integrados para cada propiedad de una entidad. Estos índices de propiedad única admiten muchas consultas simples. Firestore en modo Datastore admite una función de combinación de índices que permite que tu base de datos combine índices integrados para admitir consultas adicionales. Para consultas más complejas, debes definir índices compuestos con anticipación.

Esta página se centra en la función de combinación de índices, ya que afecta dos oportunidades importantes de optimización de índices:

  • Aceleración de las consultas
  • Reducir la cantidad de índices compuestos

En el siguiente ejemplo, se muestra la función de combinación de índices.

Filtra entidades Photo

Considera una base de datos de modo Datastore con entidades del tipo Photo:

Foto
Propiedad Tipo de valor Descripción
owner_id String ID de usuario
tag Arreglo de strings Palabras clave con asignación de token
size Número entero Enumeración:
  • 1 icon
  • 2 medium
  • 3 large
coloration Número entero Enumeración:
  • 1 black & white
  • 2 color

Imagina que necesitas una función de app que permita a los usuarios consultar entidades Photo según una lógica AND de lo siguiente:

  • Hasta tres filtros basados en las propiedades:

    • owner_id
    • size
    • coloration
  • Una string de búsqueda de tag. La aplicación asigna un token a la string de búsqueda con etiquetas y agrega un filtro para cada etiqueta.

    Por ejemplo, la app convierte la string de búsqueda outside, family en los filtros de consulta tag=outside y tag=family.

Puedes cumplir con los requisitos de índice de esta función de filtro Photo sin agregar índices compuestos adicionales mediante los índices integrados y la función de combinación de índices de Firestore en modo Datastore.

Los índices integrados para las entidades Photo admiten consultas de filtro único, como las siguientes:

Python

from google.cloud import datastore

# For help authenticating your client, visit
# https://cloud.google.com/docs/authentication/getting-started
client = datastore.Client()

query_owner_id = client.query(kind="Photo", filters=[("owner_id", "=", "user1234")])

query_size = client.query(kind="Photo", filters=[("size", "=", 2)])

query_coloration = client.query(kind="Photo", filters=[("coloration", "=", 2)])

La función de filtro Photo también requiere consultas que combinen varios filtros de igualdad con un AND lógico:

Python

from google.cloud import datastore

# For help authenticating your client, visit
# https://cloud.google.com/docs/authentication/getting-started
client = datastore.Client()

query_all_properties = client.query(
    kind="Photo",
    filters=[
        ("owner_id", "=", "user1234"),
        ("size", "=", 2),
        ("coloration", "=", 2),
        ("tag", "=", "family"),
    ],
)

Firestore en modo Datastore puede admitir estas consultas mediante la combinación de índices integrados.

Combinación de índices

Firestore en modo Datastore puede usar la combinación de índices cuando la consulta y los índices cumplan con todas las siguientes restricciones:

  • La consulta solo usa filtros de igualdad (=).
  • No existe un índice compuesto que coincida a la perfección con los filtros y el orden de la consulta.
  • Cada filtro de igualdad coincide con al menos un índice existente con el mismo orden que la consulta.

En esta situación, Firestore en modo Datastore puede usar índices existentes para admitir la consulta en lugar de requerir que configures un índice compuesto adicional.

Cuando se ordenan dos o más índices según los mismos criterios, Firestore en modo Datastore puede combinar los resultados de varios análisis de índices para encontrar los resultados que son comunes a todos esos índices. Firestore en modo Datastore puede combinar los índices integrados, ya que todos ordenan los valores por clave de entidad.

Mediante la combinación de los índices integrados, Firestore en modo Datastore admite consultas con filtros de igualdad en varias propiedades:

Python

from google.cloud import datastore

# For help authenticating your client, visit
# https://cloud.google.com/docs/authentication/getting-started
client = datastore.Client()

query_all_properties = client.query(
    kind="Photo",
    filters=[
        ("owner_id", "=", "user1234"),
        ("size", "=", 2),
        ("coloration", "=", 2),
        ("tag", "=", "family"),
    ],
)

Firestore en modo Datastore también puede combinar los resultados de índice de varias secciones del mismo índice. Mediante la combinación de diferentes secciones del índice integrado para la propiedad tag, Firestore en modo Datastore admite consultas que combinan varios filtros tag en un AND lógico:

Python

from google.cloud import datastore

# For help authenticating your client, visit
# https://cloud.google.com/docs/authentication/getting-started
client = datastore.Client()

query_tag = client.query(
    kind="Photo",
    filters=[
        ("tag", "=", "family"),
        ("tag", "=", "outside"),
        ("tag", "=", "camping"),
    ],
)

query_owner_size_color_tags = client.query(
    kind="Photo",
    filters=[
        ("owner_id", "=", "user1234"),
        ("size", "=", 2),
        ("coloration", "=", 2),
        ("tag", "=", "family"),
        ("tag", "=", "outside"),
        ("tag", "=", "camping"),
    ],
)

Las consultas admitidas por los índices integrados combinados completan el conjunto de consultas que requiere la función de filtro Photo. Ten en cuenta que, para admitir la función de filtro Photo, no se necesitaban índices compuestos adicionales.

Cuando selecciones los índices óptimos para tu app, es importante que comprendas la función de combinación de índices. La combinación de índices le brinda a Firestore en modo Datastore una mayor flexibilidad de consulta, pero podría afectar el rendimiento. En la siguiente sección, se describe el rendimiento de la combinación de índices y cómo mejorarlo mediante la incorporación de índices compuestos.

Encuentra el índice perfecto

El índice se ordena primero por principal y, luego, por valores de propiedad, en el orden especificado en la definición del índice. El índice compuesto perfecto de una consulta, que permite que esta se ejecute de la manera más eficiente, se define según las siguientes propiedades y en este orden:

  1. Propiedades usadas en filtros de igualdad
  2. Propiedades usadas en órdenes de clasificación
  3. Propiedades usadas en el filtro distinctOn
  4. Propiedades usadas en el rango y filtros de desigualdad (que aún no están incluidos en órdenes de clasificación)
  5. Propiedades usadas en agregaciones y proyecciones (que aún no se incluyen en los órdenes de clasificación ni en los filtros de rango y desigualdad)

Esto garantiza que se consideren todos los resultados para cada ejecución posible de la consulta. Las bases de datos de Firestore en modo Datastore ejecutan una consulta con un índice perfecto mediante los siguientes pasos:

  1. Identifica el índice correspondiente a la categoría, las propiedades de filtro, los operadores de filtro y el ordenamiento de la consulta.
  2. Busca desde el principio del índice hasta la primera entidad que cumple con todas las condiciones de filtro de la consulta o un subconjunto de ellas.
  3. Continúa la búsqueda del índice y muestra cada entidad que cumpla con todas las condiciones del filtro hasta que cumpla con las siguientes condiciones:
    • Encuentra una entidad que no cumple con las condiciones del filtro.
    • Se alcanza el final del índice.
    • Se recopiló la cantidad máxima de resultados solicitados por la consulta.

Por ejemplo, considera la siguiente consulta:

SELECT * FROM Task
WHERE category = 'Personal'
  AND priority < 3
ORDER BY priority DESC

El índice compuesto perfecto para esta consulta es un índice de claves para entidades de tipo Task, con columnas para los valores de las propiedades category y priority. El índice se ordena primero en orden ascendente por category y, luego, en orden descendente por priority:

indexes:
- kind: Task
  properties:
  - name: category
    direction: asc
  - name: priority
    direction: desc

Dos consultas del mismo formato, pero con valores de filtro diferentes, usan el mismo índice. Por ejemplo, la siguiente consulta usa el mismo índice que la consulta anterior:

SELECT * FROM Task
WHERE category = 'Work'
  AND priority < 5
ORDER BY priority DESC

Considera este índice:

indexes:
- kind: Task
  properties:
  - name: category
    direction: asc
  - name: priority
    direction: asc
  - name: created
    direction: asc

El índice anterior puede satisfacer ambas consultas:

SELECT * FROM Task
WHERE category = 'Personal'
  AND priority = 5
ORDER BY created ASC

y

SELECT * FROM Task
WHERE category = 'Work'
ORDER BY priority ASC, created ASC

Optimiza la selección de índices

En esta sección, se describen las características de rendimiento de la combinación de índices y dos oportunidades de optimización relacionadas:

  • Agrega índices compuestos para agilizar las consultas que dependen de índices combinados.
  • Usa los índices combinados para reducir la cantidad de índices compuestos

Rendimiento de la combinación de índices

En una combinación de índices, Firestore en modo Datastore combina los índices de manera eficiente mediante un algoritmo de combinación en zigzag. Con este algoritmo, el modo Datastore une las posibles coincidencias de varios análisis de índices para producir un conjunto de resultados que coincida con una consulta. La combinación de índices combina los componentes del filtro en el momento de la lectura en lugar de la escritura. A diferencia de la mayoría de las consultas de Firestore en modo Datastore, en las que el rendimiento depende solo del tamaño del conjunto de resultados, el rendimiento de las consultas de combinación de índices depende de los filtros de la consulta y de la cantidad de coincidencias potenciales que considera la base de datos.

El mejor rendimiento posible de la combinación de índices ocurre cuando todas las coincidencias potenciales en un índice satisfacen los filtros de consulta. En este caso, el rendimiento es O(R * I). R es el tamaño del conjunto de resultados, mientras que I es el número de índices analizados.

El peor rendimiento posible ocurre cuando la base de datos debe considerar muchas coincidencias potenciales, pero pocas satisfacen los filtros de la consulta. En este caso, el rendimiento es O(S). S es el tamaño del conjunto más pequeño de entidades potenciales de un análisis de índices único.

El rendimiento real depende de la forma de los datos. El número promedio de entidades consideradas para cada resultado que se muestra es O(S/(R * I)). Las consultas tienen un peor rendimiento cuando muchas entidades coinciden con cada análisis de índice, pero pocas entidades coinciden con la consulta en general, lo que significa que R es pequeño y S es grande.

Hay cuatro factores que mitigan este riesgo:

  • El planificador de consultas no busca una entidad hasta que sabe que la entidad coincide con toda la consulta.

  • El algoritmo de zigzag no necesita encontrar todos los resultados para mostrar el siguiente resultado. Si solicitas los primeros 10 resultados, solo pagarás la latencia por encontrar esos 10 resultados.

  • El algoritmo de zigzag omite grandes secciones de resultados que son falsos positivos. El peor rendimiento posible ocurre solo si los resultados que son falsos positivos están bien entrelazados (en orden de clasificación) entre los análisis.

  • La latencia depende de la cantidad de entidades que se encuentran en cada análisis de índices, no de la cantidad de entidades que coinciden con cada filtro. Como se muestra en la siguiente sección, puedes agregar índices compuestos para mejorar el rendimiento de la combinación de índices.

Acelera una consulta de combinación de índices

Cuando Firestore en modo Datastore combina índices, cada análisis de índices a menudo se asigna a un solo filtro en la consulta. Para mejorar el rendimiento de las consultas, agrega índices compuestos que coincidan con varios filtros en la consulta.

Considera esta consulta:

Python

from google.cloud import datastore

# For help authenticating your client, visit
# https://cloud.google.com/docs/authentication/getting-started
client = datastore.Client()

query_owner_size_tag = client.query(
    kind="Photo",
    filters=[
        ("owner_id", "=", "username"),
        ("size", "=", 2),
        ("tag", "=", "family"),
    ],
)

Cada filtro se asigna a un análisis de índice en los siguientes índices integrados:

Index(Photo, owner_id)
Index(Photo, size)
Index(Photo, tag)

Si agregas el índice compuesto Index(Photo, owner_id, size), la consulta asigna dos análisis de índices en lugar de tres:

#  Satisfies both 'owner_id=username' and 'size=2'
Index(Photo, owner_id, size)
Index(Photo, tag)

Considera una situación con muchas imágenes grandes, muchas imágenes en blanco y negro, pero pocas imágenes panorámicas grandes. Un filtro de consulta para imágenes panorámicas y en blanco y negro será lento si combina índices integrados:

Python

from google.cloud import datastore

# For help authenticating your client, visit
# https://cloud.google.com/docs/authentication/getting-started
client = datastore.Client()

query_size_coloration = client.query(
    kind="Photo", filters=[("size", "=", 2), ("coloration", "=", 1)]
)

A fin de mejorar el rendimiento de las consultas, puedes reducir el valor de S (el conjunto más pequeño de entidades en un análisis de índices único) en O(S/(R * I)) si agregas el siguiente índice compuesto:

Index(Photo, size, coloration)

En comparación con el uso de dos índices integrados, este índice compuesto produce menos resultados potenciales para los mismos dos filtros de consulta. Este enfoque mejora de forma sustancial el rendimiento a costa de un índice más.

Reduce la cantidad de índices compuestos con la combinación de índices

Aunque los índices compuestos que tienen una concordancia exacta con los filtros de una consulta tienen un mejor rendimiento, no siempre es mejor o posible agregar un índice compuesto para cada combinación de filtros. Debes equilibrar tus índices compuestos con los siguientes elementos:

  • Límites de índices compuestos:

    Límite Importe
    Cantidad máxima de índices compuestos que se permiten para una base de datos
    Suma máxima de los tamaños de las entradas en el índice compuesto de una entidad 2 MiB
    Suma máxima de lo siguiente para una entidad:
    • la cantidad de valores de propiedad indexados
    • la cantidad de entradas en el índice compuesto
    20,000
  • Los costos de almacenamiento de cada índice adicional
  • Efectos sobre la latencia de escritura

Suelen surgir problemas de indexación con campos de varios valores, como la propiedad tag de las entidades Photo.

Por ejemplo, imagina que la función de filtro Photo ahora debe admitir cláusulas de orden descendente basadas en cuatro propiedades adicionales:

Foto
Propiedad Tipo de valor Descripción
date_added Número entero Fecha y hora
rating Número de punto flotante Calificación agregada de los usuarios
comment_count Número entero Cantidad de comentarios
download_count Número entero Cantidad de descargas

Si ignoras el campo tag, es posible seleccionar índices compuestos que coincidan con cada combinación de filtros Photo:

Index(Photo, owner_id, -date_added)
Index(Photo, owner_id, -comments)
Index(Photo, size, -date_added)
Index(Photo, size, -comments)
...
Index(Photo, owner_id, size, -date_added)
Index(Photo, owner_id, size, -comments)
...
Index(Photo, owner_id, size, coloration, -date_added)
Index(Photo, owner_id, size, coloration, -comments)

La cantidad total de índices compuestos es la siguiente:

2^(number of filters) * (number of different orders) = 2 ^ 3 * 4 = 32 composite indexes

Si intentas admitir hasta 3 filtros tag, la cantidad total de elementos compuestos los índices es:

2 ^ (3 + 3 tag filters) * 4 = 256 indexes.

Los índices que incluyen propiedades de varios valores, como tag, también generan problemas de índices con alto crecimiento, que aumentan los costos de almacenamiento y la latencia de escritura.

A fin de admitir filtros en el campo tag para esta función, puedes reducir la cantidad total de índices si usas índices combinados. El siguiente conjunto de índices compuestos es el mínimo necesario para admitir la función de filtro Photo con un orden:

Index(Photo, owner_id, -date_added)
Index(Photo, owner_id, -rating)
Index(Photo, owner_id, -comments)
Index(Photo, owner_id, -downloads)
Index(Photo, size, -date_added)
Index(Photo, size, -rating)
Index(Photo, size, -comments)
Index(Photo, size, -downloads)
...
Index(Photo, tag, -date_added)
Index(Photo, tag, -rating)
Index(Photo, tag, -comments)
Index(Photo, tag, -downloads)

La cantidad de índices compuestos definidos es la siguiente:

(number of filters + 1) * (number of orders) = 7 * 4 = 28

La combinación de índices también proporciona los siguientes beneficios:

  • Permite que una entidad Photo admita hasta 1,000 etiquetas sin límite en la cantidad de filtros tag por consulta.
  • Reduce la cantidad total de índices, lo que disminuye los costos de almacenamiento y la latencia de escritura.

Selecciona índices para tu app

Puedes seleccionar índices óptimos para tu base de datos en modo Datastore mediante dos enfoques:

  • La combinación de índices para admitir consultas adicionales tiene las siguientes características:

    • Requiere menos índices compuestos.
    • Reduce el costo de almacenamiento por entidad.
    • Mejora la latencia de escritura
    • Evita los índices con alto crecimiento.
    • El rendimiento depende de la forma de los datos.
  • Definir un índice compuesto que coincida con varios filtros en una consulta

    • Mejora el rendimiento de las consultas.
    • Genera un rendimiento de consultas coherente que no depende de la forma de los datos.
    • Deben mantenerse por debajo del límite de índices compuestos
    • Hay un mayor costo de almacenamiento por entidad.
    • Hay una mayor latencia de escritura

Cuando buscas los índices óptimos para la app, la respuesta puede cambiar a medida que lo hace la forma de los datos. El rendimiento de las consultas de muestra te brinda una idea acertada de las consultas comunes de la app y las consultas lentas. Con esta información, puedes agregar índices para mejorar el rendimiento de las consultas comunes y lentas.