Projeto de esquema para dados de séries temporais

Esta página descreve conceitos, padrões e exemplos de design de esquema para armazenar dados de séries temporais no Cloud Bigtable. Antes de ler esta página, familiarize-se com a visão geral do Cloud Bigtable. Além disso, familiarize-se também com o projeto do seu esquema.

Sempre que você mede algo e registra o tempo com a medição, está construindo uma série temporal. As séries temporais estão por toda a parte:

  • Quando você olha para o gráfico de uso de memória no computador porque ele está lento, está olhando para uma série temporal.

  • Quando você olha para a temperatura ao longo do tempo em uma reportagem, está olhando para uma série temporal.

  • Se você é um operador de câmbio e seu trabalho envolve traçar preços médios móveis de 5, 10 e 30 dias para USD/JPY, está olhando para séries temporais.

As séries temporais também são incrivelmente importantes:

  • Elas nos ajudam a otimizar o uso de recursos, diminuir o uso de energia, minimizar o impacto ambiental e reduzir custos.

  • As séries temporais nos ajudam a identificar tendências em dados, permitindo-nos demonstrar concretamente o que aconteceu no passado e fazer estimativas informadas sobre o que acontecerá no futuro.

  • As séries temporais sustentam algumas das complexas análises e aprendizado de máquinas em áreas como serviços financeiros, varejo, seguros, física e química.

Este guia oferece estratégias detalhadas e um tutorial para armazenar e consultar dados de séries temporais no Cloud Bigtable.

Séries temporais e o Cloud Bigtable

O armazenamento de dados de séries temporais no Cloud Bigtable é algo natural. O Cloud Bigtable armazena dados como colunas não estruturadas em linhas. Cada linha tem uma chave de linha, e as chaves de linha são classificadas de maneira lexicográfica.

Há duas maneiras normalmente usadas para recuperar dados do Cloud Bigtable:

  • Você pode conseguir uma linha simples ao especificar a chave de linha.
  • Você pode conseguir várias linhas ao especificar um intervalo de chaves de linha.

Esses métodos são ideais para consultar dados de séries temporais porque, muitas vezes, você quer dados para um determinado intervalo de tempo. Por exemplo: todos os dados de mercado do dia ou estatísticas de CPU do servidor dos últimos 15 minutos. Como resultado, o Cloud Bigtable é funcionalmente uma ótima escolha para séries temporais.

Claro, os detalhes podem ser traiçoeiros. No Cloud Bigtable, é preciso atenção com o esquema para os dados (as colunas e a estrutura da chave de linha), que precisa ser projetado com cuidado. Um esquema bom resulta em excelente desempenho e escalabilidade, e um esquema ruim pode levar a um sistema de baixo desempenho. No entanto, não há um projeto de esquema único que seja o melhor para todos os casos de uso.

O restante deste artigo apresenta uma série de padrões para o projeto de esquema no Cloud Bigtable. Use esses padrões para projetar um esquema ideal para seu caso de uso. Depois de enumerar e explicar os padrões para o projeto do esquema, é possível aprender com exemplos para os seguintes casos de uso:

  • dados do mercado financeiro
  • métricas de servidor (por exemplo, uso de CPU, memória e rede)
  • Medidores inteligentes de energia (parte da “Internet das Coisas”, ou IoT, na sigla em inglês)

Padrões de projeto de esquema para séries temporais

Os padrões de projeto de esquema para armazenamento de séries temporais no Cloud Bigtable se encaixam nas seguintes categorias:

  • padrões gerais
  • padrões para projeto de chave de linha
  • padrões para projeto de coluna de dados

Padrões gerais

Manter nomes curtos, mas significativos

Ao transferir dados do Cloud Bigtable, você também está transferindo metadados, que incluem:

  • a chave de linha;
  • o grupo de colunas, um identificador usado para agrupar colunas relacionadas;
  • o qualificador de colunas, um nome exclusivo dentro de um grupo de colunas.

Como resultado, é importante escolher nomes significativos que também sejam o mais curtos possível, porque o tamanho de cada nome contribui para o armazenamento e a sobrecarga de RPC. Por exemplo, em vez de usar CELLPHONE_NUMBER como um qualificador de coluna, você pode usar CELL como uma abreviatura curta, mas significativa.

Padrões para projeto de chave de linha

Usar tabelas altas e estreitas

Uma tabela alta e estreita tem um pequeno número de eventos por linha, o que pode ser apenas um evento, enquanto uma tabela curta e ampla tem um grande número de eventos por linha. Conforme será explicado daqui a pouco, tabelas altas e estreitas são mais adequadas para dados de séries temporais.

Por exemplo, suponha que você meça a temperatura em seu jardim toda manhã. Se você decidir que, por medir a temperatura todas as manhãs, uma linha por dia deva ser apropriada, sua tabela será alta e estreita. Observe que o carimbo de data/hora não é o primeiro elemento da chave de linha. Como explicado mais adiante, usar um carimbo de data/hora como o primeiro elemento de uma chave de linha pode causar vários problemas.

