Como otimizar índices

Nesta página, descrevemos conceitos a serem avaliados ao selecionar os índices do Firestore em modo Datastore para seu aplicativo.

O Firestore no modo Datastore oferece alto desempenho de consulta usando índices para todas as consultas. O desempenho da maioria das consultas depende do tamanho do conjunto de resultados e não do tamanho total do banco de dados.

O Firestore no modo Datastore define índices integrados para cada propriedade em uma entidade. Esses índices de propriedade única são compatíveis com muitas consultas simples. O Firestore no modo Datastore é compatível com um recurso de mesclagem de índices que permite que seu banco de dados mescle índices integrados para aceitar outras consultas. Para consultas mais complexas, defina os índices compostos com antecedência.

Nesta página, o foco é o recurso de mesclagem de índices, porque ele afeta duas importantes oportunidades de otimização de índice:

  • Como acelerar consultas
  • Como reduzir o número de índices compostos

O exemplo a seguir demonstra o recurso de mesclagem de índices.

Como filtrar entidades Photo

Considere um banco de dados no modo Datastore com entidades do tipo Photo:

Foto
Propriedade Tipo de valor Descrição
owner_id String ID do usuário
tag Matriz de strings Palavras-chave tokenizadas
size Inteiro Enumeração:
  • 1 icon
  • 2 medium
  • 3 large
coloration Inteiro Enumeração:
  • 1 black & white
  • 2 color

Imagine que você precisa de um recurso de aplicativo que permita aos usuários consultar entidades Photo com base em um AND lógico dos seguintes itens:

  • Até três filtros com base nas propriedades:

    • owner_id
    • size
    • coloration
  • Uma string de pesquisa tag. O aplicativo tokeniza a string de em tags e adiciona um filtro para cada tag.

    Por exemplo, o aplicativo transforma a string de pesquisa outside, family nos filtros de consulta tag=outside e tag=family.

Usando índices integrados e o recurso de mesclagem de índices do Firestore em modo Datastore, é possível atender aos requisitos de índice desse recurso de filtro Photo sem adicionar outros índices compostos.

Os índices integrados para entidades Photo são compatíveis com consultas de filtro único como:

Python

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)])

O recurso de filtro Photo também requer consultas que combinam vários filtros de igualdade com um AND lógico:

Python

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

O Firestore no modo Datastore pode aceitar essas consultas mesclando índices integrados.

Mesclagem de índices

O Firestore no modo Datastore pode usar a mesclagem de índices quando sua consulta e seus índices atendem a todas as seguintes restrições:

  • A consulta usa apenas filtros de igualdade (=).
  • Não há índice composto que corresponda perfeitamente aos filtros e à ordem da consulta.
  • Cada filtro de igualdade corresponde a pelo menos um índice atual com a mesma ordem da consulta.

Nessa situação, o Firestore no modo Datastore pode usar índices atuais para aceitar a consulta em vez de exigir que você configure outro índice composto.

Quando dois ou mais índices são classificados pelos mesmos critérios, o Firestore no modo Datastore pode mesclar os resultados de várias verificações de índice para encontrar os resultados comuns a todos esses índices. O Firestore no modo Datastore pode mesclar índices integrados, porque todos eles classificam os valores por chave de entidade.

Mesclando índices integrados, o Firestore no modo Datastore oferece suporte a consultas com filtros de igualdade em várias propriedades:

Python

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

O Firestore no modo Datastore também pode mesclar resultados de índice de várias seções do mesmo índice. Mesclando diferentes seções do índice integrado para a propriedade tag, o Firestore no modo Datastore oferece suporte a consultas que combinam filtros tag em AND lógico:

Python

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')])

As consultas compatíveis com índices integrados mesclados completam o conjunto de consultas exigido pelo recurso de filtragem Photo. O suporte ao recurso de filtragem Photo não exigiu outros índices compostos.

Ao selecionar os índices ideais para seu aplicativo, é importante entender o recurso de mesclagem de índices. A mesclagem de índices oferece ao Firestore no modo Datastore maior flexibilidade de consulta, mas com uma possível compensação pelo desempenho. A próxima seção descreve o desempenho da mesclagem de índices e como melhorar o desempenho adicionando índices compostos.

Como otimizar sua seleção de índices

Nesta seção, descrevemos as características de desempenho da mesclagem de índices e duas oportunidades de otimização relacionadas à mesclagem de índices:

  • Adicione índices compostos para acelerar consultas que dependem de índices mesclados.
  • Reduza o número de índices compostos aproveitando os índices mesclados.

Desempenho de mesclagem de índices

Em uma mesclagem de índices, o Firestore no modo Datastore mescla com eficiência os índices usando um algoritmo zig-zag merge join (em inglês). Usando esse algoritmo, o modo Datastore une possíveis correspondências de várias verificações de índice para produzir um conjunto de resultados que corresponda a uma consulta. A mesclagem de índices combina os componentes do filtro no tempo de leitura em vez do tempo de gravação. Diferentemente da maioria das consultas do Firestore no modo Datastore em que o desempenho depende apenas do tamanho do conjunto de resultados, o desempenho das consultas de mesclagem de índices depende dos filtros na consulta e de quantas possíveis correspondências o banco de dados considera.

O melhor desempenho de uma mesclagem de índices ocorre quando cada possível correspondência em um índice satisfaz os filtros de consulta. Nesse caso, o desempenho é O(R * I), em que R é o tamanho do conjunto de resultados e I é o número de índices verificados.

