Introdução ao Cloud Bigtable

1. Introdução

Neste codelab, você vai aprender a usar o Cloud Bigtable com o cliente Java HBase.

Você aprenderá como realizar as seguintes tarefas:

  • Evitar erros comuns com o design de esquemas
  • Importar dados em um arquivo de sequência
  • Consultar dados

Quando terminar, você terá vários mapas que mostram dados de ônibus da cidade de Nova York. Por exemplo, você criará este mapa de calor de viagens de ônibus em Manhattan:

7349d94f7d41f1d1.png

Como você classificaria sua experiência de uso do Google Cloud Platform?

Iniciante Intermediário Proficiente

Como você usará este tutorial?

Somente leitura Ler e fazer os exercícios

2. Sobre o conjunto de dados

Você verá um conjunto de dados sobre ônibus da cidade de Nova York. Há mais de 300 trajetos de ônibus e 5.800 veículos seguindo esses trajetos. Nosso conjunto de dados é um registro que inclui nome do destino, ID do veículo, latitude, longitude, horário esperado de chegada e horário programado de chegada. O conjunto de dados é composto por snapshots tirados a cada 10 minutos em junho de 2017.

3. Design de esquema

Para ter o melhor desempenho do Cloud Bigtable, é preciso ter cuidado ao projetar o esquema. Os dados no Cloud Bigtable são classificados automaticamente lexicograficamente. Assim, se você projetar bem o esquema, a consulta de dados relacionados será muito eficiente. O Cloud Bigtable permite consultas usando pesquisas de pontos por chave de linha ou verificações de intervalos de linhas que retornam um conjunto contíguo de linhas. No entanto, se seu esquema não for bem pensado, você pode se encontrar juntando várias pesquisas de linha ou, pior ainda, fazendo verificações completas de tabela, que são operações extremamente lentas.

Planejar as consultas

Nossos dados têm várias informações, mas, neste codelab, você usará o local e o destino do ônibus.

Com essas informações, você poderia realizar estas consultas:

  • Veja a localização de um único ônibus em uma determinada hora.
  • Receba dados de um dia para uma linha de ônibus ou ônibus específico.
  • Encontre todos os ônibus em um retângulo em um mapa.
  • Descubra a localização atual de todos os ônibus (se você estiver processando esses dados em tempo real).

Esse conjunto de consultas não pode ser feito de maneira otimizada em conjunto. Por exemplo, se você estiver classificando por horário, não poderá fazer uma verificação com base em um local sem fazer uma verificação completa da tabela. Você precisa priorizar com base nas consultas que executa com mais frequência.

Neste codelab, o foco será otimizar e executar o seguinte conjunto de consultas:

  • Confira a localização de um veículo específico ao longo de uma hora.
  • Confira a localização de uma linha inteira de ônibus em mais de uma hora.
  • Veja a localização de todos os ônibus em Manhattan em uma hora.
  • Veja os locais mais recentes de todos os ônibus em Manhattan em uma hora.
  • Conferir a localização de uma linha inteira de ônibus ao longo do mês.
  • Confira as localizações de uma linha de ônibus inteira com um destino específico ao longo de uma hora.

Projetar a chave de linha

Neste codelab, você trabalhará com um conjunto de dados estático, mas criará um esquema para escalonabilidade. Você criará um esquema para transmitir mais dados de barramento para a tabela, mantendo um bom desempenho.

Este é o esquema proposto para a chave de linha:

[Empresa de ônibus/linha de ônibus/carimbo de data/hora arredondado para baixo para a hora/ID do veículo]. Cada linha tem uma hora de dados, e cada célula contém várias versões dos dados com o carimbo de data/hora.

Neste codelab, você usará um grupo de colunas para simplificar o processo. Este é um exemplo de visualização dos dados. Os dados são classificados por chave de linha.

Chave de linha

cf:VehicleLocation.Latitude (link em inglês)

cf:VehicleLocation.Longitude (link em inglês)

MTA/M86-SBS/1496275200000/NYCT_5824

40.781212 @20:52:54.0040.776163 @20:43:19.0040.778714 @20:33:46.00

-73.961942 @20:52:54.00-73.946949 @20:43:19.00-73.953731 @20:33:46.00

MTA/M86-SBS/1496275200000/NYCT_5840

40,780664 @20:13:51.0040,788416 @20:03:40.00