Chave de linha Dados da coluna
VEGGIEGARDEN#20150301 DAILY:TEMP:60.4
VEGGIEGARDEN#20150302 DAILY:TEMP:61.2
VEGGIEGARDEN#20150303 DAILY:TEMP:61.0
VEGGIEGARDEN#20150304 DAILY:TEMP:65.1
VEGGIEGARDEN#20150305 DAILY:TEMP:62.2
... ...
VEGGIEGARDEN#20150331 DAILY:TEMP:60.4

Por outro lado, suponha que você deseje traçar a temperatura de cada mês. Nesse caso, uma linha por mês será apropriada. O exemplo a seguir mostra a tabela curta e ampla que você consegue como resultado:

Chave de linha Dados da coluna
VEGGIEGARDEN#20150301 TEMP:1:60.4 TEMP:2:61.2 TEMP:3:61.0 TEMP:4:65.1 TEMP:5:62.2 ... TEMP:31:60.4

De modo geral, use tabelas altas e estreitas para séries temporais. Isso se deve a dois motivos: armazenar um evento por linha facilita a execução de consultas em relação aos dados. Armazenar muitos eventos por linha aumenta a chance de que o tamanho total da linha ultrapasse o máximo recomendado. Veja "Linhas podem ser grandes, mas não são infinitas".

Como uma otimização, é possível usar tabelas curtas e largas, mas evite números ilimitados de eventos. Por exemplo, se você geralmente precisa recuperar um mês inteiro de eventos de uma vez, a tabela de temperatura acima é uma otimização razoável, porque a linha é limitada em tamanho ao número de dias em um mês.

Preferir linhas a versões de coluna

No Cloud Bigtable, as colunas podem ter versões com carimbo de data/hora. Como resultado, teoricamente é possível armazenar uma série temporal como um conjunto de versões de uma coluna. Por exemplo, se você quiser registrar o preço de fechamento das ações ZXZZT a cada dia, poderá ter uma única coluna com uma versão com carimbo de data/hora para cada dia:

Chave de linha Dados da coluna
ZXZZT STOCK:PRICE (V1 03/01/15):558.40 STOCK:PRICE (V2 03/02/15):571.34 STOCK:PRICE (V3 03/03/15):573.64 STOCK:PRICE (V4 03/04/15):573.37 STOCK:PRICE (V5 03/05/15):575.33

No entanto, esta não é a melhor maneira de armazenar esses dados.

Por padrão, use novas linhas em vez de versões de coluna. O uso de várias linhas, com uma única versão de um evento em cada linha, é a maneira mais simples de representar, entender e consultar os dados.

É aceitável usar versões de uma coluna em que o caso de uso realmente esteja alterando um valor, e o histórico do valor é importante. Por exemplo, suponha que você tenha feito um conjunto de cálculos com base no preço de fechamento de ZXZZT e, inicialmente, os dados tenham sido inseridos por engano como 559,40 para o preço de fechamento, em vez de 558,40. Nesse caso, pode ser importante conhecer o histórico do valor, caso o valor incorreto tenha causado outros erros de cálculo.

Projetar a chave de linha com as consultas em mente

Quando o Cloud Bigtable armazena linhas, ele as classifica por chave de linha em ordem lexicográfica. Há efetivamente um único índice por tabela, que é a chave de linha. As consultas que acessam uma única linha ou um intervalo contíguo de linhas são executadas de maneira rápida e eficiente. Todas as outras consultas resultam em uma verificação completa da tabela, que será muito mais lenta. Uma verificação completa da tabela é exatamente o que parece: cada linha da tabela é examinada, uma por vez. Para o Cloud Bigtable, em que é possível armazenar muitos petabytes de dados em uma única tabela, o desempenho de uma verificação completa de tabela só vai piorar à medida que o sistema crescer.

Por exemplo, pense em uma tabela em que pontuações de jogadores de videogame são armazenadas, projetada da maneira a seguir.

Chave de linha Dados da coluna
LoL#20150301 GAME:PLAYER:Corrie GAME:WIN:false GAME:KDA:4.25
LoL#20150302 GAME:PLAYER:Jo GAME:WIN:true GAME:KDA:7.00
LoL#20150302 GAME:PLAYER:Sam GAME:WIN:true GAME:KDA:7.00
LoL#20150303 GAME:PLAYER:Corrie GAME:WIN:true GAME:KDA:9.50
Starcraft#20150303 GAME:PLAYER:Eriko GAME:WIN:true GAME:KDA:6.00

Suponha que você queira consultar esses dados para responder à pergunta “Quantos jogos de LoL Corrie venceu em março”?Com o esquema mostrado acima, será preciso verificar a maior parte da tabela para responder a essa pergunta. Por outro lado, se você criar a tabela da maneira especificada a seguir, poderá preencher essa consulta por meio da recuperação de um intervalo específico de chaves de linha:

Chave de linha Dados da coluna
LoL#Corrie#20150301 GAME:WIN:false GAME:KDA:4.25
LoL#Corrie#20150303 GAME:WIN:true GAME:KDA:9.50
LoL#Jo#20150302 GAME:WIN:true GAME:KDA:7.00
LoL#Sam#20150302 GAME:WIN:true GAME:KDA:7.00
Starcraft#Eriko#20150303 GAME:WIN:true GAME:KDA:6.00

A escolha de uma chave de linha que facilite consultas comuns é de suma importância para o desempenho geral do sistema. Enumere as consultas, coloque-as em ordem de importância e, em seguida, crie chaves de linha que funcionem para essas consultas.

