Leia este documento para tomar decisões informadas sobre a arquitetura de seus aplicativos para alto desempenho e confiabilidade. Este documento inclui tópicos avançados do Cloud Firestore. Se você está apenas começando com o Cloud Firestore, consulte o guia de início rápido .
Cloud Firestore é um banco de dados flexível e escalonável para desenvolvimento de dispositivos móveis, web e servidores do Firebase e Google Cloud. É muito fácil começar a usar o Cloud Firestore e escrever aplicativos avançados e avançados.
Para garantir que seus aplicativos continuem a ter um bom desempenho à medida que o tamanho do banco de dados e o tráfego aumentam, é útil entender a mecânica de leituras e gravações no back-end do Cloud Firestore. Você também deve compreender a interação de leitura e gravação com a camada de armazenamento e as restrições subjacentes que podem afetar o desempenho.
Consulte as seções a seguir para conhecer as práticas recomendadas antes de arquitetar seu aplicativo.
Entenda os componentes de alto nível
O diagrama a seguir mostra os componentes de alto nível envolvidos em uma solicitação da API Cloud Firestore.
SDK do Cloud Firestore e bibliotecas de cliente
O Cloud Firestore oferece suporte a SDKs e bibliotecas de cliente para diferentes plataformas. Embora um aplicativo possa fazer chamadas HTTP e RPC diretas para a API Cloud Firestore, as bibliotecas cliente fornecem uma camada de abstração para simplificar o uso da API e implementar práticas recomendadas. Eles também podem fornecer recursos adicionais, como acesso offline, caches e assim por diante.
Front-end do Google (GFE)
Este é um serviço de infraestrutura comum a todos os serviços em nuvem do Google. O GFE aceita solicitações recebidas e as encaminha para o serviço relevante do Google (serviço Firestore neste contexto). Ele também fornece outras funcionalidades importantes, incluindo proteção contra ataques de negação de serviço.
Serviço Cloud Firestore
O serviço Cloud Firestore realiza verificações na solicitação da API, que inclui autenticação, autorização, verificações de cotas e regras de segurança, além de gerenciar transações. Este serviço Cloud Firestore inclui um cliente de armazenamento que interage com a camada de armazenamento para leituras e gravações de dados.
Camada de armazenamento do Cloud Firestore
A camada de armazenamento do Cloud Firestore é responsável por armazenar os dados e metadados e os recursos de banco de dados associados fornecidos pelo Cloud Firestore. As seções a seguir descrevem como os dados são organizados na camada de armazenamento do Cloud Firestore e como o sistema é escalonado. Aprender como os dados são organizados pode ajudar você a projetar um modelo de dados escalonável e a entender melhor as práticas recomendadas no Cloud Firestore.
Intervalos e divisões principais
Cloud Firestore é um banco de dados NoSQL orientado a documentos. Você armazena dados em documentos , que são organizados em hierarquias de coleções . A hierarquia de coleção e o ID do documento são convertidos em uma única chave para cada documento. Os documentos são armazenados logicamente e ordenados lexicograficamente por esta chave única. Usamos o termo intervalo de chaves para nos referir a um intervalo de chaves lexicograficamente contíguo.
Um banco de dados típico do Cloud Firestore é muito grande para caber em uma única máquina física. Existem também cenários em que a carga de trabalho nos dados é muito pesada para ser tratada por uma máquina. Para lidar com grandes cargas de trabalho, o Cloud Firestore particiona os dados em partes separadas que podem ser armazenadas e veiculadas em várias máquinas ou servidores de armazenamento . Essas partições são feitas nas tabelas do banco de dados em blocos de intervalos de chaves chamados splits.
Replicação Síncrona
É importante observar que o banco de dados está sempre sendo replicado de forma automática e síncrona. As divisões de dados possuem réplicas em zonas diferentes para mantê-las disponíveis mesmo quando uma zona se torna inacessível. A replicação consistente para as diferentes cópias da divisão é gerenciada pelo algoritmo Paxos para consenso. Uma réplica de cada divisão é eleita para atuar como líder Paxos, que é responsável por lidar com as gravações dessa divisão. A replicação síncrona oferece a capacidade de sempre ler a versão mais recente dos dados do Cloud Firestore.
O resultado geral disso é um sistema escalável e altamente disponível que fornece baixas latências para leituras e gravações, independentemente de cargas de trabalho pesadas e em grande escala.
Layout de dados
Cloud Firestore é um banco de dados de documentos sem esquema. No entanto, internamente ele organiza os dados principalmente em duas tabelas relacionais no estilo de banco de dados em sua camada de armazenamento, da seguinte maneira:
- Tabela de documentos : os documentos são armazenados nesta tabela.
- Tabela de índices : Nesta tabela são armazenadas entradas de índice que permitem obter resultados de forma eficiente e classificados por valor de índice.
O diagrama a seguir mostra a aparência das tabelas de um banco de dados do Cloud Firestore com as divisões. As divisões são replicadas em três zonas diferentes e cada divisão tem um líder Paxos atribuído.
Região única versus multirregião
Ao criar um banco de dados, você deve selecionar uma região ou multirregião .
Uma única localização regional é uma localização geográfica específica, como us-west1 . As divisões de dados de um banco de dados Cloud Firestore possuem réplicas em diferentes zonas da região selecionada, conforme explicado anteriormente.
Um local multirregional consiste em um conjunto definido de regiões onde as réplicas do banco de dados são armazenadas. Em uma implantação multirregional do Cloud Firestore, duas das regiões têm réplicas completas de todos os dados no banco de dados. Uma terceira região possui uma réplica testemunha que não mantém um conjunto completo de dados, mas participa da replicação. Ao replicar os dados entre múltiplas regiões, os dados ficam disponíveis para serem gravados e lidos mesmo com a perda de uma região inteira.
Para obter mais informações sobre os locais de uma região, consulte Locais do Cloud Firestore .
Entenda a vida de uma gravação no Cloud Firestore
Um cliente do Cloud Firestore pode gravar dados criando, atualizando ou excluindo um único documento. Uma gravação em um único documento requer a atualização atômica do documento e de suas entradas de índice associadas na camada de armazenamento. O Cloud Firestore também oferece suporte a operações atômicas que consistem em múltiplas leituras e/ou gravações em um ou mais documentos.
Para todos os tipos de gravação, o Cloud Firestore fornece as propriedades ACID (atomicidade, consistência, isolamento e durabilidade) de bancos de dados relacionais. O Cloud Firestore também oferece serialização , o que significa que todas as transações aparecem como se fossem executadas em uma ordem serial.
Etapas de alto nível em uma transação de gravação
Quando o cliente Cloud Firestore emite uma gravação ou confirma uma transação, usando qualquer um dos métodos mencionados anteriormente, isso é executado internamente como uma transação de leitura e gravação de banco de dados na camada de armazenamento. A transação permite que o Cloud Firestore forneça as propriedades ACID mencionadas anteriormente.
Como primeira etapa de uma transação, o Cloud Firestore lê o documento existente e determina as mutações a serem feitas nos dados da tabela Documentos.
Isso também inclui fazer as atualizações necessárias na tabela Índices da seguinte forma:
- Os campos que estão sendo adicionados aos documentos necessitam de inserções correspondentes na tabela Índices.
- Os campos que estão sendo removidos dos documentos precisam de exclusões correspondentes na tabela Índices.
- Os campos que estão sendo modificados nos documentos precisam tanto de exclusões (para valores antigos) quanto de inserções (para novos valores) na tabela de Índices.
Para calcular as mutações mencionadas anteriormente, o Cloud Firestore lê a configuração de indexação do projeto. A configuração de indexação armazena informações sobre os índices de um projeto. O Cloud Firestore usa dois tipos de índices: campo único e composto. Para uma compreensão detalhada dos índices criados no Cloud Firestore, consulte Tipos de índice no Cloud Firestore .
Depois que as mutações são calculadas, o Cloud Firestore as coleta em uma transação e depois a confirma.
Entenda uma transação de gravação na camada de armazenamento
Conforme discutido anteriormente, uma gravação no Cloud Firestore envolve uma transação de leitura e gravação na camada de armazenamento. Dependendo do layout dos dados, uma gravação pode envolver uma ou mais divisões, conforme visto no layout dos dados .
No diagrama a seguir, o banco de dados Cloud Firestore tem oito divisões (marcadas de 1 a 8) hospedadas em três servidores de armazenamento diferentes em uma única zona, e cada divisão é replicada em três (ou mais) zonas diferentes. Cada divisão tem um líder Paxos, que pode estar em uma zona diferente para diferentes divisões.
Considere um banco de dados Cloud Firestore que possui a coleção Restaurants
da seguinte forma:
O cliente Cloud Firestore solicita a seguinte alteração em um documento da coleção Restaurant
atualizando o valor do campo priceCategory
.
As seguintes etapas de alto nível descrevem o que acontece como parte da gravação:
- Crie uma transação de leitura e gravação.
- Leia o documento
restaurant1
na coleçãoRestaurants
da tabela Documents da camada de armazenamento. - Leia os índices do documento na tabela Índices .
- Calcule as mutações a serem feitas nos dados. Neste caso, existem cinco mutações:
- M1: atualize a linha do
restaurant1
na tabela Documentos para refletir a alteração no valor do campopriceCategory
. - M2 e M3: Exclua as linhas do valor antigo de
priceCategory
na tabela Índices para índices decrescentes e ascendentes. - M4 e M5: Insira as linhas para o novo valor de
priceCategory
na tabela Índices para índices decrescentes e ascendentes.
- M1: atualize a linha do
- Comprometa essas mutações.
O cliente de armazenamento no serviço Cloud Firestore procura os splits que possuem as chaves das linhas a serem alteradas. Vamos considerar um caso em que o Split 3 atende M1 e o Split 6 atende M2-M5. Existe uma transação distribuída, envolvendo todos esses splits como participantes . As divisões de participantes também podem incluir qualquer outra divisão a partir da qual os dados foram lidos anteriormente como parte da transação de leitura e gravação.
As etapas a seguir descrevem o que acontece como parte do commit:
- O cliente de armazenamento emite uma confirmação. O commit contém as mutações M1-M5.
- Os splits 3 e 6 são os participantes desta transação. Um dos participantes é escolhido como coordenador , como na Divisão 3. A função do coordenador é garantir que a transação seja confirmada ou abortada atomicamente em todos os participantes.
- As réplicas líderes dessas divisões são responsáveis pelo trabalho realizado pelos participantes e coordenadores.
- Cada participante e coordenador executa um algoritmo Paxos com suas respectivas réplicas.
- O líder executa um algoritmo Paxos com as réplicas. O quorum é alcançado se a maioria das réplicas responder com um
ok to commit
a resposta ao líder. - Cada participante notifica o coordenador quando estiver preparado (primeira fase do commit em duas fases). Se algum participante não puder confirmar a transação, toda a transação
aborts
.
- O líder executa um algoritmo Paxos com as réplicas. O quorum é alcançado se a maioria das réplicas responder com um
- Uma vez que o coordenador saiba que todos os participantes, inclusive ele próprio, estão preparados, ele comunica o resultado da transação
accept
a todos os participantes (segunda fase do commit de duas fases). Nesta fase, cada participante registra a decisão de commit para armazenamento estável e a transação é confirmada. - O coordenador responde ao cliente de armazenamento no Cloud Firestore que a transação foi confirmada. Paralelamente, o coordenador e todos os participantes aplicam as mutações aos dados.
Quando o banco de dados do Cloud Firestore é pequeno, pode acontecer que uma única divisão possua todas as chaves nas mutações M1-M5. Nesse caso, há apenas um participante na transação e o commit em duas fases mencionado anteriormente não é necessário, tornando as escritas mais rápidas.
Grava em multirregiões
Numa implementação multirregional, a distribuição de réplicas entre regiões aumenta a disponibilidade, mas acarreta um custo de desempenho. A comunicação entre réplicas em diferentes regiões leva mais tempo de ida e volta. Conseqüentemente, a latência básica para operações do Cloud Firestore é um pouco maior em comparação com implantações de região única.
Configuramos as réplicas de forma que a liderança das divisões permaneça sempre na região primária. A região primária é aquela de onde o tráfego chega ao servidor Cloud Firestore. Essa decisão da liderança reduz o atraso de ida e volta na comunicação entre o cliente de armazenamento no Cloud Firestore e o líder da réplica (ou coordenador para transações multi-split).
Cada gravação no Cloud Firestore também envolve alguma interação com o mecanismo em tempo real do Cloud Firestore. Para obter mais informações sobre consultas em tempo real, consulte Compreender consultas em tempo real em escala .
Entenda a vida de uma leitura no Cloud Firestore
Esta seção se aprofunda nas leituras autônomas e não em tempo real no Cloud Firestore. Internamente, o servidor Cloud Firestore lida com a maioria dessas consultas em dois estágios principais:
- Uma varredura de intervalo único na tabela Índices
- Pesquisas de pontos na tabela Documentos com base no resultado da verificação anterior
As leituras de dados da camada de armazenamento são feitas internamente usando uma transação de banco de dados para garantir leituras consistentes. Entretanto, diferentemente das transações usadas para escrita, essas transações não aceitam bloqueios. Em vez disso, eles funcionam escolhendo um carimbo de data/hora e executando todas as leituras nesse carimbo de data/hora. Como não adquirem bloqueios, não bloqueiam transações simultâneas de leitura e gravação. Para executar essa transação, o cliente de armazenamento no Cloud Firestore especifica um limite de carimbo de data/hora, que informa à camada de armazenamento como escolher um carimbo de data/hora de leitura. O tipo de carimbo de data/hora escolhido pelo cliente de armazenamento no Cloud Firestore é determinado pelas opções de leitura da solicitação de leitura.
Entenda uma transação de leitura na camada de armazenamento
Esta seção descreve os tipos de leituras e como elas são processadas na camada de armazenamento do Cloud Firestore.
Leituras fortes
Por padrão, as leituras do Cloud Firestore são fortemente consistentes . Essa forte consistência significa que uma leitura do Cloud Firestore retorna a versão mais recente dos dados que reflete todas as gravações que foram confirmadas até o início da leitura.
Leitura de divisão única
O cliente de armazenamento no Cloud Firestore procura as divisões que possuem as chaves das linhas a serem lidas. Vamos supor que seja necessário fazer uma leitura do Split 3 da seção anterior. O cliente envia a solicitação de leitura para a réplica mais próxima para reduzir a latência de ida e volta.
Neste ponto, os seguintes casos podem acontecer dependendo da réplica escolhida:
- A solicitação de leitura vai para uma réplica líder (Zona A).
- Como o líder está sempre atualizado, a leitura pode prosseguir diretamente.
- A solicitação de leitura vai para uma réplica não líder (como Zona B)
- O Split 3 pode saber pelo seu estado interno que possui informações suficientes para servir a leitura e o split faz isso.
- O Split 3 não tem certeza se viu os dados mais recentes. Ele envia uma mensagem ao líder solicitando o carimbo de data/hora da última transação que precisa ser aplicada para servir a leitura. Depois que a transação for aplicada, a leitura poderá prosseguir.
O Cloud Firestore então retorna a resposta ao seu cliente.
Leitura multi-dividida
Na situação em que as leituras devem ser feitas a partir de múltiplas divisões, o mesmo mecanismo acontece em todas as divisões. Depois que os dados de todas as divisões forem retornados, o cliente de armazenamento no Cloud Firestore combina os resultados. O Cloud Firestore então responde ao seu cliente com esses dados.
Leituras obsoletas
Leituras fortes são o modo padrão no Cloud Firestore. No entanto, isso tem o custo de uma latência potencialmente maior devido à comunicação que pode ser necessária com o líder. Muitas vezes, seu aplicativo Cloud Firestore não precisa ler a versão mais recente dos dados e a funcionalidade funciona bem com dados que podem ficar obsoletos por alguns segundos.
Nesse caso, o cliente pode optar por receber leituras obsoletas usando as opções de leitura read_time
. Nesse caso, as leituras são feitas como os dados estavam em read_time
, e é altamente provável que a réplica mais próxima já tenha verificado que possui dados no read_time
especificado. Para um desempenho visivelmente melhor, 15 segundos é um valor de desatualização razoável. Mesmo para leituras obsoletas, as linhas geradas são consistentes entre si.
Evite pontos de acesso
As divisões no Cloud Firestore são automaticamente divididas em partes menores para distribuir o trabalho de servir o tráfego para mais servidores de armazenamento quando necessário ou quando o espaço principal se expande. As divisões criadas para lidar com o excesso de tráfego são retidas por cerca de 24 horas, mesmo que o tráfego desapareça. Portanto, se houver picos de tráfego recorrentes, as divisões serão mantidas e mais divisões serão introduzidas sempre que necessário. Esses mecanismos ajudam os bancos de dados do Cloud Firestore a serem escalonados automaticamente de acordo com o aumento da carga de tráfego ou do tamanho do banco de dados. No entanto, existem algumas limitações que você deve conhecer, conforme explicado abaixo.
Dividir o armazenamento e a carga leva tempo, e aumentar o tráfego muito rapidamente pode causar erros de alta latência ou de prazo excedido, comumente chamados de pontos de acesso , enquanto o serviço se ajusta. A prática recomendada é distribuir operações em todo o intervalo de chaves, enquanto aumenta o tráfego em uma coleção em um banco de dados com 500 operações por segundo. Após esse aumento gradual, aumente o tráfego em até 50% a cada cinco minutos. Esse processo é chamado de regra 500/50/5 e posiciona o banco de dados para dimensionar de maneira ideal para atender à sua carga de trabalho.
Embora as divisões sejam criadas automaticamente com o aumento da carga, o Cloud Firestore só pode dividir um intervalo de chaves até servir um único documento usando um conjunto dedicado de servidores de armazenamento replicados. Como resultado, volumes elevados e sustentados de operações simultâneas num único documento podem levar a um ponto crítico nesse documento. Se você encontrar altas latências sustentadas em um único documento, considere modificar seu modelo de dados para dividir ou replicar os dados em vários documentos.
Erros de contenção acontecem quando múltiplas operações tentam ler e/ou gravar o mesmo documento simultaneamente.
Outro caso especial de hotspotting acontece quando uma chave crescente/decrescente sequencial é usada como ID do documento no Cloud Firestore, e há um número consideravelmente alto de operações por segundo. Criar mais divisões não ajuda aqui, pois o aumento do tráfego simplesmente se move para a divisão recém-criada. Como o Cloud Firestore indexa automaticamente todos os campos do documento por padrão, esses pontos de acesso móveis também podem ser criados no espaço de índice de um campo do documento que contém um valor crescente/diminuente sequencial, como um carimbo de data/hora.
Observe que, seguindo as práticas descritas acima, o Firestore pode ser escalonado para atender cargas de trabalho arbitrariamente grandes sem precisar ajustar nenhuma configuração.
Solução de problemas
O Firestore fornece o Key Visualizer como uma ferramenta de diagnóstico projetada especificamente para analisar padrões de uso e solucionar problemas de hotspot.
Qual é o próximo
- Leia sobre mais práticas recomendadas
- Aprenda sobre consultas em tempo real em grande escala