-73.958357 @20:13:51.00 -73,976748 @20:03:40.00

MTA/M86-SBS/1496275200000/NYCT_5867

40.780281 @20:51:45.0040.779961 @20:43:15.0040.788416 @20:33:44.00

-73.946890 @20:51:45.00-73.959465 @20:43:15.00-73.976748 @20:33:44.00

...

...

...

4. Criar instância, tabela e família

Em seguida, você criará uma tabela do Cloud Bigtable.

Primeiro, crie um novo projeto. Use o Cloud Shell integrado, que pode ser aberto clicando no botão "Ativar o Cloud Shell" no canto superior direito.

a74d156ca7862b28.png

Defina as variáveis de ambiente abaixo para facilitar a ação de copiar e colar os comandos do codelab:

INSTANCE_ID="bus-instance"
CLUSTER_ID="bus-cluster"
TABLE_ID="bus-data"
CLUSTER_NUM_NODES=3
CLUSTER_ZONE="us-central1-c"

O Cloud Shell vem com as ferramentas que você vai usar neste codelab, a ferramenta de linha de comando gcloud, a interface de linha de comando cbt e o Maven, que já estão instalados.

Execute este comando para ativar as APIs do Cloud Bigtable.

gcloud services enable bigtable.googleapis.com bigtableadmin.googleapis.com

Crie uma instância executando o seguinte comando:

gcloud bigtable instances create $INSTANCE_ID \
    --cluster=$CLUSTER_ID \
    --cluster-zone=$CLUSTER_ZONE \
    --cluster-num-nodes=$CLUSTER_NUM_NODES \
    --display-name=$INSTANCE_ID

Após criar a instância, preencha o arquivo de configuração cbt e crie uma tabela e um grupo de colunas executando os seguintes comandos:

echo project = $GOOGLE_CLOUD_PROJECT > ~/.cbtrc
echo instance = $INSTANCE_ID >> ~/.cbtrc

cbt createtable $TABLE_ID
cbt createfamily $TABLE_ID cf

5. Importar dados

Importe um conjunto de arquivos sequenciais do gs://cloud-bigtable-public-datasets/bus-data para este codelab, seguindo estas etapas:

Execute este comando para ativar a API Cloud Dataflow.

gcloud services enable dataflow.googleapis.com

Execute os comandos a seguir para importar a tabela.

NUM_WORKERS=$(expr 3 \* $CLUSTER_NUM_NODES)
gcloud beta dataflow jobs run import-bus-data-$(date +%s) \
--gcs-location gs://dataflow-templates/latest/GCS_SequenceFile_to_Cloud_Bigtable \
--num-workers=$NUM_WORKERS --max-workers=$NUM_WORKERS \
--parameters bigtableProject=$GOOGLE_CLOUD_PROJECT,bigtableInstanceId=$INSTANCE_ID,bigtableTableId=$TABLE_ID,sourcePattern=gs://cloud-bigtable-public-datasets/bus-data/*

Monitorar a importação

É possível monitorar o job na IU do Cloud Dataflow. Além disso, é possível visualizar a carga na instância do Cloud Bigtable com a IU de monitoramento. A importação completa leva cinco minutos.

6. Acessar o código

git clone https://github.com/googlecodelabs/cbt-intro-java.git
cd cbt-intro-java

Mude para o Java 11 executando os seguintes comandos:

sudo update-java-alternatives -s java-1.11.0-openjdk-amd64 && export JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64/

7. Realizar uma pesquisa

A primeira consulta que você vai realizar é uma pesquisa de linha simples. Você receberá os dados de um ônibus da linha M86-SBS no dia 1o de junho de 2017, da 0h à 1h. Um veículo com o ID NYCT_5824 está na linha de ônibus.

Com essas informações e conhecendo o design do esquema (empresa de ônibus/linha de ônibus/carimbo de data/hora arredondado para a hora/ID do veículo), você pode deduzir que a chave de linha é:

MTA/M86-SBS/1496275200000/NYCT_5824

BusQueries.java

private static final byte[] COLUMN_FAMILY_NAME = Bytes.toBytes("cf");
private static final byte[] LAT_COLUMN_NAME = Bytes.toBytes("VehicleLocation.Latitude");
private static final byte[] LONG_COLUMN_NAME = Bytes.toBytes("VehicleLocation.Longitude");