Como você lida com uma situação em que não há uma chave de linha perfeita? Por exemplo, suponha que as consultas para todas as partidas de LoL em março e todas as partidas de Corrie no mesmo mês fossem igualmente importantes. O esquema acima nos permitiria consultar as partidas de Corrie de LoL em março, mas não nos ajudaria com todas as partidas de LoL em março. O melhor que você poderia fazer seria consultar todas as partidas de LoL e depois filtrar apenas março. Há duas formas de resolver esse problema:

Desnormalização

  • Use duas tabelas, cada uma com uma chave de linha apropriada para uma das consultas. Esta é uma boa solução, porque resulta em um sistema robusto e escalonável.

Consulta e filtro

  • Mantenha o esquema mostrado acima e faça uma consulta (todas as partidas de LoL em março) que apresente desempenho inferior, porque você está filtrando um grande número de linhas. Esta normalmente não é uma boa solução, porque resulta em um sistema menos escalonável que poderia facilmente ser deteriorado à medida que o uso aumentasse.

Garantir que a chave de linha evite o uso excessivo do ponto de acesso

O problema comum para a série temporal no Cloud Bigtable é o hotspotting. Ele pode afetar qualquer tipo de chave de linha em que haja um valor monotonicamente crescente.

Em resumo, quando uma chave de linha de uma série temporal inclui um carimbo de data/hora, todas as gravações vão procurar um único node, preencher esse node e depois seguir para o próximo node no cluster, causando o hotspotting. Por exemplo, se você estiver armazenando o status da bateria de um smartphone e a chave de linha consistir na palavra "BATERIA" mais um carimbo de data/hora (como mostrado abaixo), a chave de linha sempre aumentará em sequência. Como o Cloud Bigtable armazena chaves de linha adjacentes no mesmo node de servidor, todas as gravações se concentrarão somente em um node até que esse node fique cheio. Nesse momento, as gravações serão movidas para o próximo node no cluster.

Chave de linha Dados da coluna
BATTERY#20150301124501001 METRIC:USER:Corrie METRIC:PERCENTAGE:98
BATTERY#20150301124501002 METRIC:USER:Jo METRIC:PERCENTAGE:54
BATTERY#20150301124501003 METRIC:USER:Corrie METRIC:PERCENTAGE:96
BATTERY#20150301124501004 METRIC:USER:Sam METRIC:PERCENTAGE:43
BATTERY#20150301124501005 METRIC:USER:Sam METRIC:PERCENTAGE:38

Há algumas maneiras de resolver esse problema:

  • Promoção de campo. Mova os campos dos dados da coluna para a chave de linha para tornar as gravações não contíguas.

  • Sal. Adicione mais um elemento calculado à chave de linha para deixar as gravações não contíguas artificialmente.

Usar a ferramenta Key Visualizer pode ajudar na identificação de pontos de acesso e facilitar a solução de problemas com o Cloud Bigtable.

Promoção de campo

Neste exemplo, você promoverá USER a partir de uma coluna para um elemento da chave de linha. Essa alteração solucionaria o problema de hotspotting, porque os identificadores de usuário fornecerão uma distribuição mais uniforme das chaves de linha. Como resultado, as gravações serão divididas em vários nodes no cluster.

A vantagem da promoção de campo é que ela muitas vezes também deixa as consultas mais eficientes, o que faz dela a melhor estratégia. A (pequena) desvantagem é que as consultas são restringidas pelos campos promovidos, gerando trabalho em dobro se você não promover os campos certos.

Chave de linha Dados da coluna
BATTERY#Corrie#20150301124501001 METRIC:PERCENTAGE:98
BATTERY#Corrie#20150301124501003 METRIC:PERCENTAGE:96
BATTERY#Jo#20150301124501002 METRIC:PERCENTAGE:54
BATTERY#Sam#20150301124501004 METRIC:PERCENTAGE:43
BATTERY#Sam#20150301124501005 METRIC:PERCENTAGE:38

Sal

Neste exemplo, você vai pegar um hash do carimbo de data/hora e dividi-lo por três, pegar o restante desse cálculo e adicioná-lo à chave de linha. Por que por três? Neste caso, essa é uma estimativa do número de nodes no cluster e forneceria uma boa divisão de atividade entre esses nodes.

A vantagem do processo de sal é sua simplicidade, já que é essencialmente uma simples função de hash. Uma desvantagem é que, quando você consulta intervalos de tempo, tem que fazer várias verificações (uma por valor de sal) e combinar os resultados no seu próprio código. Outra desvantagem é que é difícil escolher um valor de sal que distribua a atividade entre os nodes e funcione bem conforme você amplia ou diminui o sistema. Por causa dessas desvantagens, e porque é melhor usar chaves de linha legíveis por humanos, evite o uso de sal a menos que você não consiga encontrar outra maneira de evitar o hotspotting.

Chave de linha Dados da coluna
BATTERY#1#20150301124501003 METRIC:USER:Jo METRIC:PERCENTAGE:96
BATTERY#1#20150301124501004 METRIC:USER:Sam METRIC:PERCENTAGE:43
BATTERY#2#20150301124501002 METRIC:USER: Corrie METRIC:PERCENTAGE:54
BATTERY#3#20150301124501005 METRIC:USER:Sam METRIC:PERCENTAGE:38
BATTERY#3#20150301124501001 METRIC:USER:Corrie METRIC:PERCENTAGE:98