O pior desempenho ocorre quando o banco de dados precisa considerar muitas correspondências possíveis, mas poucas satisfazem os filtros de consulta. Nesse caso, o desempenho é O(S), em que S é o tamanho do menor conjunto de possíveis entidades de uma única verificação de índice.

O desempenho real depende da forma dos dados. O número médio de entidades consideradas para cada resultado retornado é O(S/(R * I)). As consultas têm pior desempenho quando muitas entidades correspondem a cada verificação de índice, mas poucas entidades correspondem à consulta como um todo, ou seja, R é pequeno e S é grande.

Quatro fatores atenuam esse risco:

  • O planejador de consultas não procura uma entidade até saber que a entidade corresponde à consulta inteira.

  • O algoritmo zig-zag não precisa encontrar todos os resultados para retornar o próximo resultado. Se você solicitar os 10 primeiros resultados, pagará apenas a latência para encontrar esses 10 resultados.

  • O algoritmo zig-zag pula seções grandes de resultados falsos positivos. O pior desempenho ocorrerá somente se resultados positivos falsos estiverem perfeitamente entrelaçados (em ordem de classificação) entre verificações.

  • A latência depende do número de entidades encontradas em cada verificação de índice, não do número de entidades que correspondem a cada filtro. Como mostrado na próxima seção, adicione índices compostos para melhorar o desempenho da mesclagem de índices.

Como acelerar uma consulta de mesclagem de índices

Quando o Firestore no modo Datastore mescla índices, cada verificação de índice geralmente é mapeada para um único filtro na consulta. É possível melhorar o desempenho da consulta adicionando índices compostos que correspondem a vários filtros na consulta.

Considere esta consulta:

Python

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

Cada filtro mapeia para uma verificação de índice nos seguintes índices integrados:

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

Se você adicionar o índice composto Index(Photo, owner_id, size), a consulta é mapeada para duas verificações de índice em vez de três:

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

Considere um cenário com muitas imagens grandes, muitas imagens em preto e branco, mas poucas imagens panorâmicas grandes. Uma filtragem de consulta para imagens panorâmicas e em preto e branco será lenta se mesclar índices integrados:

Python

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

Para melhorar o desempenho da consulta, é possível diminuir o valor de S (menor conjunto de entidades em uma única verificação de índice) em O(S/(R * I)) adicionando o seguinte índice composto:

    Index(Photo, size, coloration)
    

Em comparação com o uso de dois índices integrados, esse índice composto produz menos resultados em potencial para os mesmos dois filtros de consulta. Essa abordagem melhora substancialmente o desempenho ao custo de mais um índice.

Como reduzir o número de índices compostos com mesclagem de índices

Embora os índices compostos que correspondem exatamente aos filtros em uma consulta tenham melhor desempenho, nem sempre é melhor ou possível adicionar um índice composto para cada combinação de filtros. É necessário equilibrar seus índices compostos em relação aos seguintes itens:

  • Limites de índice composto:

    Limite Valor
    Número máximo de índices compostos para um projeto 200
    Soma máxima dos tamanhos das entradas do índice composto de uma entidade 2 MiB
    Soma máxima dos itens a seguir para uma entidade:
    • número de valores de propriedade indexados
    • número de entradas de índice composto
    20.000
  • Custos de armazenamento de cada novo índice.

Os problemas de indexação geralmente surgem com campos de vários valores, como a propriedade tag das entidades Photo.

Por exemplo, imagine que o recurso de filtragem Photo agora precisa oferecer suporte a cláusulas de ordem decrescente com base em quatro outras propriedades:

Foto
Propriedade Tipo de valor Descrição
date_added Inteiro Data/hora
rating Ponto flutuante Avaliação de usuários agregados
comment_count Inteiro Número de comentários
download_count Inteiro Número de downloads

Se você desconsiderar o campo tag, é possível selecionar índices compostos que correspondam a cada combinação 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)
    

O número total de índices compostos é:

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

Se você tentar aceitar até três filtros tag, o número total de índices compostos excederá o limite de 200:

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

Índices que incluem propriedades de vários valores, como tag também levam à explosão de problemas de índice que aumentam os custos de armazenamento.

Para oferecer suporte a filtros no campo tag desse recurso, é possível reduzir o número total de índices contando com os índices mesclados. O seguinte conjunto de índices compostos é o mínimo necessário para oferecer suporte ao recurso de filtragem Photo com ordenação:

    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)
    

O número de índices compostos definidos é:

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

A mesclagem de índices também oferece os seguintes benefícios:

  • Permite que uma entidade Photo ofereça suporte a até 1.000 tags sem limite no número de tag filtros por consulta.
  • Reduz o número total de índices, o que diminui os custos de armazenamento.

Como selecionar índices para seu aplicativo

Selecione índices ideais para seu banco de dados no modo Datastore usando duas abordagens:

  • Usar a mesclagem de índices para dar suporte a novas consultas

    • Requer menos índices compostos.
    • Reduz o custo de armazenamento por entidade.
    • Evita índices em explosão.
    • O desempenho depende da forma dos dados.
  • Definir um índice composto que corresponda a vários filtros em uma consulta

    • Melhora o desempenho da consulta.
    • Desempenho de consulta consistente que não depende da forma dos dados.
    • Precisa permanecer abaixo do limite de 200 índices compostos.
    • Aumento no custo de armazenamento por entidade.

Ao descobrir os índices ideais para seu aplicativo, a resposta pode mudar conforme a forma dos dados é alterada. O desempenho da consulta de amostragem fornece uma boa ideia das consultas comuns do seu aplicativo e de suas consultas lentas. Com essas informações, é possível adicionar índices para melhorar o desempenho de consultas comuns e lentas.