String rowKey = "MTA/M86-SBS/1496275200000/NYCT_5824";
Result getResult =
    table.get(
        new Get(Bytes.toBytes(rowKey))
            .addColumn(COLUMN_FAMILY_NAME, LAT_COLUMN_NAME)
            .addColumn(COLUMN_FAMILY_NAME, LONG_COLUMN_NAME));

O resultado deve conter a localização mais recente do ônibus dentro desse horário. No entanto, você quer ver todos os locais. Portanto, defina o número máximo de versões na solicitação GET.

BusQueries.java

Result getResult =
    table.get(
        new Get(Bytes.toBytes(rowKey))
            .setMaxVersions(Integer.MAX_VALUE)
            .addColumn(COLUMN_FAMILY_NAME, LAT_COLUMN_NAME)
            .addColumn(COLUMN_FAMILY_NAME, LONG_COLUMN_NAME));

No Cloud Shell, execute o seguinte comando para ver uma lista de latitudes e longitudes do ônibus em questão ao longo da hora:

mvn package exec:java -Dbigtable.projectID=$GOOGLE_CLOUD_PROJECT \
-Dbigtable.instanceID=$INSTANCE_ID -Dbigtable.table=$TABLE_ID \
-Dquery=lookupVehicleInGivenHour

Você pode copiar e colar as latitudes e longitudes no app MapMaker para visualizar os resultados. Após algumas camadas, você receberá uma solicitação para criar uma conta sem custo financeiro. Você pode criar uma conta ou simplesmente excluir as camadas existentes. Este codelab inclui uma visualização de cada etapa, se você quiser apenas acompanhar. Aqui está o resultado para a primeira consulta:

f1a1fac6051c6210.png

8. Fazer uma verificação

Agora, vamos ver todos os dados da linha de ônibus referente a esse horário. O código de verificação é bastante semelhante ao código get. Você dá ao scanner uma posição inicial e depois indica que quer apenas linhas para a linha de ônibus M86-SBS dentro da hora indicada pelo carimbo de data/hora 1496275200000.

BusQueries.java

Scan scan;
scan = new Scan();
scan.setMaxVersions(Integer.MAX_VALUE)
    .addColumn(COLUMN_FAMILY_NAME, LAT_COLUMN_NAME)
    .addColumn(COLUMN_FAMILY_NAME, LONG_COLUMN_NAME)
    .withStartRow(Bytes.toBytes("MTA/M86-SBS/1496275200000"))
    .setRowPrefixFilter(Bytes.toBytes("MTA/M86-SBS/1496275200000"));
ResultScanner scanner = table.getScanner(scan);

Execute o comando a seguir para receber os resultados.

mvn package exec:java -Dbigtable.projectID=$GOOGLE_CLOUD_PROJECT \
-Dbigtable.instanceID=$INSTANCE_ID -Dbigtable.table=$TABLE_ID \
-Dquery=scanBusLineInGivenHour

c18a4ac6522d08a2.png

O aplicativo Map Maker pode exibir várias listas de uma só vez. Assim, você pode ver quais dos ônibus são o veículo da primeira consulta executada.

234c1b51e3b201e.png

Uma modificação interessante nessa consulta é visualizar o mês inteiro de dados da linha de ônibus M86-SBS, e isso é muito fácil de fazer. Remova o carimbo de data/hora da linha inicial e o filtro de prefixo para conferir o resultado.

BusQueries.java

scan.withStartRow(Bytes.toBytes("MTA/M86-SBS/"))
    .setRowPrefixFilter(Bytes.toBytes("MTA/M86-SBS/"));

// Optionally, reduce the results to receive one version per column
// since there are so many data points.
scan.setMaxVersions(1);

Execute o comando a seguir para receber os resultados. (Haverá uma longa lista de resultados.)

mvn package exec:java -Dbigtable.projectID=$GOOGLE_CLOUD_PROJECT \
-Dbigtable.instanceID=$INSTANCE_ID -Dbigtable.table=$TABLE_ID \
-Dquery=scanEntireBusLine

Se você copiar os resultados para o MapMaker, poderá ver um mapa de calor do trajeto de ônibus. Os pontos laranjas indicam as paradas e os vermelhos representam o início e o fim do trajeto.

346f52e61b3d8902.png

9. Implementar filtros

Em seguida, você vai filtrar os ônibus na direção leste e na direção oeste e criar um mapa de calor separado para cada um deles.

