Esta página descreve os conceitos a ter em conta ao selecionar os índices do Firestore no modo Datastore para a sua app.
O Firestore no modo Datastore oferece um elevado desempenho de consultas através da utilização de índices para todas as consultas. O desempenho da maioria das consultas depende da dimensão do conjunto de resultados e não da dimensão total da base de dados.
O Firestore no modo Datastore define índices incorporados para cada propriedade numa entidade. Estes índices de propriedade única suportam muitas consultas simples. O Firestore no modo Datastore suporta uma funcionalidade de união de índices que permite à sua base de dados unir índices incorporados para suportar consultas adicionais. Para consultas mais complexas, tem de definir índices compostos antecipadamente.
Esta página centra-se na funcionalidade de união de índices, porque afeta duas oportunidades importantes de otimização de índices:
- Acelerar as consultas
- Reduzir o número de índices compostos
O exemplo seguinte demonstra a funcionalidade de união de índices.
Filtrar entidades Photo
Considere uma base de dados do modo Datastore
com entidades do tipo Photo
:
Foto | ||
---|---|---|
Propriedade | Tipo de valor | Descrição |
owner_id |
String | ID do utilizador |
tag |
Matriz de strings | Palavras-chave tokenizadas |
size |
Número inteiro |
Enumeração:
|
coloration |
Número inteiro |
Enumeração:
|
Imagine que precisa de uma funcionalidade da app que permita aos utilizadores consultar Photo
entidades com base num AND
lógico do seguinte:
Até três filtros com base nas propriedades:
owner_id
size
coloration
Uma
tag
string de pesquisa. A app tokeniza a string de pesquisa em etiquetas e adiciona um filtro para cada etiqueta.Por exemplo, a app transforma a string de pesquisa
outside, family
na consulta filtrostag=outside
etag=family
.
Ao usar os índices incorporados e a funcionalidade de união de índices do Firestore no modo Datastore, pode
cumprir os requisitos de índice desta funcionalidade de filtro Photo
sem adicionar
índices compostos adicionais.
Os índices incorporados para entidades Photo
suportam consultas com um único filtro, como:
Python
A funcionalidade de filtro Photo
também requer consultas que combinem vários filtros de igualdade com um AND
lógico:
Python
O Firestore no modo Datastore pode suportar estas consultas através da união de índices incorporados.
União de índices
O Firestore no modo Datastore pode usar a união de índices quando a sua consulta e os seus índices cumprem todas as seguintes restrições:
- A consulta usa apenas filtros de igualdade (
=
) - Não existe nenhum índice composto que corresponda perfeitamente aos filtros e à ordenação da consulta
- Cada filtro de igualdade corresponde, pelo menos, a um índice existente com a mesma ordenação que a consulta
Nesta situação, o Firestore no modo Datastore pode usar os índices existentes para suportar a consulta em vez de exigir que configure um índice composto adicional.
Quando dois ou mais índices são ordenados pelos mesmos critérios, o Firestore no modo Datastore pode unir os resultados de várias análises de índices para encontrar os resultados comuns a todos esses índices. O Firestore no modo Datastore pode unir índices incorporados, porque todos ordenam os valores pela chave da entidade.
Ao unir índices incorporados, o Firestore no modo Datastore suporta consultas com filtros de igualdade em várias propriedades:
Python
O Firestore no modo Datastore também pode unir resultados de índice de várias secções do mesmo índice. Ao unir diferentes secções do índice incorporado para a propriedade tag
, o Firestore no modo Datastore suporta consultas que combinam vários filtros tag
numa lógica AND
:
Python
As consultas suportadas por índices incorporados unidas completam o conjunto de consultas
necessárias pela funcionalidade de filtragem Photo
. Tenha em atenção que o suporte da funcionalidade de filtragem não exigiu índices compostos adicionais.Photo
Quando selecionar os índices ideais para a sua app, é importante compreender a funcionalidade de união de índices. A união de índices oferece ao Firestore no modo Datastore uma maior flexibilidade de consulta, mas com uma possível compensação no desempenho. A secção seguinte descreve o desempenho da união de índices e como melhorar o desempenho adicionando índices compostos.
Encontrar o índice perfeito
O índice é ordenado primeiro pelo antepassado e, em seguida, pelos valores das propriedades, na ordem especificada na definição do índice. O índice composto perfeito para uma consulta, que permite que a consulta seja executada da forma mais eficiente, é definido nas seguintes propriedades, por ordem:
- Propriedades usadas em filtros de igualdade
- Propriedades usadas em ordens de ordenação
- Propriedades usadas no filtro
distinctOn
- Propriedades usadas em filtros de intervalo e desigualdade (que ainda não estão incluídas nas ordens de ordenação)
- Propriedades usadas em agregações e projeções (que ainda não estão incluídas em ordens de ordenação e filtros de intervalo e desigualdade)
Isto garante que todos os resultados para cada execução possível da consulta são contabilizados. As bases de dados do Firestore no modo Datastore executam uma consulta com um índice perfeito através dos seguintes passos:
- Identifica o índice correspondente ao tipo, às propriedades de filtro, aos operadores de filtro e às ordens de ordenação da consulta
- Analisa a partir do início do índice até à primeira entidade que cumpre todas ou um subconjunto das condições de filtro da consulta
- Continua a analisar o índice, devolvendo cada entidade que satisfaz todas as condições de filtro, até que
- Encontra uma entidade que não cumpre as condições do filtro ou
- Chega ao fim do índice ou
- Recolheu o número máximo de resultados pedidos pela consulta
Por exemplo, considere a seguinte consulta:
SELECT * FROM Task
WHERE category = 'Personal'
AND priority < 3
ORDER BY priority DESC
O índice composto perfeito para esta consulta é um índice de chaves para entidades do tipo Task
, com colunas para os valores das propriedades category
e priority
. O índice é ordenado primeiro por ordem ascendente por category
e, em seguida, por ordem descendente por priority
:
indexes:
- kind: Task
properties:
- name: category
direction: asc
- name: priority
direction: desc
Duas consultas do mesmo formulário, mas com valores de filtro diferentes, usam o mesmo índice. Por exemplo, a seguinte consulta usa o mesmo índice que a consulta anterior:
SELECT * FROM Task
WHERE category = 'Work'
AND priority < 5
ORDER BY priority DESC
Para este índice
indexes:
- kind: Task
properties:
- name: category
direction: asc
- name: priority
direction: asc
- name: created
direction: asc
O índice anterior pode satisfazer ambas as seguintes consultas:
SELECT * FROM Task
WHERE category = 'Personal'
AND priority = 5
ORDER BY created ASC
e
SELECT * FROM Task
WHERE category = 'Work'
ORDER BY priority ASC, created ASC
Otimizar a seleção de índices
Esta secção descreve as caraterísticas de desempenho da união de índices e duas oportunidades de otimização relacionadas com a união de índices:
- Adicione índices compostos para acelerar as consultas que dependem de índices unidos
- Reduza o número de índices compostos tirando partido dos índices unidos
Desempenho da união de índices
Numa união de índices, o Firestore no modo Datastore une os índices de forma eficiente através de um algoritmo de junção de união em zigue-zague. Com este algoritmo, o modo Datastore junta potenciais correspondências de várias análises de índice para produzir um conjunto de resultados que corresponda a uma consulta. A união de índices combina componentes de filtro no momento da leitura, em vez de no momento da escrita. Ao contrário 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 união de índices depende dos filtros na consulta e do número de potenciais correspondências que a base de dados considera.
O melhor desempenho possível de uma união de índices ocorre quando todas as potenciais correspondências num índice satisfazem os filtros de consulta. Neste caso, o desempenho é O(R * I)
, onde R
é o tamanho do conjunto de resultados e I
é o número de índices analisados.
O desempenho no pior cenário ocorre quando a base de dados tem de considerar muitas correspondências potenciais, mas poucas delas satisfazem os filtros de consulta. Neste caso, o desempenho é O(S)
, em que S
é o tamanho do conjunto mais pequeno de potenciais entidades a partir de uma única análise de índice.
O desempenho real depende do formato dos dados. O número médio de entidades consideradas para cada resultado devolvido é O(S/(R * I))
. As consultas têm um desempenho pior quando muitas entidades correspondem a cada análise de índice, mas poucas entidades correspondem à consulta como um todo, o que significa que R
é pequeno e S
é grande.
Quatro aspetos mitigam este risco:
O planeador de consultas não procura uma entidade até saber que a entidade corresponde à consulta completa.
O algoritmo em zigue-zague não precisa de encontrar todos os resultados para devolver o resultado seguinte. Se pedir os primeiros 10 resultados, paga apenas a latência para encontrar esses 10 resultados.
O algoritmo em ziguezague ignora grandes secções de resultados de falsos positivos. O pior desempenho só ocorre se os resultados falsos positivos estiverem perfeitamente interligados (por ordem de classificação) entre as análises.
A latência depende do número de entidades encontradas em cada análise de índice e não do número de entidades que correspondem a cada filtro. Conforme mostrado na secção seguinte, pode adicionar índices compostos para melhorar o desempenho da união de índices.
Acelerar uma consulta de união de índices
Quando o Firestore no modo Datastore une índices, cada análise de índice é frequentemente mapeada para um único filtro na consulta. Pode melhorar o desempenho das consultas adicionando índices compostos que correspondam a vários filtros na consulta.
Considere esta consulta:
Python
Cada filtro é mapeado para uma análise de índice nos seguintes índices incorporados:
Index(Photo, owner_id) Index(Photo, size) Index(Photo, tag)
Se adicionar o índice composto Index(Photo, owner_id, size)
, a consulta é mapeada para duas análises de índice em vez de três:
# Satisfies both 'owner_id=username' and 'size=2'
Index(Photo, owner_id, size)
Index(Photo, tag)
Considere um cenário com muitas imagens grandes, muitas imagens a preto e branco, mas poucas imagens panorâmicas grandes. Uma consulta que filtre imagens panorâmicas e a preto e branco é lenta se unir índices incorporados:
Python
Para melhorar o desempenho das consultas, pode diminuir o valor de S
(conjunto mais pequeno de entidades numa única análise de índice) em O(S/(R * I))
adicionando o seguinte índice composto:
Index(Photo, size, coloration)
Em comparação com a utilização de dois índices incorporados, este índice composto produz menos resultados potenciais para os mesmos dois filtros de consulta. Esta abordagem melhora substancialmente o desempenho ao custo de mais um índice.
Reduzir o número de índices compostos com a união de índices
Embora os índices compostos que correspondem exatamente aos filtros numa consulta tenham o melhor desempenho, nem sempre é melhor ou possível adicionar um índice composto para cada combinação de filtros. Tem de equilibrar os índices compostos com o seguinte:
Limites de índice composto:
Limite Montante Número máximo de índices compostos para uma base de dados -
200 quando não ativou a faturação para o seu Google Cloud projeto.
Se precisar de mais quota, tem de ativar a faturação para o seu Google Cloud projeto.
-
1000 quando ativa a faturação para o seu projeto do Google Cloud .
Pode contactar o apoio técnico para pedir um aumento deste limite.
Soma máxima dos tamanhos das entradas do índice composto de uma entidade 2 MiB Soma máxima do seguinte para uma entidade: - O número de valores de propriedades indexados
- o número de entradas de índice composto
20 000 -
- Custos de armazenamento de cada índice adicional.
- Efeitos na latência de gravação.
Os problemas de indexação surgem frequentemente com campos de vários valores, como a propriedade tag
das entidades Photo
.
Por exemplo, imagine que a funcionalidade de filtragem Photo
tem agora de suportar
cláusulas de ordenação descendente com base em quatro propriedades adicionais:
Foto | ||
---|---|---|
Propriedade | Tipo de valor | Descrição |
date_added |
Número inteiro | Data/hora |
rating |
Flutuante | Classificação dos utilizadores agregada |
comment_count |
Número inteiro | Número de comentários |
download_count |
Número inteiro | Número de transferências |
Se ignorar o campo tag
, é possível selecionar índices compostos que correspondam a todas as combinações 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 tentar suportar até 3 filtros tag
, o número total de índices compostos é:
2 ^ (3 + 3 tag filters) * 4 = 256 indexes.
Os índices que incluem propriedades com vários valores, como tag
, também originam problemas de índice em expansão que aumentam os custos de armazenamento e a latência de escrita.
Para suportar filtros no campo tag
para esta funcionalidade, pode reduzir o número total de índices com base em índices unidos. O seguinte conjunto de índices compostos é o mínimo necessário para suportar a funcionalidade 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 união de índices também oferece as seguintes vantagens:
- Permite que uma entidade
Photo
suporte até 1000 etiquetas sem limite no número de filtrostag
por consulta. - Reduz o número total de índices, o que reduz os custos de armazenamento e a latência de escrita.
Selecionar índices para a sua app
Pode selecionar os índices ideais para a sua base de dados do modo Datastore através de duas abordagens:
Use a união de índices para suportar consultas adicionais
- Requer menos índices compostos
- Reduz o custo de armazenamento por entidade
- Melhora a latência de escrita
- Evita índices expansivos
- O desempenho depende do formato dos dados
Defina um índice composto que corresponda a vários filtros numa consulta
- Melhora o desempenho das consultas
- Desempenho de consulta consistente que não depende do formato dos dados
- Tem de permanecer abaixo do limite de índices compostos
- Aumento do custo de armazenamento por entidade
- Aumento da latência de escrita
Ao determinar os índices ideais para a sua app, a resposta pode mudar à medida que a forma dos seus dados muda. O desempenho das consultas de amostragem dá-lhe uma boa ideia das consultas comuns da sua app e das consultas lentas. Com estas informações, pode adicionar índices para melhorar o desempenho das consultas comuns e lentas.