Práticas recomendadas

Use as práticas recomendadas aqui como uma referência rápida ao criar um app que usa o Firestore.

Local do banco de dados

Ao criar uma instância de banco de dados, selecione o local do banco de dados mais próximo dos usuários e calcule os recursos. Os saltos de rede de longo alcance são mais propensos a erros e aumentam a latência das consultas.

Para maximizar a disponibilidade e a durabilidade do aplicativo, selecione um local com várias regiões e coloque recursos essenciais de computação em pelo menos duas regiões.

Selecione um local regional para reduzir os custos, para diminuir a latência de gravação se o aplicativo for sensível à latência ou para compartilhar o local com outros recursos do GCP.

IDs de documentos;

  • Evite os IDs de documentos . e ...
  • Evite usar / (barras inclinadas) em IDs de documentos.
  • Não use IDs de documento que aumentam constantemente, como estes:

    • Customer1, Customer2, Customer3, ...
    • Product 1, Product 2, Product 3, ...

    Esses IDs sequenciais podem levar a pontos de acesso que afetam a latência.

Nomes dos campos

  • Evite os seguintes caracteres nos nomes de campos, porque eles exigem escape extra:

    • . (ponto)
    • [ (colchete esquerdo)
    • ] (colchete direito)
    • * (asterisco)
    • ` (acento grave)

Índices

Reduzir a latência de gravação

O principal fator para a latência de gravação é o fanout de índice. Estas são as práticas recomendadas para reduzir o fanout de índice:

  • Defina as isenções de índice no nível da coleção. Um padrão fácil é desativar a indexação em ordem decrescente e de matriz. A remoção de valores indexados não utilizados também vai reduzir os custos de armazenamento.

  • Reduzir o número de documentos em uma transação. Para gravar um grande número de documentos, use uma gravação em massa em vez da gravação em lote atômica.

Isenções de índice

Para a maioria dos aplicativos, é possível confiar na indexação automática, além de links de mensagens de erro para gerenciar seus índices. No entanto, é possível adicionar isenções de campo único nos seguintes casos:

Caso Descrição
Campos de string grandes

Se você tiver um campo de string que geralmente armazena valores de string longos que você não usa para consulta, é possível reduzir os custos de armazenamento com a isenção da indexação no campo.

Taxas de gravação altas em uma coleção que contém documentos com valores sequenciais

Se você indexar um campo que aumenta ou diminui sequencialmente entre documentos em uma coleção, como um carimbo de data/hora, a taxa máxima de gravação para a coleção será de 500 gravações por segundo. Se você não fizer consultas com base no campo com valores sequenciais, poderá isentar o campo da indexação para ignorar esse limite.

Em um caso de uso de IoT com uma alta taxa de gravação, por exemplo, uma coleção que contém documentos com um campo de carimbo de data/hora pode se aproximar do limite de 500 gravações por segundo.

Campos TTL

Se você usa políticas de TTL, observe que o campo TTL precisa ser um carimbo de data/hora. A indexação em campos TTL é ativada por padrão e pode afetar o desempenho em taxas de tráfego mais altas. Como prática recomendada, adicione isenções de campo único para seus campos TTL.

Campos grandes de matriz ou de mapa

Campos grandes de matriz ou de mapa podem se aproximar do limite de 40.000 entradas de índice por documento. Se você não estiver fazendo consultas com base em um campo grande de matriz ou de mapa, recomendamos isentá-lo da indexação.

Operações de leitura e gravação

  • A taxa máxima exata em que um app pode atualizar um único documento depende muito da carga de trabalho. Para mais informações, consulte Atualizações para um único documento.

  • Quando possível, use chamadas assíncronas, em vez de chamadas síncronas. As chamadas assíncronas reduzem o impacto da latência. Por exemplo, considere um aplicativo que precisa do resultado de uma pesquisa de documento e dos resultados de uma consulta antes de renderizar uma resposta. Se a pesquisa e a consulta não tiverem dependência de dados, não haverá necessidade de aguardar a conclusão da pesquisa de maneira síncrona antes de iniciar a consulta.

  • Não use deslocamentos. Em vez disso, use cursores. O uso de um deslocamento só evita o retorno dos documentos ignorados para o aplicativo, mas ainda é possível recuperar esses documentos internamente. Os documentos ignorados afetam a latência da consulta, e as operações de leitura necessárias para recuperá-los são cobradas do seu aplicativo.

Novas tentativas de transações

Os SDKs e as bibliotecas de clientes do Firestore repetem automaticamente as transações com falha para lidar com erros transitórios. Se o aplicativo acessar o Firestore por meio das APIs REST ou RPC diretamente, em vez de usar um SDK, o aplicativo deverá implementar novas tentativas de transação para aumentar a confiabilidade.

Atualizações em tempo real

Consulte as práticas recomendadas para atualizações em tempo real em Entender consultas em tempo real em grande escala.

Como elaborar um design para escalonar

As práticas recomendadas a seguir descrevem como evitar situações que criam problemas de contenção:

Atualizações para um único documento

Ao projetar seu app, considere a velocidade com que ele atualiza documentos únicos. A melhor maneira de caracterizar o desempenho da carga de trabalho é realizar testes de carga. A taxa máxima exata em que um app pode atualizar um único documento depende muito da carga de trabalho. Os fatores incluem a taxa de gravação, a disputa entre solicitações e o número de índices afetados.

Uma operação de gravação de documentos atualiza o documento e quaisquer índices associados, e o Firestore aplica de maneira síncrona a operação de gravação em um quórum de réplicas. Com taxas de gravação altas o suficiente, o banco de dados vai começar a passar por contenções, aumento de latência ou outros erros.

Altas taxas de leitura, gravação e exclusão para um intervalo de documentos limitado

Evite altas taxas de leitura ou gravação para fechar documentos lexicograficamente, ou seu aplicativo sofrerá erros de contenção. Esse problema é conhecido como uso excessivo do ponto de acesso, e seu aplicativo pode sofrer isso ao realizar as seguintes ações:

  • Criar novos documentos com uma taxa muito alta e alocar os próprios IDs constantemente crescentes.

    O Firestore aloca IDs de documentos usando um algoritmo de dispersão. Você não encontrará um uso excessivo do ponto de acesso em gravações se criar novos documentos ao usar IDs de documentos automáticos.

  • Criar novos documentos com uma alta taxa em uma coleção com poucos documentos.

  • Criar novos documentos com um campo constantemente crescente, como um carimbo de data/hora, com uma taxa muito alta.

  • Excluir documentos em uma coleção com uma taxa alta.

  • Gravar no banco de dados com uma taxa muito alta sem aumentar gradualmente o tráfego.

Evitar pular dados excluídos

Evite consultas que pulam dados excluídos recentemente. É possível que isso aconteça com muitas entradas de índice se os primeiros resultados da consulta tiverem sido excluídos recentemente.

Um exemplo de uma carga de trabalho que talvez precise pular muitos dados excluídos é aquela que tenta encontrar os itens de trabalho mais antigos em uma fila. A consulta pode ser assim:

docs = db.collection('WorkItems').order_by('created').limit(100)
delete_batch = db.batch()
for doc in docs.stream():
  finish_work(doc)
  delete_batch.delete(doc.reference)
delete_batch.commit()

Sempre que essa consulta é executada, ela verifica as entradas de índice no campo created de qualquer documento excluído recentemente. Isso atrasa as consultas.

Para melhorar o desempenho, use o método start_at se precisar encontrar a melhor maneira de começar. Por exemplo:

completed_items = db.collection('CompletionStats').document('all stats').get()
docs = db.collection('WorkItems').start_at(
    {'created': completed_items.get('last_completed')}).order_by(
        'created').limit(100)
delete_batch = db.batch()
last_completed = None
for doc in docs.stream():
  finish_work(doc)
  delete_batch.delete(doc.reference)
  last_completed = doc.get('created')

if last_completed:
  delete_batch.update(completed_items.reference,
                      {'last_completed': last_completed})
  delete_batch.commit()

OBSERVAÇÃO: o exemplo acima usa um campo constantemente crescente, o que é um antipadrão para altas taxas de gravação.

Como intensificar o tráfego

Intensifique gradualmente o tráfego para novas coleções ou documentos lexicograficamente próximos para que o Firestore tenha tempo suficiente de preparar os documentos para o aumento do tráfego. Recomendamos começar com um máximo de 500 operações por segundo para uma nova coleção e aumentar o tráfego em 50% a cada 5 minutos. Também é possível aumentar o tráfego de gravação, mas considere os limites padrão do Firestore. Confira se as operações estão distribuídas de forma relativamente uniforme por todo o intervalo de chaves. Isso é chamado de regra "500/50/5".

Como migrar o tráfego para uma nova coleção

O aumento gradual é particularmente importante quando você migra o tráfego de aplicativos de uma coleção para outra. Uma forma simples de lidar com essa migração é ler a coleção antiga e, se o documento não existir, ler a partir da nova coleção. No entanto, isso pode causar um aumento repentino de tráfego para fechar documentos lexicograficamente na nova coleção. O Firestore pode não conseguir preparar com eficiência a nova coleção para aumentar o tráfego, especialmente quando ela contém poucos documentos.

Um problema semelhante poderá ocorrer se você alterar os IDs de muitos documentos na mesma coleção.

A melhor estratégia para migrar o tráfego para uma nova coleção depende do seu modelo de dados. Veja a seguir uma estratégia de exemplo conhecida como leituras paralelas. É necessário determinar se essa estratégia é ou não eficaz para seus dados, e uma consideração importante é o impacto dos custos das operações paralelas durante a migração.

Leituras paralelas

Primeiro leia a coleção antiga para implementar leituras paralelas à medida que migrar o tráfego a uma nova coleção. Se não houver documento, leia a partir da nova coleção. Uma alta taxa de leitura de documentos inexistentes pode levar ao uso excessivo do ponto de acesso. Dessa forma, certifique-se de aumentar gradualmente a carga para a nova coleção. Uma melhor estratégia é copiar o documento antigo para a nova coleção e, em seguida, excluir o documento antigo. Aumente gradualmente a leitura paralela a fim de garantir que o Firestore possa lidar com o tráfego para a nova coleção.

Uma estratégia possível para aumentar gradualmente as leituras ou gravações em uma nova coleção é gerar um hash determinístico do ID do usuário para selecionar uma porcentagem aleatória de usuários que tentam gravar novos documentos. Verifique se o resultado do hash do código do usuário não foi distorcido pela função ou pelo comportamento do usuário.

Enquanto isso, gere um job em lote que copie todos os dados dos documentos antigos para a nova coleção. O trabalho em lote deve evitar gravações em IDs de documentos sequenciais para evitar pontos de acesso. Quando o trabalho em lote termina, é possível ler somente a partir da nova coleção.

Uma maneira de refinar essa estratégia é migrar lotes pequenos de usuários de uma só vez. Adicione um campo ao documento do usuário que rastreie o status de migração dele. Selecione um lote de usuários para migrar com base em um hash do ID do usuário. Use um job em lote para migrar documentos para esse lote de usuários e use leituras paralelas para os usuários no meio da migração.

Observe que a reversão não é fácil, a menos que você faça gravações duplas das entidades antigas e novas durante a fase de migração. Isso aumentaria os custos do Firestore.

Privacidade

  • Evite armazenar informações confidenciais no código do projeto do Cloud. Há a possibilidade de esse código persistir após o fim da duração do projeto.
  • Como prática recomendada de conformidade de dados, recomendamos não armazenar informações confidenciais nos nomes de documentos e campos de documentos.