Como projetar seu esquema

Nesta página, você encontra informações sobre o design do esquema do Cloud Bigtable. Antes de ler esta página, familiarize-se com a visão geral do Bigtable. Os tópicos a seguir são abordados nesta página:

  • Conceitos gerais: conceitos básicos a serem considerados ao projetar seu banco de dados.
  • Práticas recomendadas: orientações de design que se aplicam à maioria dos casos de uso, divididas por componente de tabela.
  • Casos de uso especiais: recomendações para alguns casos de uso e padrões de dados específicos.

Conceitos gerais

Projetar um esquema do Bigtable é diferente de projetar um esquema para um banco de dados relacional. No Bigtable, um esquema é um blueprint ou modelo de uma tabela, incluindo a estrutura dos componentes de tabela a seguir:

  • Chaves de linha
  • Grupos de colunas, inclusive as políticas de coleta de lixo deles
  • Colunas

Os conceitos gerais a seguir se aplicam ao design do esquema do Bigtable:

  • O Bigtable é um armazenamento de chave/valor, não um armazenamento relacional. Ela não é compatível com mesclagens e as transações são compatíveis apenas com uma única linha.
  • Cada tabela tem apenas um índice: a chave de linha. Não há índices secundários. Cada chave de linha precisa ser única.
  • As linhas são classificadas lexicograficamente por chave de linha, da string que tem menos bytes para a que tem mais. As chaves de linha são classificadas em ordem de bytes big-endian (às vezes chamada de ordem de bytes de rede), o equivalente binário da ordem alfabética.
  • Os grupos de colunas não são armazenados em nenhuma ordem específica.
  • As colunas são reunidas por grupo e classificadas em ordem lexicográfica dentro desse grupo. Por exemplo, em um grupo de colunas chamado SysMonitor com qualificadores de coluna de ProcessName, User, %CPU, ID, Memory, DiskRead e Priority, o Bigtable armazena as colunas nesta ordem:
SysMonitor
%CPU DiskRead ID Memória Prioridade ProcessName Usuário
  • A interseção de uma linha e coluna pode conter várias células com carimbo de data/hora. Cada célula contém uma versão exclusiva e com carimbo de data/hora dos dados para essa linha e coluna.
  • Todas as operações são atômicas no nível da linha. Isso significa que uma operação afeta uma linha inteira ou nenhuma da linha.
  • O ideal é que leituras e gravações sejam distribuídas por igual pelo espaço da linha de uma tabela.

  • As tabelas do Bigtable são esparsas. Uma coluna não ocupa espaço em uma linha que não usa a coluna.

Práticas recomendadas

Um esquema bom resulta em excelente desempenho e escalonabilidade, e um esquema ruim pode levar a um sistema de baixo desempenho. Cada caso de uso é diferente e requer um design próprio, mas as práticas recomendadas a seguir se aplicam à maioria dos casos de uso. Exceções são observadas.

Começando no nível da tabela até o nível da chave de linha, as seções a seguir descrevem as práticas recomendadas para o design do esquema:

Todos os elementos da tabela, especialmente as chaves de linha, devem ser projetados tendo em mente as solicitações de leitura planejadas. Consulte cotas e limites para ver os limites e tamanho rígido recomendados para todos os elementos da tabela.

Tabelas

Armazenar conjuntos de dados com esquemas semelhantes na mesma tabela, em vez de em tabelas separadas.

Em outros sistemas de banco de dados, é possível por armazenar dados em várias tabelas com base no assunto e no número de colunas. No entanto, no Bigtable, geralmente é melhor armazenar todos os dados em uma tabela grande. É possível atribuir um prefixo exclusivo de chave de linha a ser usado em cada conjunto de dados, para que o Bigtable armazene os dados relacionados em um intervalo contíguo de linhas que podem ser consultados por prefixo de chave de linha.