Por padrão, prefira a promoção de campo. A promoção de campo evita o hotspotting em quase todos os casos e tende a facilitar a criação de uma chave de linha que deixa as consultas mais fáceis.

Use o sal somente quando a promoção de campo não resolver o hotspotting. No caso raro em que você aplicar uma função de sal, tome cuidado para não fazer muitas suposições sobre o tamanho subjacente do cluster. No exemplo acima, temos uma função de sal que supõe que há três nodes no cluster. Essa suposição é segura porque seria dimensionada para o número limitado de nodes que é possível haver em um cluster do Cloud Bigtable. Se você pudesse criar clusters com centenas de nodes, teria que usar uma função de sal diferente.

Reverter carimbos de data/hora somente quando necessário

Para reverter carimbos de data/hora, subtraia o carimbo de data/hora do valor máximo da linguagem de programação para números inteiros longos (como java.lang.Long.MAX_VALUE de Java). Ao reverter o carimbo de data/hora, você pode criar uma chave de linha em que o evento mais recente aparece no início da tabela, em vez de no fim. Como resultado, você pode conseguir os N eventos mais recentes simplesmente recuperando as primeiras N linhas da tabela.

Dê preferência à reversão dos carimbos de data/hora somente quando a consulta mais comum for para os valores mais recentes. Isso porque reverter os carimbos de data/hora deixa todas as outras consultas mais complexas, além de complicar o esquema geral.

Padrões para projeto de coluna de dados

Linhas podem ser grandes, mas não são infinitas

As linhas no Cloud Bigtable podem conter cerca de 100 grupos de colunas e milhões de colunas, com um limite de 100 MB em cada valor armazenado em uma coluna. Esses limites generosos oferecem grande flexibilidade. No entanto, você não deve pressupor que linhas grandes sejam a maneira correta de armazenar dados e que, portanto, seja preciso preencher cada linha com o máximo de dados possível. Tenha sempre em mente que recuperar valores grandes consome mais tempo e memória.

Em geral, mantenha os tamanhos das linhas abaixo de aproximadamente 100 MB. Isso é mais uma orientação do que uma regra, já que as linhas podem ter mais de 100 MB. No entanto, se você tiver muitas linhas maiores do que isso, poderá ter problemas de desempenho.

Em geral, mantenha valores de coluna abaixo de aproximadamente 10 MB. Novamente, isso é mais uma orientação do que uma regra, já que você pode armazenar alguns valores que são maiores do que 10 MB, mas eles podem causar problemas de desempenho.

Para reiterar, se você costuma contar com linhas grandes ou valores grandes individuais, haverá problemas de desempenho no sistema.

O Cloud Bigtable é um armazenamento de chave/valor, não um armazenamento relacional. Ele não é compatível com junções nem com transações, exceto dentro de uma linha simples. Como resultado, é melhor acessar dados em linhas individuais ou em um conjunto de linhas contíguas.

Um resultado desse padrão é bastante óbvio: na grande maioria dos casos, as consultas de séries temporais estão acessando um determinado conjunto de dados para um dado período. Portanto, verifique se todos os dados para um determinado período estão armazenados em linhas contíguas, a menos que fazer isso possa causar hotspotting.

Outro resultado é que quando você lê dados de uma linha ou de um intervalo de linhas, eles devem ser úteis por conta própria. Não é necessário combiná-los com outros dados. Por exemplo, suponha que você esteja armazenando a atividade de um usuário em um site de compras e, muitas vezes, precisa recuperar as últimas cinco ações realizadas pelo usuário para exibi-las em uma barra lateral. Neste caso, você deve considerar desnormalizar os dados e incluir alguns detalhes de usuário e de produto na tabela de ações recentes. Por outro lado, com um banco de dados relacional, você provavelmente armazenaria o código do usuário e o código do produto em uma tabela e, em seguida, a associaria com tabelas separadas de usuários e produtos em sua consulta SQL.

Dito isto, você não precisa incluir cada dado sobre uma entidade em cada linha. Por exemplo, se você estiver exibindo informações sobre as ações recentes de um usuário, não é necessário armazenar o número de telefone do usuário ou o endereço do fabricante do produto, porque você não vai exibir essas informações em uma barra lateral.

Procure oportunidades para desnormalizar dados para satisfazer consultas, mas inclua apenas os dados exigidos pelas consultas.

Armazenar dados que você acessará em uma única consulta em um único grupo de colunas

Os qualificadores de coluna em um único grupo de colunas têm um relacionamento físico e um lógico. Em geral, todos os qualificadores de coluna em um único grupo de colunas são armazenados, acessados e colocados em cache juntos. Como resultado, uma consulta que acessa um único grupo de colunas pode ser executada com mais eficiência do que uma consulta abrangendo grupos de colunas.

As consultas comuns devem ser o mais eficientes possível por meio da recuperação de dados do menor número possível de grupos de colunas.

Não explorar a atomicidade de linhas únicas

