Práticas recomendadas

As práticas recomendadas listadas aqui podem ser usadas como referência rápida ao criar um aplicativo que usa o Cloud Datastore. Se você ainda está dando os primeiros passos no Cloud Datastore, esta página não é o melhor lugar para começar. Ela não contém os ensinamentos básicos de como usar o Cloud Datastore. Se você é um novo usuário, sugerimos que comece com Primeiros passos no Cloud Datastore.

Geral

  • Sempre use caracteres UTF-8 nos nomes de namespaces, tipos, propriedades e chaves personalizadas. Usar caracteres diferentes de UTF-8 nesses nomes pode prejudicar a funcionalidade do Cloud Datastore. Por exemplo, um caractere não codificado em UTF-8 em um nome de propriedade pode impedir a criação de índices que usam essa propriedade.
  • Não use barras (/) nos nomes de tipos ou chaves personalizadas. Usar barras nesses nomes pode prejudicar a funcionalidade futura.
  • 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.

Chamadas de API

  • Em vez de operações individuais, use operações em lote para realizar leituras, gravações e exclusões. As operações em lote são mais eficientes, pois realizam várias operações gerando a mesma sobrecarga que uma única operação.
  • Se uma transação falhar, faça o rollback. A operação de rollback reduz a latência da repetição de solicitações diferentes que disputam os mesmos recursos de uma transação. Observe que a operação de rollback também pode falhar e por isso precisa ser tentada somente uma vez.
  • 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, pense em um aplicativo que precise dos resultados de um lookup() síncrono e dos resultados de uma consulta antes de processar uma resposta. Se lookup() e a consulta não tiverem dependência de dados, não haverá necessidade de aguardar a conclusão de lookup() de maneira síncrona antes de iniciar a consulta.

Entidades

  • Agrupe os dados altamente relacionados em grupos de entidades. Com os grupos de entidades, é possível realizar consultas de ancestral, que retornam resultados muito consistentes. Além disso, as consultas de ancestral realizam uma rápida verificação dos grupos de entidades com o mínimo de E/S, pois as entidades contidas no mesmo grupo são armazenadas em locais fisicamente próximos nos servidores do Cloud Datastore.
  • Evite realizar mais de uma gravação por segundo no mesmo grupo de entidades. Realizar gravações em uma taxa constante acima desse limite torna as leituras consistentes mais lentas, ocasiona erros de tempo limite nas leituras com forte consistência e reduz o desempenho do aplicativo. Gravações em lote ou transacionais em um grupo de entidades são consideradas como uma única gravação dentro desse limite.
  • Não inclua a mesma entidade (por chave) várias vezes na mesma operação commit. Fazer isso pode afetar a latência do Cloud Datastore.

Chaves

  • Se não forem fornecidos na criação da entidade, os nomes das chaves serão gerados automaticamente. Eles são alocados de maneira a serem distribuídos uniformemente no keyspace.
  • Sempre use caracteres UTF-8, exceto a barra (/), se quiser personalizar os nomes das chaves. O uso de caracteres diferentes de UTF-8 prejudica vários processos, como a importação do backup do Cloud Datastore para o Google BigQuery. Usar barras pode prejudicar a funcionalidade futura.
  • No caso de chaves com código numérico:
    • Não use um número negativo como código. Um código negativo pode prejudicar a classificação.
    • Não use o valor zero (0) como código. Caso contrário, o código será atribuído automaticamente.
    • Se quiser atribuir manualmente seus próprios códigos numéricos às entidades criadas, faça com que o aplicativo consiga um bloco de códigos com o método allocateIds(). Assim, o Cloud Datastore não atribuirá os códigos numéricos que você criou manualmente a outras entidades.
  • Se você atribuir códigos numéricos manuais ou nomes personalizados às entidades que você mesmo criou, não use valores crescentes de maneira uniforme, como:

    1, 2, 3, …,
    "Customer1", "Customer2", "Customer3", ….
    "Product 1", "Product 2", "Product 3", ….
    

    Esse tipo de numeração sequencial pode causar hotspots que afetam a latência do Cloud Datastore, no caso de aplicativos que geram muito tráfego. Para evitar esse problema, consiga os códigos numéricos por meio do método allocateIds(). O método allocateIds() gera sequências bem distribuídas de códigos numéricos.

  • Ao especificar uma chave ou armazenar o nome gerado, mais adiante você poderá realizar um lookup() consistente nessa entidade sem precisar emitir uma consulta para encontrar a entidade.