O Bigtable tem um limite de 1.000 tabelas por instância, mas, na maioria dos casos, você precisa ter bem menos tabelas que isso. Criar várias tabelas pequenas é um antipadrão do Bigtable por alguns motivos:

  • Enviar solicitações para muitas tabelas diferentes pode aumentar a sobrecarga da conexão de back-end, o que resulta em aumento da "tail latency".
  • Ter várias tabelas de tamanhos diferentes pode interromper o balanceamento de carga em segundo plano que faz com que o Bigtable funcione bem.

Com razão, talvez você precisa de uma tabela separada para um caso de uso completamente diferente que requer um esquema distinto, mas não use tabelas separadas para dados semelhantes. Por exemplo, não crie uma nova tabela por causa de um novo ano ou um novo cliente.

Grupos de colunas

Coloque colunas relacionadas no mesmo grupo de colunas. Quando uma linha contém vários valores relacionados entre si, é recomendável agrupar as colunas que contenham esses valores no mesmo grupo de colunas. Agrupe os dados o mais próximo possível para evitar a necessidade de projetar filtros complexos. Assim, você recebe apenas as informações necessárias, e não mais, nas solicitações de leitura mais frequentes.

Crie até 100 grupos de colunas por tabela. A criação de mais de 100 grupos de colunas pode prejudicar o desempenho.

Escolha nomes curtos, mas significativos, para os grupos de colunas. Os nomes são incluídos nos dados transferidos para cada solicitação.

Coloque colunas que tenham diferentes necessidades de retenção de dados em diferentes grupos de colunas. Isso é importante se você quiser limitar os custos de armazenamento. As políticas de coleta de lixo são definidas no nível do grupo de colunas, não no nível da coluna. Por exemplo, se você precisa manter apenas a versão mais recente de um determinado dado, não armazene-o em um grupo de colunas definido para armazenar 1.000 versões de outra coisa. Caso contrário, você está pagando para armazenar 999 células de dados desnecessários.

Colunas

Trate os qualificadores de coluna como dados. Como é preciso armazenar um qualificador de coluna para cada coluna, você pode economizar espaço nomeando-a com um valor. Como um exemplo, pense em uma tabela que armazena dados sobre amigos, em que cada linha representa uma pessoa e todos os seus amigos. Cada qualificador de coluna pode ser o ID de um amigo e o valor dessa coluna nessa linha pode ser o círculo social que o amigo está. Neste exemplo, as linhas podem ter a seguinte aparência:

José Fred:clube do livro Gabriel:trabalho Hiroshi:tênis
Sófia Hiroshi:trabalho Seo Yoon:escola Jakob:clube de xadrez

Compare isso com um esquema para os mesmos dados que não usam qualificadores de coluna como dados:

José#1 Amigo:Fred Círculo:clube do livro
José#2 Amigo:Gabriel Círculo:trabalho
José#3 Amigo:Hiroshi Círculo:tênis
Sófia#1 Amigo:Hiroshi Círculo:trabalho
Sófia#2 Amigo:Seo Yoon Círculo:escola
Sófia# Amigo:Jakob Círculo:clube de xadrez

Com o segundo design do esquema, a tabela cresce muito mais rápido.

Se você não estiver usando qualificadores de coluna para armazenar dados e quiser reduzir a quantidade de dados transferidos para cada solicitação, forneça qualificadores de coluna curtos, mas significativos. O tamanho máximo é de 16 KB.

Crie quantas colunas forem necessárias na tabela. As tabelas do Bigtable são esparsas e não há penalidade de espaço para uma coluna que não é usada em uma linha. É possível ter milhões de colunas em uma tabela, desde que nenhuma linha exceda o limite máximo de 256 MB por linha.

Evite usar muitas colunas em qualquer linha única. Embora uma tabela possa ter milhões de colunas, uma linha não pode. Alguns fatores influenciam essa prática recomendada:

  • Leva algum tempo para que o Bigtable processe todas as células de uma linha.
  • Cada célula aumenta a quantidade de dados armazenados na tabela e enviados pela rede. Por exemplo, se você estiver armazenando 1 KB (1.024 bytes) de dados, será muito mais eficiente armazenar esses dados em uma única célula em vez de distribuí-los por 1.024 células, cada uma contendo 1 byte.