O Cloud Bigtable não é compatível com transações, com uma exceção: as operações em uma linha única são transacionais. Transações também são caras. Isso quer dizer que um sistema que depende de transações não terá um desempenho tão bom quanto o de um que não depende.

Ao trabalhar com séries temporais, não aproveite o comportamento transacional das linhas. As alterações nos dados de uma linha existente devem ser armazenadas como uma nova linha separada, e não alteradas na linha existente. Esse é um modelo mais fácil de construir e permite que você mantenha um histórico da atividade sem depender de versões de coluna.

Exemplos de projeto de esquema

Agora você aplicará os padrões de projeto de esquema para criar exemplos para os seguintes tipos de dados:

  • dados do mercado financeiro
  • métricas do servidor
  • medidores inteligentes de energia (Internet das coisas)

Lembre-se, estes são apenas exemplos. Para encontrar o melhor esquema para os dados de séries temporais, você precisará considerar quais dados está armazenando e como planeja consultá-los e, então, aplicar os padrões de projeto da seção anterior.

Dados do mercado financeiro

Este exemplo pega uma hipotética mensagem de dados do mercado de ações que representa informações sobre uma ação imaginária:

Campo Dados de exemplo
Símbolo ticker ZXZZT
Lance 600,55
Pedido 600,60
Tamanho do lance 500
Tamanho do pedido 1.500
Última venda 600,58
Último tamanho 300
Hora da cotação 12:53:32.156
Hora do pregão 12:53:32.045
Bolsa NASDAQ
Volume 89.000

Algumas observações sobre as mensagens de dados do mercado de ações antes de começar:

  • A mensagem agrega dados de cotação e dados do comércio, que são separados logicamente.

  • Há um número relativamente grande (vários milhares) de símbolos de ticker (tickers).

  • Várias centenas desses tickers responderão por 90% das mensagens recebidas, porque relativamente poucas ações são negociadas ativamente.

  • As mensagens chegam frequentemente, de centenas a dezenas de milhares por segundo, com uma média de várias milhares por segundo.

  • As consultas típicas serão para dados de cotação ou para dados do comércio separadamente, não para ambos ao mesmo tempo.

  • Para dados de cotação e dados do comércio, uma consulta típica especificará:

    • uma bolsa (como NASDAQ);
    • um símbolo ticker (como ZXZZT);
    • uma hora de início e fim.

Agora você pode criar a tabela para este caso de uso:

Manter dados relacionados na mesma tabela, manter dados não relacionados em tabelas diferentes

  • Armazene dados de cotação em uma tabela chamada QUOTE.
  • Armazene dados do comércio em uma tabela chamada TRADE.
  • As consultas podem incluir intervalos de tempo arbitrários, assim você armazenará dados a partir de uma única mensagem em cada linha.

Linhas podem ser grandes, mas não infinitamente grandes

  • Cada linha armazena dados a partir de uma única mensagem. Isso não gera preocupações de tamanho.

Não explorar a atomicidade de linhas únicas

  • Cada mensagem é autossuficiente, por isso não há preocupações.

Manter nomes curtos, mas significativos

  • Por simplicidade, este exemplo usa os campos da mensagem, em maiúsculas e com os espaços removidos, como nomes.

Essas decisões geram o seguinte layout de coluna:

Exemplo da tabela QUOTE:

Dados da coluna
MD:SYMBOL:ZXZZT MD:BID:
600,55
MD:ASK:
600,60
MD:BIDSIZE:
500
MD:ASKSIZE:
1.500
MD:QUOTETIME:
1426535612156
MD:EXCHANGE:
NASDAQ

Exemplo da tabela TRADE:

Dados da coluna
MD:SYMBOL:
ZXZZT
MD:LASTSALE:
600,58
MD:LASTSIZE:
300
MD:TRADETIME:
1426535612045
MD:EXCHANGE:
NASDAQ
MD:VOLUME:
89.000

Em seguida, projete a chave de linha:

Usar tabelas altas e estreitas

  • Cada linha armazenará dados a partir de uma mensagem, resultando em um número muito grande de linhas relativamente estreitas.

Preferir linhas a versões de coluna

  • Use versões de coluna somente na circunstância excepcional em que um valor estiver incorreto.