Índices

Propriedades

  • Sempre use caracteres UTF-8 nas propriedades do tipo string. O uso de caracteres diferentes de UTF-8 em propriedades do tipo string pode prejudicar as consultas. Se você precisar salvar dados que contenham caracteres não codificados em UTF-8, use uma string de bytes.
  • Não use pontos nos nomes das propriedades. Isso pode prejudicar a indexação futura de propriedades estruturadas.

Consultas

  • Se você precisar acessar somente a chave dos resultados de consultas, use uma consulta apenas de chaves. Esse tipo de consulta retorna resultados com menor latência e custo do que a recuperação de entidades inteiras.
  • Se você precisar acessar somente propriedades específicas de uma entidade, use uma consulta de projeção. Esse tipo de consulta retorna resultados com menor latência e custo do que a recuperação de entidades inteiras.
  • Da mesma forma, caso seja necessário acessar apenas as propriedades incluídas no filtro de consultas (como as listadas em uma cláusula order by), use uma consulta de projeção.
  • Não use deslocamentos. Em vez disso, use cursores. Fazer o uso de deslocamentos apenas evita o retorno das entidades ignoradas para o aplicativo. No entanto, essas entidades ainda são recuperadas internamente. As entidades ignoradas afetam a latência da consulta. Além disso, as operações de leitura necessárias para recuperar essas entidades serão cobradas do seu aplicativo.
  • Se você precisar de uma consistência forte nas suas consultas, use uma consulta de ancestral. Para tanto, primeiro é necessário estruturar os dados para uma forte consistência. Esse tipo de consulta retorna resultados com forte consistência. Observe que, em uma consulta não ancestral somente de chaves seguida por lookup(), não há consistência nos resultados, porque ela recebe resultados de um índice inconsistente no momento da consulta.

Como desenvolver um design para escalonar

Atualizações em um único grupo de entidades

Não atualize um único grupo de entidades no Cloud Datastore muito rapidamente.

Se você usa o Cloud Datastore, o Google recomenda desenvolver um design que fará com que o seu aplicativo não precise atualizar um grupo de entidades mais de uma vez por segundo. Lembre-se de que uma entidade sem pai nem filhos constitui um grupo de entidades próprio. Se você atualizar um grupo de entidades muito rapidamente, as gravações do Cloud Datastore ocasionarão mais latências e erros de tempo limite, entre outros. Trata-se de uma contenção.

As taxas de gravação do Cloud Datastore em um único grupo de entidades, às vezes, excedem o limite de uma gravação por segundo. Portanto, há a possibilidade dos testes de carga não mostrarem esse problema. Você pode encontrar algumas sugestões de design para o seu aplicativo no artigo sobre contenção do Cloud Datastore.

Taxas de leitura/gravação altas em um intervalo de chaves restrito

Evite a ocorrência de taxas de leitura ou gravações altas nas chaves do Cloud Datastore que sejam próximas alfabeticamente.

O Cloud Datastore se baseia no banco de dados NoSQL do Google, o Bigtable. Por isso, está sujeito às mesmas características de desempenho do Bigtable. O Bigtable escalona por meio da fragmentação de linhas em blocos separados. Essas linhas seguem a ordem alfabética por chave.

No Cloud Datastore, um aumento repentino na taxa de gravação em um intervalo pequeno de chaves, que excede a capacidade de um único servidor de blocos, ocasiona gravações mais lentas devido à sobrecarga no bloco. Mais cedo ou mais tarde, o Bigtable dividirá o espaço da chave para aguentar a alta carga.

Normalmente, o limite de leituras é muito maior do que o de gravações, a menos que as leituras sejam realizadas em uma única chave a uma alta taxa. O Bigtable não pode dividir uma chave única em mais de um bloco.

Os blocos mais usados são aplicáveis a intervalos de chaves usados tanto por chaves de entidades quanto por índices.

Em alguns casos, um hotspot do Cloud Datastore pode causar um impacto maior em um aplicativo do que somente impedir leituras ou gravações em um intervalo pequenos de chaves. Por exemplo, as chaves mais acessadas podem passar por leituras ou gravações durante a inicialização da instância, causando falha nas solicitações de carregamento.

