Cloud Bigtable para usuários do Cassandra

1. Introdução

Este codelab é um guia para quem está migrando consultas do Apache Cassandra para o Google Cloud Bigtable.

Neste codelab, você vai

  • Usar o emulador do Cloud Bigtable
  • Analisar um caso de uso de séries temporais
  • Criar uma tabela e um grupo de colunas
  • Conhecer os equivalentes em Java do Cloud Bigtable dos recursos "Inserir", "Atualizar", "Selecionar" e "Excluir" do CQL

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. Configurar

Você estará analisando um conjunto de dados de amostra com apenas algumas linhas para ajudá-lo a compreender os principais conceitos rapidamente.

Cassandra

Consultas equivalentes do Cassandra estarão presentes em cada etapa, então fique à vontade para acompanhar um cluster local se quiser ou configure rapidamente um cluster do Cassandra click-to-deploy e use SSH nele.

Se você está acompanhando, crie um keyspace e use-o. A estratégia de replicação não será importante aqui.

cqlsh> create keyspace mykeyspace with replication = {'class':'SimpleStrategy','replication_factor' : 2};
cqlsh> use mykeyspace;

Cloud Bigtable

Você vai precisar de uma instância do Cloud Bigtable para sua tabela. Configure uma instância local sem custo financeiro usando o emulador. Não é necessário criar um keyspace no Cloud Bigtable. A replicação é feita pela configuração da instância.

Use o seguinte comando para iniciar o emulador:

gcloud beta emulators bigtable start

Em seguida, em outra janela ou guia do shell, defina a variável de ambiente do emulador com este comando:

$(gcloud beta emulators bigtable env-init) #Sets BIGTAB`LE_EMULATOR_HOST

Em seguida, crie um projeto Java que você usará para executar os exemplos de código e importe o cliente do Cloud Bigtable com Maven, Gradle ou SBT. Em seguida, em um novo arquivo Java, crie uma conexão com o cliente de dados.

BigtableDataSettings settings =
    BigtableDataSettings.newBuilder().setProjectId(projectId).setInstanceId(instanceId).build();

try {
  dataClient = BigtableDataClient.create(settings);
} catch (Exception e) {
  System.out.println("Error during data client connection: \n" + e.toString());
}

3. Criação de tabela

Nas tabelas do Cassandra e do Cloud Bigtable, cada linha tem uma chave associada a ela. As chaves do Cassandra têm uma chave de partição e uma coluna de clustering que podem ser separadas ou sobrepostas. Todas as chaves do Cloud Bigtable são usadas para divisões (partições) e ordenação. Esses dois sistemas são incrivelmente semelhantes quando se trata de construção da chave primária/de linha. Os dois sistemas são basicamente listas classificadas lexicograficamente em que as chaves atuam como a forma principal de distribuição de linhas entre os nós. Na maioria dos casos, é possível reutilizar a mesma chave para o Cloud Bigtable.

Neste codelab, você usará o Cloud Bigtable para armazenar dados de séries temporais sobre smartphones e tablets de dispositivos móveis. Não os confunda com tablets Bigtable. Confira abaixo as instruções para criar a tabela.

Cassandra

cqlsh:mykeyspace> create table mobileTimeSeries (
           deviceid text,
           devicetype text,
           date date,
           connected_cell map<timestamp,Boolean>, 
           os_build text, 
           os_name text,
           PRIMARY KEY((devicetype, deviceid), date));

Cloud Bigtable

É possível criar uma tabela e um grupo de colunas com o cliente Java, mas é mais fácil usar o comando a seguir com a ferramenta cbt:

cbt createtable mobile-time-series families=stats_summary

O Cloud Bigtable é um banco de dados NoSQL. Portanto, você não precisa definir um esquema na criação da tabela, mas pode pensar nas consultas que vai executar e otimizar a chave de linha para elas.

A estratégia mais comum para a migração de chaves é simplesmente pegar todas as chaves de partição e colunas de clustering e mesclá-las para formar uma string que represente a chave de linha do Cloud Bigtable. Geralmente, recomendamos o uso de chaves baseadas em strings porque elas ajudam a depurar a distribuição de chaves pelo Key Visualizer. É possível usar um separador, como um hash "#" entre os valores das colunas para facilitar a leitura.

Neste exemplo, usaremos uma chave de linha de "[DEVICE_TYPE]#[DEVICE_ID]#[AAAAMMDD]"

4. Inserção

As inserções são bastante semelhantes entre o Cassandra e o Cloud Bigtable. Aqui, você insere uma linha e, em seguida, várias linhas usando uma chave de linha de "[DEVICE_TYPE]#[DEVICE_ID]#[AAAAMMDD]".

Cassandra

Single

cqlsh:mykeyspace> insert into mobileTimeSeries (deviceid, devicetype, date, connected_cell, os_build) values ('4c410523', 'phone',toDate(now()), {toTimeStamp(now()): true}, 'PQ2A.190405.003');

Batch