BusQueries.java

Scan scan;
ResultScanner scanner;
scan = new Scan();
SingleColumnValueFilter valueFilter =
    new SingleColumnValueFilter(
        COLUMN_FAMILY_NAME,
        Bytes.toBytes("DestinationName"),
        CompareOp.EQUAL,
        Bytes.toBytes("Select Bus Service Yorkville East End AV"));

scan.setMaxVersions(1)
    .addColumn(COLUMN_FAMILY_NAME, LAT_COLUMN_NAME)
    .addColumn(COLUMN_FAMILY_NAME, LONG_COLUMN_NAME);
scan.withStartRow(Bytes.toBytes("MTA/M86-SBS/"))
    .setRowPrefixFilter(Bytes.toBytes("MTA/M86-SBS/"));
scan.setFilter(valueFilter);
scanner = table.getScanner(scan);

Execute o comando a seguir para receber os resultados de ônibus na direção leste.

mvn package exec:java -Dbigtable.projectID=$GOOGLE_CLOUD_PROJECT \
-Dbigtable.instanceID=$INSTANCE_ID -Dbigtable.table=$TABLE_ID \
-Dquery=filterBusesGoingEast

Para que os ônibus sigam na direção oeste, altere a string em valueFilter:

BusQueries.java

SingleColumnValueFilter valueFilter =
    new SingleColumnValueFilter(
        COLUMN_FAMILY_NAME,
        Bytes.toBytes("DestinationName"),
        CompareOp.EQUAL,
        Bytes.toBytes("Select Bus Service Westside West End AV"));

Execute o comando a seguir para receber os resultados de ônibus indo na direção oeste.

mvn package exec:java -Dbigtable.projectID=$GOOGLE_CLOUD_PROJECT \
-Dbigtable.instanceID=$INSTANCE_ID -Dbigtable.table=$TABLE_ID \
-Dquery=filterBusesGoingWest

Ônibus indo para o leste

76f6f62096a6847a.png

Ônibus indo na direção oeste

2b5771ee9046399f.png

Ao comparar os dois mapas de calor, você pode ver as diferenças nos trajetos e no ritmo. Uma interpretação dos dados é que, no trajeto na direção oeste, os ônibus estão ficando mais parados, especialmente ao entrar no Central Park. Nos ônibus indo para o leste, não há muitos pontos de estrangulamento.

10. Executar uma verificação de vários intervalos

Na consulta final, você vai abordar o caso em que há muitas linhas de ônibus em uma área:

BusQueries.java

private static final String[] MANHATTAN_BUS_LINES = {"M1","M2","M3",...

Scan scan;
ResultScanner scanner;
List<RowRange> ranges = new ArrayList<>();
for (String busLine : MANHATTAN_BUS_LINES) {
  ranges.add(
      new RowRange(
          Bytes.toBytes("MTA/" + busLine + "/1496275200000"), true,
          Bytes.toBytes("MTA/" + busLine + "/1496275200001"), false));
}
Filter filter = new MultiRowRangeFilter(ranges);
scan = new Scan();
scan.setFilter(filter);
scan.setMaxVersions(Integer.MAX_VALUE)
    .addColumn(COLUMN_FAMILY_NAME, LAT_COLUMN_NAME)
    .addColumn(COLUMN_FAMILY_NAME, LONG_COLUMN_NAME);
scan.withStartRow(Bytes.toBytes("MTA/M")).setRowPrefixFilter(Bytes.toBytes("MTA/M"));
scanner = table.getScanner(scan);

Execute o comando a seguir para receber os resultados.

mvn package exec:java -Dbigtable.projectID=$GOOGLE_CLOUD_PROJECT \
-Dbigtable.instanceID=$INSTANCE_ID -Dbigtable.table=$TABLE_ID \
-Dquery=scanManhattanBusesInGivenHour

7349d94f7d41f1d1.png

11. Concluir

Fazer uma limpeza para evitar cobranças

Para evitar cobranças na sua conta do Google Cloud Platform pelos recursos usados neste codelab, exclua sua instância.

gcloud bigtable instances delete $INSTANCE_ID

O que vimos

  • Design de esquema
  • Como configurar uma instância, tabela e família
  • Como importar arquivos sequenciais com o Dataflow
  • Consultas com uma pesquisa, uma verificação, uma verificação com um filtro e uma verificação de vários intervalos

Próximas etapas