Projetar a chave de linha com as consultas em mente

  • As chaves de linha QUOTE e TRADE podem seguir a mesma forma.
  • Como esta é uma série temporal, você pode pressupor por padrão que QUOTETIME fará parte da chave de linha.
  • Para consultar por bolsa e ticker para uma determinada hora de início e término, você precisará usar os valores de EXCHANGE, SYMBOL e QUOTETIME.
  • Portanto, você promoverá EXCHANGE (como um código de seis caracteres, porque bolsas com menos de seis caracteres serão preenchidas com espaços), SYMBOL (como um código de cinco caracteres, porque tickers com menos de cinco caracteres serão preenchidos com espaços) e QUOTETIME (como um número de treze dígitos). Ao preencher EXCHANGE e SYMBOL com espaços, você garante que cada parte da chave de linha esteja em um deslocamento previsível.
  • Com esses valores juntos, a chave de linha terá a forma de EXCHANGE + SYMBOL + QUOTETIME (por exemplo, NASDAQ#ZXZZT#1426535612156).

Garantir que a chave de linha evite o uso excessivo do ponto de acesso

  • Ter EXCHANGE e SYMBOL nas primeiras posições da chave de linha distribuirá a atividade naturalmente.
  • Dado que 90% das mensagens estão concentradas em algumas centenas de tickers, há algum risco de uso excessivo do ponto de acesso, mas você precisa testar o sistema antes de fazer outras mudanças. Se essa concentração resultar em desempenho ruim, você poderá aplicar sal para dividir a atividade de maneira mais eficaz.

Reverter carimbos de data/hora somente quando necessário

  • Você não reverterá carimbos de data/hora neste caso, porque as consultas nem sempre exigem acesso aos dados mais recentes.

Após este exercício de projeto, você terá as seguintes tabelas:

Exemplo da tabela QUOTE:

Chave de linha Dados da coluna
NASDAQ#ZXZZT#1426535612156 MD:SYMBOL:
ZXZZT
MD:BID:
600,55
MD:ASK:
600,60
MD:BIDSIZE:
500
MD:ASKSIZE:1
500
MD:QUOTETIME:
1426535612156
MD:EXCHANGE:
NASDAQ

Exemplo da tabela TRADE:

Chave de linha Dados da coluna
NASDAQ#ZXZZT#1426535612045 MD:SYMBOL:
ZXZZT
MD:LASTSALE:
600,58
MD:LASTSIZE:
300
MD:TRADETIME:
1426535612045
MD:EXCHANGE:
NASDAQ
MD:VOLUME:
89.000

Essas tabelas vão crescer à razão de centenas de milhões de linhas por dia, e o Cloud Bigtable consegue lidar com isso sem dificuldade.

Métricas do servidor

O exemplo a seguir usa um sistema de monitoramento de servidor hipotético que coleta uma grande variedade de métricas (como uso de CPU por núcleo, de memória e disco) de um grande inventário de máquinas. Você vai passar por várias iterações do esquema neste exemplo.

Você pode fazer as seguintes suposições sobre os dados:

  • Coletar 100 métricas por máquina.
  • Coletar métricas de 100.000 máquinas.
  • As métricas são coletadas a cada cinco segundos.
  • As consultas típicas serão uma das seguintes:

    • métricas de uma determinada máquina com base em horas de início e fim específicas
    • as métricas mais recentes para todo o inventário de máquinas

Com esse caso de uso em mente, você pode projetar a tabela:

Iteração 1

Manter dados relacionados na mesma tabela, manter dados não relacionados em tabelas diferentes

  • Você armazenará dados de métricas em uma tabela chamada METRIC.
  • Há várias categorias de métricas, e você as agrupará em famílias de colunas apropriadas.
  • As consultas podem incluir intervalos de tempo arbitrários, assim você terá cada armazenamento de linha em um único conjunto de métricas de uma máquina em um determinado momento.

Linhas podem ser grandes, mas não infinitamente grandes

  • Cada linha armazena um único conjunto de métricas, o que não causa preocupações de tamanho.

Não explorar a atomicidade de linhas únicas

  • Você não dependerá da atomicidade de linhas em nosso projeto de esquema.

Manter nomes curtos, mas significativos

  • Para simplificar, você usará os nomes dos campos das métricas, em maiúsculas e com os espaços removidos, como nomes de qualificadores de coluna.

Essas decisões geram o seguinte layout de coluna:

Dados da coluna
METRIC:
HOSTNAME:
server1.bbb.com
METRIC:
CPU/CPU1_USR:
0.02
METRIC:
CPU/CPU1_NICE:
0.00
... METRIC:
IO/BLK_READ:
253453634
METRIC:
MIO/BLK_WRTN:
657365234

Em seguida, projete a chave de linha com base nos padrões:

Usar tabelas altas e estreitas

  • Cada linha na tabela armazenará um conjunto de métricas para uma máquina, resultando em um número muito grande de linhas.

Preferir linhas a versões de coluna

  • Você não usará versões de coluna.

Projetar a chave de linha com as consultas em mente

  • Como esta é uma série temporal, inclua o carimbo de data/hora, TS, na chave de linha.
  • Para recuperar métricas de uma determinada máquina para uma determinada hora de início e término, você recuperará um intervalo de linhas usando HOSTNAME e TS.
  • Recuperar as métricas mais recentes para todo o inventário de máquinas é complicado. Não é possível simplesmente reverter o carimbo de data/hora e verificar N linhas, porque não há garantia de que isso englobaria cada máquina no inventário.

Agora, você encontrou um problema com o projeto da chave de linha. A solução aqui é a desnormalização. Você criará uma tabela separada que contém as versões mais recentes das métricas, chamada CURRENT_METRIC, que você atualizará sempre que atualizar METRIC. Quando você atualizar as métricas existentes de uma máquina, simplesmente substituirá a linha dessa máquina.

Em seguida, itere em seu projeto original:

Iteração 2

Manter dados relacionados na mesma tabela, manter dados não relacionados em tabelas diferentes

  • Você armazenará dados de métricas em uma tabela chamada METRIC.
  • Você armazenará a versão mais recente das métricas em uma tabela chamada CURRENT_METRIC.
  • As outras informações permanecem iguais às da iteração 1.

Linhas podem ser grandes, mas não infinitamente grandes

  • Permanece igual à iteração 1.

Não explorar a atomicidade de linhas únicas

  • Você vai contar com a atomicidade das linhas para atualizar os dados de cada máquina em CURRENT_METRIC. Esta é uma mutação de linha simples, com pouco potencial para contenção, por isso não causará nenhum problema.

Manter nomes curtos, mas significativos

  • Permanece igual à iteração 1.

Em seguida, projete a chave de linha com base nos padrões:

Usar tabelas altas e estreitas

  • Em ambas as tabelas, cada linha na tabela armazenará um conjunto de métricas para uma máquina, resultando em um grande número de linhas.

Preferir linhas a versões de coluna

  • Permanece igual à iteração 1.

Projetar a chave de linha com as consultas em mente

  • Como esta é uma série temporal, você incluirá o carimbo de data/hora, TS, na chave de linha. Para recuperar métricas de uma determinada máquina para uma determinada hora de início e término, você recuperará um intervalo de linhas a partir de METRIC usando HOSTNAME e TS.
  • Portanto, você promoverá HOSTNAME para a chave de linha e usará uma chave de linha no formato HOSTNAME + TS.
  • Para encontrar as métricas mais recentes para máquinas específicas, verifique CURRENT_METRIC, filtrando o prefixo da chave de linha, ou HOSTNAME, dessas máquinas.
  • Para encontrar as métricas mais recentes para todo o inventário de máquinas, verifique CURRENT_METRIC sem especificar uma chave de linha.
  • Por isso, não é necessário promover campos adicionais para a chave de linha, e ela terá o formato simples HOSTNAME + TS. Isso leva a um esquema simples e de fácil compreensão que possibilita que as tabelas sejam compartilhadas de maneira efetiva.
  • Na tabela CURRENT_METRIC, mesmo que você saiba que ela está sempre armazenando a métrica mais recente para cada tabela, por motivos de simplicidade, a chave de linha será novamente HOSTNAME + TS.

    Exemplo de chave de linha METRIC e CURRENT_METRIC: server1.aaa.bbb.com#1426535612045

Garantir que a chave de linha evite o uso excessivo do ponto de acesso

  • Para METRIC e CURRENT_METRIC, não há preocupação com o uso excessivo do ponto de acesso, porque o fato de o nome do host estar no início da chave de linha distribuirá a atividade entre regiões.

Reverter carimbos de data/hora somente quando necessário

  • Você está armazenando dados para as métricas mais recentes em uma tabela separada, portanto, não é necessário reverter os carimbos de data/hora.

Após o exercício de projeto, você terá o seguinte:

Exemplo de tabela METRIC e CURRENT_METRIC:

Chave de linha Dados da coluna
server1.bbb.com#1426535612045 METRIC:
CPU/CPU1_USR:
0.02
METRIC:
CPU/CPU1_NICE:
0.00
... METRIC:
IO/BLK_READ:
253453634
METRIC:
MIO/BLK_WRTN:
657365234

Essas tabelas vão crescer a uma taxa de aproximadamente dois bilhões de linhas por dia, e o Cloud Bigtable consegue lidar com isso sem dificuldade.

Medidores inteligentes de energia (Internet das coisas)

Este exemplo usa um cenário hipotético da Internet das coisas (IoT, na sigla em inglês) no qual há medidores de energia inteligentes que enviam leituras de sensores periodicamente a um sistema centralizado. Mais uma vez, você vai passar por várias iterações do esquema neste exemplo.

Você pode fazer as seguintes suposições sobre os dados:

  • Há 10 milhões de medidores operacionais.
  • Cada medidor envia um sensor de leitura a cada 15 minutos.
  • O código do medidor é um código numérico exclusivo.
  • As consultas típicas serão uma das seguintes:

    • todos os dados de um determinado medidor em um dia específico
    • todos os dados de um determinado dia

Dado esse caso de uso, você pode projetar a tabela:

Manter dados relacionados na mesma tabela, manter dados não relacionados em tabelas diferentes

  • Você armazenará os dados do sensor em uma tabela chamada SENSOR.
  • As consultas estão em incrementos diários, assim você terá cada dado de armazenamento de linha de um medidor em um dia.

Linhas podem ser grandes, mas não infinitamente grandes

  • Cada linha conterá dados de um medidor por um dia, para um total de 96 colunas (24 horas por dia * 60 minutos em uma hora / 1 leitura a cada 15 minutos), o que não causa preocupações de tamanho.

Não explorar a atomicidade de linhas únicas

  • Você explorará a atomicidade das linhas porque, quando os dados forem recebidos, serão adicionados à linha diária apropriada. Esta é uma mutação de linha simples, com pouco potencial para contenção, por isso não causará nenhum problema.

Manter nomes curtos, mas significativos

  • ID para armazenar o código do medidor (um número inteiro único).
  • Uma série de colunas com nomes de 0000 a 2345 para conter os 96 valores registrados a cada 15 minutos durante o dia. Esse esquema é adotado porque permite que você mude para frequências diferentes de 15 minutos, se necessário.

Essas decisões geram o seguinte layout de coluna:

Iteração 1

Dados da coluna
METER:ID:987654 METER:0000:
12.34
METER:0015:
13.45
... METER:2330:
27.89
METER:2345:
28.90

Em seguida, projete a chave de linha:

Usar tabelas altas e estreitas

  • Cada linha armazenará dados para um dia, conforme explicado anteriormente.

Preferir linhas a versões de coluna

  • Use versões de coluna somente na circunstância excepcional em que um valor estiver incorreto.

Projetar a chave de linha com as consultas em mente

  • Como esta é uma série temporal, inclua DATE na chave de linha. Como você está interessado apenas na precisão para o dia, os últimos cinco dígitos do carimbo de data/hora serão zero e, portanto, ficarão omitidos.
  • Para consultar um determinado medidor para um determinado dia, você recuperará uma linha simples usando METER e DATE.
  • Para consultar todos os medidores de um determinado dia, recupere um intervalo de linhas usando DATE.
  • Por isso, promova DATE como um numeral de 8 dígitos e METER como um numeral de 10 dígitos, com o ID do medidor preenchido com 10 dígitos para acomodar um bilhão de medidores em potencial, mantendo a ordenação lexicográfica.
  • Com as consultas em conjunto, a chave de linha terá o formato de DATE + METER. Por exemplo: |20170726|0000987654|.

Neste ponto, você poderá notar um problema: no início de cada dia, todos os medidores atingirão um único nó, porque a data está na posição inicial na chave de linha. Com 10 milhões de medidores, este é provavelmente um problema que afeta o desempenho todos os dias. A solução é encontrar uma maneira melhor de consultar os dados do medidor para um dia específico. Se você executar uma única consulta a cada noite e armazenar os resultados como uma nova tabela, chamada SENSOR_YYYYMMDD, não precisará otimizar a chave de linha para as consultas com base em data.

Vamos iterar para resolver esse problema:

Iteração 2

Manter dados relacionados na mesma tabela, manter dados não relacionados em tabelas diferentes

  • Armazene os dados do sensor em uma tabela chamada SENSOR.
  • As consultas recuperarão dados em incrementos diários, assim você terá cada dado de armazenamento de linha de um medidor em um dia.
  • Você executaria uma consulta em lote durante a noite que produz outra tabela chamada SENSOR_YYYYMMDD (a data) para armazenar todos os dados do medidor para essa data.

Linhas podem ser grandes, mas não infinitamente grandes

  • Permanece igual à iteração 1.

Não explorar a atomicidade de linhas únicas

  • Permanece igual à iteração 1.

Manter nomes curtos, mas significativos

  • Permanece igual à iteração 1.

Juntando tudo isso, uma linha de exemplo seria semelhante ao exemplo a seguir para a tabela SENSOR e as tabelas SENSOR_YYYYMMDD:

Dados da coluna
METER:ID:987654 METER:0000:
12.34
METER:0015:
13.45
... METER:2330:
27.89
METER:2345:
28.90

Em seguida, projete a chave de linha:

Usar tabelas altas e estreitas

  • Permanece igual à iteração 1.

Preferir linhas a versões de coluna

  • Permanece igual à iteração 1.

Projetar a chave de linha com as consultas em mente

  • Como esta é uma série temporal, inclua DATE na chave de linha.
  • Para consultar um determinado medidor para um determinado dia, você precisará recuperar uma linha simples usando METER e DATE.
  • Por isso, promova DATE como um numeral de 8 dígitos e METER como um numeral de 10 dígitos, com o ID do medidor preenchido com 10 dígitos para acomodar um bilhão de medidores em potencial, mantendo a ordenação lexicográfica.
  • A chave de linha terá que ficar no formato METER + DATE (por exemplo, "0000987654#20170726"), o que vai satisfazer a consulta e levar a uma boa distribuição de atividade entre os nodes.
  • Você também executará uma consulta em lote uma vez por dia, que verificará toda a tabela e armazenará os dados de ontem em uma nova tabela chamada SENSOR_YYYYMMDD, em que YYYYMMDD é a data de ontem.

Garantir que a chave de linha evite o uso excessivo do ponto de acesso

  • O uso excessivo do ponto de acesso não é uma preocupação na tabela SENSOR. As gravações serão distribuídas uniformemente porque a chave de linha tem METER na posição inicial.
  • O uso excessivo do ponto de acesso não é uma preocupação nas tabelas SENSOR_YYYYMMDD. Cada tabela é criada apenas uma vez, como uma consulta em lote, na qual o desempenho é uma preocupação menor. No entanto, a criação dessas tabelas requer uma verificação completa de SENSOR. Portanto, crie as tabelas SENSOR_YYYYMMDD quando houver poucas outras consultas da tabela SENSOR.

Reverter carimbos de data/hora somente quando necessário

  • Nesse caso, você não precisa reverter carimbos de data/hora.

Após o exercício de projeto, você tem o seguinte para a tabela SENSOR e as tabelas SENSOR_YYYYMMDD:

Chave de linha Dados da coluna
0000987654#14264892 METER:ID:987654 METER:0000:
12.34
METER:0015:
13.45
... METER:2330:
27.89
METER:2345:
8,90

Essa tabela vai crescer a uma taxa de pouco menos de 10 milhões de linhas por dia, e o Cloud Bigtable consegue lidar com isso sem dificuldade.

Próximas etapas

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

Enviar comentários sobre…

Documentação do Cloud Bigtable