cqlsh:mykeyspace> BEGIN BATCH
insert into mobileTimeSeries (deviceid, devicetype, date, os_name, os_build) values ('a0b81f74', 'tablet', '2019-01-01', 'chromeos', '12155.0.0-rc1');
insert into mobileTimeSeries (deviceid, devicetype, date, os_name, os_build) values ('a0b81f74', 'tablet', '2019-01-02','chromeos', '12145.0.0-rc6');
APPLY BATCH;

Cloud Bigtable

Single

Crie uma mutação com a chave de linha e os dados que você gostaria de usar e, em seguida, aplique a mutação com o cliente de dados. Você adicionará uma linha de dados para um smartphone com informações sobre a conexão celular e o sistema operacional.

try {
  System.currentTimeMillis();
  long timestamp = (long) 1556712000 * 1000; // Timestamp of June 1, 2019 12:00

  String rowKey = "phone#4c410523#20190501";
  ByteString one = ByteString.copyFrom(new byte[] {0, 0, 0, 0, 0, 0, 0, 1});

  RowMutation rowMutation =
      RowMutation.create(tableId, rowKey)
          .setCell(
              COLUMN_FAMILY_NAME,
              ByteString.copyFrom("connected_cell".getBytes()),
              timestamp,
              one)
          .setCell(COLUMN_FAMILY_NAME, "os_build", timestamp, "PQ2A.190405.003");

  dataClient.mutateRow(rowMutation);
} catch (Exception e) {
  System.out.println("Error during Write: \n" + e.toString());
}

Batch

Defina várias mutações em um objeto BulkMutation e use o cliente de dados para aplicar todas as mutações com uma chamada de API. Você adicionará alguns dias de dados sobre o nome e a versão do sistema operacional de um tablet.

try {
  long timestamp = (long) 1556712000 * 1000; // Timestamp of June 1, 2019 12:00

  BulkMutation bulkMutation =
      BulkMutation.create(tableId)
          .add(
              "tablet#a0b81f74#20190501",
              Mutation.create()
                  .setCell(COLUMN_FAMILY_NAME, "os_name", timestamp, "chromeos")
                  .setCell(COLUMN_FAMILY_NAME, "os_build", timestamp, "12155.0.0-rc1"))
          .add(
              "tablet#a0b81f74#20190502",
              Mutation.create()
                  .setCell(COLUMN_FAMILY_NAME, "os_name", timestamp, "chromeos")
                  .setCell(COLUMN_FAMILY_NAME, "os_build", timestamp, "12155.0.0-rc6"));

  dataClient.bulkMutateRows(bulkMutation);
} catch (Exception e) {
  System.out.println("Error during WriteBatch: \n" + e.toString());
}

5. Atualizações

Aqui você vai atualizar uma célula que ainda não foi gravada e, em seguida, gravar um novo valor em uma célula, mantendo também as versões anteriores.

Cassandra

Adicionar células

cqlsh:mykeyspace> UPDATE mobileTimeSeries SET os_name = 'android' WHERE devicetype='phone' AND deviceid = '4c410523' AND date = '2019-09-06';

Como atualizar células

cqlsh:mykeyspace> UPDATE mobileTimeSeries SET connected_cell = connected_cell +  {toTimeStamp(now()): false} WHERE devicetype='phone' AND deviceid = '4c410523' AND date = '2019-09-06';

Cloud Bigtable

No Cloud Bigtable, basta tratar as atualizações da mesma forma que as gravações.

Adicionar células

Isso é o mesmo que gravar células, basta fornecer uma coluna na qual você não tenha gravado anteriormente. Aqui você vai adicionar o nome do sistema operacional à linha do smartphone.

try {
  long timestamp = (long) 1556713800 * 1000; // Timestamp of June 1, 2019 12:30

  String rowKey = "phone#4c410523#20190501";

  RowMutation rowMutation =
      RowMutation.create(tableId, rowKey)
          .setCell(COLUMN_FAMILY_NAME, "os_name", timestamp, "android");

  dataClient.mutateRow(rowMutation);
} catch (Exception e) {
  System.out.println("Error during update: \n" + e.toString());
}

Como atualizar células

Aqui você vai adicionar novos dados sobre o status da conexão celular do smartphone. É possível usar versões de célula para armazenar facilmente parte dos dados de série temporal. Basta fornecer um carimbo de data/hora para sua gravação e adicionar uma nova versão para a célula. Para limpar seus dados, use a coleta de lixo para excluir versões após um determinado número ou período.

try {
  long timestamp = (long) 1556713800 * 1000; // Timestamp of June 1, 2019 12:30

  String rowKey = "phone#4c410523#20190501";

  ByteString zero = ByteString.copyFrom(new byte[] {0, 0, 0, 0, 0, 0, 0, 0});

  RowMutation rowMutation =
      RowMutation.create(tableId, rowKey)
          .setCell(
              COLUMN_FAMILY_NAME,
              ByteString.copyFrom("connected_cell".getBytes()),
              timestamp,
              zero);

  dataClient.mutateRow(rowMutation);
} catch (Exception e) {
  System.out.println("Error during update2: \n" + e.toString());
}

6. Seleciona