Por padrão, o Cloud Datastore aloca as chaves usando um algoritmo disperso. Assim, normalmente não ocorre hotspotting nas gravações no Cloud Datastore quando entidades novas são criadas a uma alta taxa de gravação usando a política padrão de alocação de códigos. Há alguns casos especiais que podem ocasionar esse problema:

  • Na criação de entidades novas a uma taxa muito alta usando a política legada de alocação de códigos sequenciais.

  • Na criação de entidades novas a uma taxa muito alta com alocação de códigos próprios monotonicamente crescentes.

  • Na criação de entidades novas a uma taxa muito alta para um tipo que tinha pouquíssimas entidades anteriormente. O Bigtable iniciará com todas as entidades no mesmo servidor de blocos e demorará um pouco para dividir o intervalo de chaves em servidores de blocos diferentes.

  • Esse problema também ocorre na criação de entidades novas a uma taxa alta com propriedades indexadas de forma monotonicamente crescente, como o carimbo de data/hora. O motivo é que essas propriedades são as chaves das colunas nas tabelas de índice do Bigtable.

  • O Cloud Datastore insere o namespace e o tipo do grupo de entidades raiz antes da chave de linha do Bigtable. Você poderá encontrar um hotspot se começar uma gravação em um novo namespace ou tipo sem intensificar o tráfego gradualmente.

Se tiver uma chave ou propriedade indexada que será monotonicamente crescente, será possível antecedê-la com um hash aleatório para que as chaves sejam fragmentadas em vários blocos.

Da mesma forma, se você precisar consultar uma propriedade monotonicamente crescente (ou decrescente) usando um tipo ou filtro, será possível, como alternativa, indexar uma nova propriedade em que o valor monotônico seja prefixado com um valor de alta cardinalidade em todo o conjunto de dados, mas que seja comum a todas as entidades no escopo da consulta que quer executar. Por exemplo, para consultar entradas pelo carimbo de data/hora para um único usuário por vez, prefixe o carimbo de data/hora com o código do usuário e indexe essa nova propriedade. Isso ainda permite consultas e resultados ordenados para esse usuário, mas a presença do código do usuário garante que o índice em si seja bem fragmentado.

Para uma explicação mais detalhada sobre esse problema, consulte a postagem no blog de Ikai Lan sobre como salvar valores monotonicamente crescentes no Cloud Datastore.

Como intensificar o tráfego

Intensifique o tráfego gradualmente para os novos tipos ou partes do keyspace no Cloud Datastore.

Intensifique o tráfego gradualmente para novos tipos do Cloud Datastore de forma que o Bigtable tenha tempo suficiente para dividir os blocos à medida que o tráfego aumenta. Recomendamos realizar no máximo 500 operações por segundo em um tipo novo de Cloud Datastore e, depois, aumentar o tráfego em 50% a cada cinco minutos. Teoricamente, seguindo essa programação de intensificação do tráfego, é possível aumentar para até 740 mil operações por segundo após 90 minutos. Confira se as gravações estão distribuídas relativamente de forma uniforme por todo o intervalo de chaves. Nossos SREs chamam isso de regra dos "500/50/5".

Esse padrão de intensificação gradual do tráfego é muito importante ao alterar o código para deixar de usar o tipo A e usar o tipo B. Uma forma simples de processar essa migração é alterar o código para ler o tipo B e, caso ele não exista, ler o tipo A. No entanto, isso pode causar um aumento repentino no tráfego de um tipo novo que utiliza uma parte muito pequena do keyspace. É possível que o Bigtable não consiga dividir os blocos com eficiência se o keyspace for esparso.

O mesmo problema poderá ocorrer ao migrar as entidades para usar um intervalo de chaves diferente dentro do mesmo tipo.

A estratégia usada para migrar as entidades para um novo tipo ou chave dependerá do modelo de dados. Veja abaixo um exemplo de estratégia de migração, conhecida como "leituras paralelas". Você precisará determinar se essa estratégia é eficaz para os seus dados. Um ponto importante a ser levado em consideração é o impacto no custo das operações paralelas durante a migração.

Primeiro, faça a leitura de uma entidade ou chave antiga. Se não houver uma, faça a leitura de uma entidade ou chave nova. Uma taxa alta de leituras de entidades inexistentes pode provocar o hotspotting. Portanto, é preciso ter certeza para aumentar a carga gradualmente. Uma estratégia melhor é copiar a entidade antiga para a nova e excluir a antiga em seguida. Aumente gradualmente as leituras paralelas para que o novo keyspace seja bem dividido.