Se o conjunto de dados exigir logicamente mais colunas por linha do que o Bigtable pode processar com eficiência, considere armazenar os dados como um protobuf em uma única coluna.

Linhas

Não armazene mais de 100 MB de dados em uma única linha. Linhas que excedem esse limite podem resultar em um desempenho de leitura reduzido.

Mantenha todas as informações de uma entidade em uma única linha. Na maioria dos casos, evite armazenar os dados que você precisa ler atomicamente, ou todos de uma vez, em mais de uma linha para evitar inconsistências. Por exemplo, se você atualizar duas linhas em uma tabela, possivelmente uma linha será atualizada com êxito, e a outra falhará. Verifique se o esquema não exige que mais de uma linha seja atualizada ao mesmo tempo, para que os dados relacionados sejam precisos. Isso garante que, se parte de uma solicitação de gravação falhar ou precisar ser enviada novamente, esse dado não estará temporariamente incompleto.

Exceção: se a manutenção de uma entidade em uma única linha resultar em linhas de centenas de MB, divida os dados em várias linhas.

Armazene entidades relacionadas em linhas adjacentes, para tornar as leituras mais eficientes.

Célula

Não armazene mais de 10 MB de dados em uma única célula. Lembre-se de que uma célula contém os dados armazenados para uma determinada linha e coluna com um carimbo de data/hora exclusivo e que várias células podem ser armazenadas na interseção dessa linha e coluna. O número de células retidas em uma coluna é determinado pela política de coleta de lixo definida para o grupo de colunas da coluna.

Chaves de linha

Crie a chave de linha com base nas consultas que você usará para recuperar os dados. Chaves de linha bem projetadas oferecem o melhor desempenho do Bigtable. As consultas mais eficientes do Bigtable recuperam dados usando uma das seguintes opções:

  • Chave de linha
  • Prefixo da chave de linha
  • Intervalo de linhas definidas pelas chaves de linha inicial e final

Outros tipos de consultas acionam a verificação total da tabela, o que é bem menos eficiente. Se você escolher a chave de linha correta agora, evitará um complicado processo de migração de dados mais tarde.

Mantenha suas chaves de linha curtas. Uma chave de linha precisa ter 4 KB. ou menos Chaves de linha compridas ocupam um espaço adicional de memória e armazenamento, além de aumentarem o tempo necessário para receber as respostas do servidor do Bigtable.

Armazene vários valores delimitados em cada chave de linha. Como a melhor maneira de consultar o Bigtable com eficiência é por chave de linha, geralmente é útil incluir vários identificadores na chave de linha. Quando a chave de linha contém vários valores, é importante saber como os dados serão usados.

Os segmentos de chave de linha geralmente são separados por um delimitador, como dois pontos, barra ou símbolo de hash. O primeiro segmento ou conjunto de segmentos contíguos é o prefixo da chave de linha e o último segmento ou conjunto de segmentos contíguos é o sufixo da chave de linha.

Chave de linha de amostra

Os prefixos de chave de linha bem planejados permitem aproveitar a ordem de classificação integrada do Bigtable para armazenar dados relacionados em linhas contíguas. O armazenamento de dados relacionados em linhas contíguas permite acessar dados relacionados como um intervalo de linhas, em vez de executar varreduras de tabela ineficientes.

Se os dados incluem números inteiros que você quer armazenar ou classificar numericamente, preencha os números inteiros com zeros à esquerda. O Bigtable armazena dados lexicograficamente. Por exemplo, lexicograficamente, 3 > 20, mas 20 > 03. Preencher os três com um zero à esquerda garante que os números sejam classificados numericamente. Essa tática é importante para carimbos de data/hora, em que as consultas com base em intervalo são desejadas.

É importante criar uma chave de linha que permita recuperar um intervalo de linhas bem definido. Caso contrário, sua consulta requer uma verificação de tabela, que é muito mais lenta do que recuperar linhas específicas.