Agora, você recuperará os dados que escreveu na tabela. Ao migrar as instruções SELECT do CQL, você precisa considerar vários aspectos das instruções SELECT, como colunas, filtragem por cláusulas WHERE e funções de limite e agregação, como group by. Aqui, você verá apenas duas instruções SELECT simples para ter uma ideia básica, mas pode consultar a documentação para mais informações sobre a seleção. No Cloud Bigtable, há dois tipos de operações de recuperação: Get e Scan. Get recupera uma linha enquanto scan recupera o intervalo de linhas.

Cassandra

Single

cqlsh:mykeyspace> SELECT * FROM mobileTimeSeries WHERE devicetype='phone' AND deviceid = '4c410523' AND date = '2019-09-04';

Vários

cqlsh:mykeyspace> SELECT * FROM mobileTimeSeries WHERE devicetype='tablet' AND deviceid = 'a0b81f74' AND date >= '2019-09-04';

Cloud Bigtable

Solteiro

Use uma pesquisa de linha para ver os dados de um telefone específico na data especificada em uma única linha. Isso vai retornar cada versão com carimbo de data/hora dos valores, portanto, você verá duas linhas para "célula conectada" em carimbos de data/hora diferentes.

try {
  String rowKey = "phone#4c410523#20190501";

  Row row = dataClient.readRow(tableId, rowKey);
  for (RowCell cell : row.getCells()) {

    System.out.printf(
        "Family: %s    Qualifier: %s    Value: %s    Timestamp: %s%n",
        cell.getFamily(),
        cell.getQualifier().toStringUtf8(),
        cell.getValue().toStringUtf8(),
        cell.getTimestamp());
  }
} catch (Exception e) {
  System.out.println("Error during lookup: \n" + e.toString());
}

Várias

Use uma verificação de intervalo para exibir os dados de um mês de um tablet específico, distribuídos em várias linhas. Você pode usar um filtro com eles para obter apenas determinadas versões dos dados ou filtrar valores.

try {
  Query query = Query.create(tableId).range("tablet#a0b81f74#201905", "tablet#a0b81f74#201906");
  ServerStream<Row> rowStream = dataClient.readRows(query);
  for (Row row : rowStream) {
    System.out.println("Row Key: " + row.getKey().toStringUtf8());
    for (RowCell cell : row.getCells()) {

      System.out.printf(
          "Family: %s    Qualifier: %s    Value: %s    Timestamp: %s%n",
          cell.getFamily(),
          cell.getQualifier().toStringUtf8(),
          cell.getValue().toStringUtf8(),
          cell.getTimestamp());
    }
  }
} catch (Exception e) {
  System.out.println("Error during scan: \n" + e.toString());
}

7. Exclusões

Aqui você excluirá os dados que colocar em sua tabela. Primeiro, você excluirá uma linha individual e, em seguida, excluirá várias linhas.

A CQL do Cassandra permite exclusões de uma única linha, bem como remoções de intervalos quando todas as colunas primárias são especificadas. Você pode fazer isso com o Bigtable verificando um intervalo e realizando exclusões no nível da linha. Você obterá o mesmo resultado, mas terá mais operações, já que cada exclusão será uma operação própria.

Cassandra

Single

cqlsh:mykeyspace> DELETE from mobileTimeSeries where devicetype='phone' and deviceid = '4c410523';

Vários

cqlsh:mykeyspace> DELETE from mobileTimeSeries where devicetype='tablet' and deviceid = 'a0b81f74';

Cloud Bigtable

Single

Aqui você vai excluir os dados de um smartphone e uma data específicos. Use a chave de linha para excluir uma linha por vez.

try {
  String rowKey = "phone#4c410523#20190501";

  RowMutation mutation = RowMutation.create(tableId, rowKey).deleteRow();

  dataClient.mutateRow(mutation);
} catch (Exception e) {
  System.out.println("Error during Delete: \n" + e.toString());
}

Vários

Aqui você excluirá todos os dados de um tablet móvel específico. Para migrar uma consulta CQL excluindo várias linhas, você precisaria executar uma verificação e, em seguida, excluir cada linha usando o conjunto resultante de chaves de linha.

try {
  Query query = Query.create(tableId).prefix("tablet#a0b81f7");
  ServerStream<Row> rowStream = dataClient.readRows(query);
  BulkMutation bulkMutation = BulkMutation.create(tableId);
  for (Row row : rowStream) {
    bulkMutation.add(row.getKey(), Mutation.create().deleteRow());
  }

  dataClient.bulkMutateRows(bulkMutation);
} catch (Exception e) {
  System.out.println("Error during DeleteMultiple: \n" + e.toString());
}

8. Conclusão

Limpeza

Cassandra

Se você criou um cluster do Cassandra para acompanhar, exclua-o como faria normalmente.

Cloud Bigtable

Se você criou a tabela em uma instância do Cloud Bigtable, é possível excluí-la com o comando "cbt".

cbt deletetable mobile-time-series

Se você usou o emulador, basta interrompê-lo para limpar todo o trabalho pressionando CTRL-C no terminal em que ele foi iniciado.

Próximas etapas