Uma estratégia possível para aumentar gradualmente as leituras ou gravações em um tipo novo é usar um hash determinista do código do usuário e, assim, conseguir uma porcentagem aleatória de usuários que realizam gravações em entidades novas. Verifique se o resultado do hash de código do usuário não foi distorcido pela função aleatória ou comportamento do usuário.

Enquanto isso, execute um job do Dataflow para copiar todos os dados das entidades ou chaves antigas para as novas. O job em lote precisa evitar gravações em chaves sequenciais para prevenir a ocorrência de hotspots no Bigtable. Quando o job em lote estiver concluído, só será possível realizar leituras no local novo.

Uma maneira de refinar essa estratégia é fazer a migração em lotes pequenos de usuários de uma só vez. Adicione um campo à entidade que rastreia a migração de cada usuário. Selecione um lote de usuários a serem migrados com base no hash do código do usuário. Um job do MapReduce ou do Dataflow migrará as chaves desse lote. Os usuários que estão em migração usarão leituras paralelas.

Observe que não é fácil fazer o rollback, a menos que você realize gravações duplas das entidades antigas e novas durante a fase de migração. No entanto, isso aumentará os custos incorridos do Cloud Datastore.

Exclusões

Evite excluir um grande número de entidades do Cloud Datastore em um intervalo pequeno de chaves.

O Bigtable periodicamente regrava as tabelas para remover as entradas excluídas e reorganizar os dados. Assim, as leituras e gravações ficam mais eficientes. Esse processo é conhecido como compactação.

Se você excluir um grande número de entidades do Cloud Datastore em um intervalo pequeno de chaves, as consultas realizadas nessa parte do índice serão mais lentas até que a compactação esteja concluída. Em casos extremos, as consultas podem atingir o tempo limite antes de retornar os resultados.

Usar um valor de carimbo de data/hora de um campo indexado para representar a data de expiração da entidade é um antipadrão. Para recuperar as entidades expiradas, é necessário realizar uma consulta no campo indexado que, provavelmente, está em uma parte sobreposta do keyspace com as entradas do índice das entidades excluídas mais recentemente.

É possível melhorar o desempenho com "consultas fragmentadas" que colocam uma string de comprimento fixo antes do carimbo de data/hora de expiração. O índice é classificado na string completa. Portanto, as entidades no mesmo carimbo de data/hora serão localizadas ao longo do intervalo de chaves do índice. Execute várias consultas em paralelo para buscar os resultados de cada fragmento.

Uma solução mais completa para o problema do carimbo de data/hora de expiração é usar um "número de geração", um contador global que é atualizado periodicamente. O número de geração antecede o carimbo de data/hora de expiração para que as consultas sejam classificadas primeiro por esse número, depois pelo fragmento e, por fim, pelo carimbo de data/hora. A exclusão de entidades antigas ocorre em uma geração anterior. Todas as entidades que não foram excluídas precisam ter o número de geração incrementado. Após a exclusão, você avança para a próxima geração. As consultas realizadas em uma geração mais antiga terão um desempenho ruim até que a compactação esteja concluída. Talvez seja necessário aguardar até que várias gerações sejam concluídas antes de consultar o índice para conseguir a lista de entidades a serem excluídas. Isso reduz o risco de perder resultados devido à consistência eventual.

Fragmentação e replicação

Use a fragmentação ou a replicação nas chaves do Cloud Datastore mais usadas.

Use a replicação se precisar realizar leituras em uma parte do intervalo de chaves a uma taxa acima da permitida pelo Bigtable. Ao usar essa estratégia, você armazenará “n” cópias da mesma entidade, permitindo uma taxa de leituras “n” vezes mais elevada do que a compatível para uma única entidade.

Use a fragmentação se precisar gravar em uma parte do intervalo de chaves com uma taxa maior do que a permitida pelo Bigtable. A fragmentação divide uma entidade em pedaços menores. Os princípios estão explicados no artigo sobre contadores de fragmentação.

Alguns erros comuns ao fragmentar:

  • Fragmentar usando um prefixo de tempo. Quando o tempo passa para o próximo prefixo, a nova parte não dividida se torna um hotspot. Em vez disso, passe gradualmente uma parte das gravações para o prefixo novo.

  • Fragmentar apenas as entidades mais usadas. Se você fragmentar uma pequena proporção do número total de entidades, não haverá linhas suficientes entre as entidades mais usadas para garantir que elas fiquem em partes divididas diferentes.

A seguir

Esta página foi útil? Conte sua opinião sobre:

Enviar comentários sobre…

Documentação do Cloud Datastore