Por exemplo, caso seu aplicativo rastreie dados de dispositivos móveis, é possível ter uma chave de linha que consiste no tipo de dispositivo, no ID do dispositivo e no dia em que os dados são gravados. As chaves de linha desses dados podem ter a seguinte aparência:

        phone#4c410523#20200501
        phone#4c410523#20200502
        tablet#a0b81f74#20200501
        tablet#a0b81f74#20200502

Esse design de chave de linha permite recuperar dados com uma única solicitação para:

  • Um tipo de dispositivo
  • Uma combinação de ID e tipo de dispositivo

Este design da chave de linha não seria ideal se você quiser recuperar todos os dados de um determinado dia. Como o dia é armazenado no terceiro segmento ou no sufixo da chave de linha, não é possível solicitar um intervalo de linhas com base no sufixo ou em um segmento do meio da chave de linha. Em vez disso, você precisa enviar uma solicitação de leitura com um filtro que verifica toda a tabela em busca do valor do dia.

Use valores de string legíveis nas chaves de linha sempre que possível. Essa prática facilita o uso da ferramenta Key Visualizer para resolver problemas com o Bigtable.

Em muitos casos, é recomendável projetar chaves de linha que comecem com um valor comum e terminem com um valor granular. Por exemplo, se a chave de linha incluir um continente, um país e uma cidade, será possível criar chaves de linha que tenham a seguinte aparência, para que elas sejam classificadas automaticamente primeiro por valores com menor cardinalidade:

        asia#india#bangalore
        asia#india#mumbai
        asia#japan#okinawa
        asia#japan#sapporo
        southamerica#bolivia#cochabamba
        southamerica#bolivia#lapaz
        southamerica#chile#santiago
        southamerica#chile#temuco

Chaves de linha a ser evitadas

Alguns tipos de chaves de linha podem dificultar a consulta dos dados e alguns resultam em um desempenho insatisfatório. Esta seção descreve alguns tipos de chave de linha que você deve evitar no Bigtable.

Chaves de linha que começam com um carimbo de data/hora. Isso fará com que as gravações sequenciais sejam enviadas para um único nó, criando um ponto de acesso. Se você incluir um carimbo de data/hora em uma chave de linha, será necessário precedê-lo com um valor de alta cardinalidade, como um ID de usuário, para evitar o uso excessivo do ponto de acesso.

Chaves de linha que impedem o agrupamento de dados relacionados. Evite chaves de linha que façam com que os dados relacionados sejam armazenados em intervalos de linhas não contíguos, que são ineficientes na leitura conjunta.

Códigos numéricos sequenciais. Suponha que seu sistema atribua um código numérico para cada usuário do seu aplicativo. Provavelmente, você ficará tentado a usar o código numérico do usuário como chave de linha na sua tabela. No entanto, como é mais provável que os usuários novos sejam mais ativos, essa abordagem poderá empurrar a maior parte do tráfego para um número pequeno de nós.

Uma abordagem mais segura é usar uma versão invertida do ID numérico do usuário, que distribui o tráfego de maneira mais uniforme em todos os nós da tabela do Bigtable.

Identificadores atualizados com frequência. Evite usar uma única chave de linha para identificar um valor que é atualizado com muita frequência. Por exemplo, se você armazenar dados de uso de memória para vários dispositivos uma vez por segundo, não use uma chave de linha única para cada dispositivo, que é composto do ID do dispositivo. e a métrica armazenada, como 4c410523#memusage e atualize a linha várias vezes. Esse tipo de operação sobrecarrega o bloco que armazena a linha usada com muita frequência. Isso também pode fazer com que uma linha exceda o limite de tamanho, já que os valores anteriores de uma coluna ocupam espaço até que as células sejam removidas na coleta de lixo.

Em vez disso, armazene cada nova leitura em uma nova linha. Usando o exemplo de uso de memória, cada chave de linha pode conter o ID do dispositivo, o tipo de métrica e um carimbo de data/hora. Portanto, as chaves de linha são semelhantes a 4c410523#memusage#1423523569918. Essa é uma estratégia eficiente porque criar uma linha nova no Bigtable é mais rápido que criar uma célula nova. Além disso, é possível realizar a leitura dos dados rapidamente a partir de um período específico calculando as chaves inicial e final adequadas.

Com relação aos valores que alteram com muita frequência, como um contador que é atualizado centenas de vezes por minuto, a prática recomendada é simplesmente manter os dados na memória, na camada do aplicativo, e gravar linhas novas periodicamente no Bigtable.

Valores em hash. A geração de hash de uma chave de linha impede que você aproveite a ordem de classificação natural do Bigtable, impossibilitando o armazenamento de linhas de uma maneira ideal para consulta. Pelo mesmo motivo, o uso de valores de hash dificulta o uso da ferramenta Key Visualizer para resolver problemas com o Bigtable. Use valores legíveis em vez de valores em hash.

Valores expressos como bytes brutos em vez de strings legíveis. Bytes brutos são adequados para valores de coluna, mas, para legibilidade e solução de problemas, use valores de string em chaves de linha.

Casos de uso especiais

Você pode ter um conjunto de dados exclusivo que requer consideração especial ao projetar um esquema para armazená-lo no Bigtable. Nesta seção, descrevemos alguns, mas não todos, tipos diferentes de dados do Bigtable e algumas táticas sugeridas para armazená-los da maneira ideal.

Dados baseados no tempo

Incluir um carimbo de data/hora como parte da sua chave de linha se você precisar recuperar dados com base no momento em que eles foram gravados.

Por exemplo, digamos que o seu aplicativo precise gravar, a cada segundo, dados relacionados ao desempenho de um grande número de máquinas, como utilização de CPU e memória. A chave de linha desses dados poderia combinar um identificador para a máquina com um carimbo de data/hora dos dados (por exemplo, machine_4223421#1425330757685). Lembre-se de que as chaves de linha são classificadas lexicograficamente.

Não use um carimbo de data/hora sozinho ou no início de uma chave de linha, porque isso fará com que as gravações sequenciais sejam enviadas para um único nó, criando um ponto de acesso.

Se você normalmente recupera primeiro os registros mais recentes, é possível usar um carimbo de data/hora invertido na chave de linha subtraindo o carimbo de data/hora do valor máximo da linguagem de programação para números inteiros longos (em Java, java.lang.Long.MAX_VALUE). Com o carimbo invertido, os registros serão ordenados do mais recente para o mais antigo.

Para informações específicas sobre como trabalhar com dados de séries temporais, consulte Projeto de esquema para dados de séries temporais.

Multilocação

Os prefixos de chave de linha fornecem uma solução escalonável para um caso de uso de "multilocação", um cenário em que você armazena dados semelhantes, usando o mesmo modelo de dados, em nome de vários clientes. Usar uma tabela para todos os locatários é a maneira mais eficiente de armazenar e acessar dados de vários deles.

Por exemplo, digamos que você armazene e rastreie históricos de compras em nome de muitas empresas. É possível usar seu ID exclusivo para cada empresa como um prefixo de chave de linha. Todos os dados de um locatário são armazenados em linhas contíguas na mesma tabela e você pode consultar ou filtrar usando o prefixo da chave de linha. Então, quando uma empresa não for mais seu cliente e você precisar excluir os dados do histórico de compras que estavam armazenados para a empresa, será possível descartar o intervalo de linhas que usam esse prefixo.

Por exemplo, se você estiver armazenando dados de celular para os clientes altostrat e examplepetstore, será possível criar chaves de linha como as seguintes. Em seguida, se a altostrat não for mais sua cliente, você descartará todas as linhas com o prefixo de chave de linha altostrat.

        altostrat#phone#4c410523#20190501
        altostrat#phone#4c410523#20190502
        altostrat#tablet#a0b41f74#20190501
        examplepetstore#phone#4c410523#20190502
        examplepetstore#tablet#a6b81f79#20190501
        examplepetstore#tablet#a0b81f79#20190502

Por outro lado, se você armazenar dados em nome de cada empresa em sua própria tabela, poderá enfrentar problemas de desempenho e escalonabilidade. Também é provável que você, sem perceber, alcance o limite do Bigtable de 1.000 tabelas por instância. Depois que uma instância atinge esse limite, o Bigtable impede que você crie mais tabelas nela.

Privacidade

A menos que seu caso de uso exija, evite usar informações de identificação pessoal (PII, na sigla em inglês) ou dados do usuário em chaves de linha ou IDs de grupos de colunas. As chaves de linha e os grupos de colunas são dados e metadados e os aplicativos que os usam como metadados, como criptografia ou geração de registros, podem expô-los inadvertidamente a usuários que não devem ter acesso a dados privados.

Nomes de domínio

Diversos nomes de domínio

Se você estiver armazenando dados sobre entidades que podem ser representadas como nomes de domínio, use um nome de domínio reverso (por exemplo, com.company.product) como a chave de linha. Usá-lo assim é uma ótima ideia, principalmente se os dados de cada linha se sobrepuserem às linhas adjacentes. Nesse caso, o Bigtable será mais eficiente ao compactar os dados.

Por outro lado, nomes de domínio padrão não revertidos podem fazer com que as linhas sejam classificadas de uma forma que os dados relacionados não sejam agrupados em um só lugar, o que pode resultar em uma compactação menos eficiente e leituras menos eficientes.

Essa abordagem funciona melhor quando os dados estão espalhados por muitos nomes de domínio inversos diferentes.

Para ilustrar este ponto, considere os seguintes nomes de domínio, classificados automaticamente em ordem lexicográfica pelo Bigtable:

      drive.google.com
      en.wikipedia.org
      maps.google.com

Isso não é desejável para o caso de uso em que você quer consultar todas as linhas de google.com. Por outro lado, considere as mesmas linhas em que os nomes de domínio foram revertidos:

      com.google.drive
      com.google.maps
      org.wikipedia.en

No segundo exemplo, as linhas relacionadas são classificadas automaticamente de uma maneira que facilita a recuperação delas como um intervalo de linhas.

Poucos nomes de domínio

Se você espera armazenar muitos dados apenas para um ou um pequeno número de nomes de domínio, considere outros valores para a chave de linha. Caso contrário, é possível enviar gravações para um único nó no cluster, o que resulta em uso excessivo do ponto de acesso ou as linhas podem ficar muito grandes.

Alterações ou consultas incertas

Se você nem sempre executa as mesmas consultas nos dados ou se não tem certeza de quais serão suas consultas, uma opção é armazenar todos os dados de uma linha em uma coluna, em vez de várias colunas. Com essa abordagem, você usa um formato que facilita a extração dos valores individuais posteriormente, como o formato binário de buffer de protocolo ou json.

A chave de linha ainda é cuidadosamente projetada para garantir que você possa recuperar os dados necessários, mas cada linha normalmente tem apenas uma coluna que contém todos os dados da linha em um único protobuf.

Armazenar dados como uma mensagem protobuf em uma coluna, em vez de espalhar os dados em várias colunas, oferece vantagens e desvantagens. As vantagens incluem o seguinte:

  • Os dados ocupam menos espaço, por isso você paga menos para armazená-los.
  • Você mantém certa flexibilidade ao não se comprometer com grupos de colunas e qualificadores de coluna.
  • Seu aplicativo de leitura não precisa "saber" qual é o esquema da tabela.

Veja a seguir algumas desvantagens:

  • É necessário desserializar as mensagens protobuf depois que elas forem lidas no Bigtable.
  • Você perde a opção de consultar os dados em mensagens protobuf usando filtros.
  • Não é possível usar o BigQuery para executar consultas federadas em campos em mensagens protobuf depois de lê-las no Bigtable.

A seguir