Introduzione a Cloud Bigtable

1. Introduzione

In questo codelab, imparerai a utilizzare Cloud Bigtable con il client Java HBase.

Imparerai a

  • Evita gli errori comuni nella progettazione dello schema
  • Importa i dati in un file di sequenza
  • Eseguire query sui dati

Quando hai finito, avrai a disposizione più mappe in cui vengono visualizzati i dati degli autobus di New York. Ad esempio, creerai questa mappa termica dei viaggi in autobus a Manhattan:

7349d94f7d41f1d1.png

Come giudichi la tua esperienza di utilizzo della piattaforma Google Cloud?

Principiante Livello intermedio Eccellente

Come utilizzerai questo tutorial?

Solo lettura Leggilo e completa gli esercizi

2. Informazioni sul set di dati

Stai esaminando un set di dati sugli autobus di New York. Ci sono più di 300 linee di autobus e 5.800 veicoli che seguono questi percorsi. Il nostro set di dati è un log che include il nome della destinazione, l'ID del veicolo, la latitudine, la longitudine, l'ora di arrivo prevista e l'ora di arrivo prevista. Il set di dati è composto da snapshot acquisiti ogni 10 minuti circa per giugno 2017.

3. Progettazione di uno schema

Per ottenere le migliori prestazioni da Cloud Bigtable, devi prestare attenzione quando progetti lo schema. I dati in Cloud Bigtable vengono ordinati automaticamente in modo grammaticale, quindi se progetti bene lo schema, l'esecuzione di query sui dati correlati è molto efficiente. Cloud Bigtable consente di eseguire query che utilizzano ricerche per punto per chiave di riga o scansioni di intervalli di righe che restituiscono un insieme contiguo di righe. Tuttavia, se lo schema non è ben strutturato, potresti dover eseguire più ricerche di righe o, peggio ancora, eseguire scansioni complete delle tabelle, operazioni estremamente lente.

Pianificare le query

I nostri dati contengono una serie di informazioni, ma per questo codelab utilizzerai location e destination dell'autobus.

Con queste informazioni, potresti eseguire queste query:

  • Visualizza la posizione di un singolo autobus in una determinata ora.
  • Ottieni i dati relativi a una giornata di dati per una linea di autobus o un autobus specifico.
  • Trova tutti gli autobus in un rettangolo su una mappa.
  • Ottieni le posizioni attuali di tutti gli autobus (se stai importando questi dati in tempo reale).

Questo insieme di query non può essere eseguito insieme in modo ottimale. Ad esempio, se ordini i dati in base all'ora, non puoi eseguire una scansione basata su una località senza eseguire una scansione completa della tabella. Devi assegnare la priorità in base alle query che esegui più di frequente.

In questo codelab, ti concentrerai sull'ottimizzazione e sull'esecuzione del seguente insieme di query:

  • Ottieni le posizioni di un veicolo specifico in un'ora.
  • Ottieni le posizioni di un'intera linea di autobus in un'ora.
  • Ottieni le posizioni di tutti gli autobus a Manhattan in un'ora.
  • Ottieni le posizioni più recenti di tutti gli autobus a Manhattan in un'ora.
  • Visualizza le posizioni di un'intera linea di autobus nel corso del mese.
  • Ottieni le posizioni di un'intera linea di autobus con una determinata destinazione in un'ora.

Progettare la chiave di riga

Per questo codelab lavorerai con un set di dati statico, ma progetterai uno schema per la scalabilità. Progetterai uno schema che ti consenta di trasmettere più dati di bus nella tabella senza compromettere il rendimento.

Ecco lo schema proposto per la chiave di riga:

[Azienda di autobus/linea dell'autobus/Timestamp arrotondati per difetto all'ora/ID veicolo]. Ogni riga contiene un'ora di dati e ogni cella contiene più versioni con timestamp dei dati.

Per questo codelab, utilizzerai una famiglia di colonne per semplificare le cose. Ecco un esempio di come si presentano i dati. I dati vengono ordinati per chiave di riga.

Chiave di riga

cf:VehicleLocation.Latitude

cf:VeicoliLocation.Longitude

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. Crea istanza, tabella e famiglia

Successivamente, creerai una tabella Cloud Bigtable.

Per prima cosa, crea un nuovo progetto. Usa Cloud Shell integrato, che puoi aprire facendo clic su "Attiva Cloud Shell" nell'angolo in alto a destra.

a74d156ca7862b28.png

Imposta le seguenti variabili di ambiente per semplificare l'operazione di copia e incolla dei comandi del codelab:

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

Cloud Shell include gli strumenti che utilizzerai in questo codelab, lo strumento a riga di comando gcloud, l'interfaccia a riga di comando cbt e Maven, già installati.

Abilita le API Cloud Bigtable eseguendo questo comando.

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

Crea un'istanza eseguendo questo comando:

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

Dopo aver creato l'istanza, compila il file di configurazione cbt, quindi crea una famiglia di tabelle e colonne eseguendo questi comandi:

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

cbt createtable $TABLE_ID
cbt createfamily $TABLE_ID cf

5. Importa dati

Importa un insieme di file di sequenza per questo codelab da gs://cloud-bigtable-public-datasets/bus-data seguendo questi passaggi:

Abilita l'API Cloud Dataflow eseguendo questo comando.

gcloud services enable dataflow.googleapis.com

Esegui questi comandi per importare la tabella.

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/*

Monitorare l'importazione

Puoi monitorare il job nell'interfaccia utente di Cloud Dataflow. Inoltre, puoi visualizzare il carico sull'istanza Cloud Bigtable con la relativa UI di monitoraggio. L'intera importazione dovrebbe richiedere 5 minuti.

6. Ottieni il codice

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

Passa a Java 11 eseguendo questi comandi:

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

7. Esegui una ricerca

La prima query da eseguire è una semplice ricerca di riga. Riceverai i dati per un autobus sulla linea M86-SBS il 1° giugno 2017 dalle 00:00 all'01:00. Allora un veicolo con ID NYCT_5824 si trova sulla linea dell'autobus.

Con queste informazioni e conoscendo la progettazione dello schema (azienda di autobus/linea di autobus/timestamp arrotondati per difetto all'ora/ID veicolo), puoi dedurre che la chiave di riga è:

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));

Il risultato dovrebbe contenere la posizione più recente dell'autobus in quell'ora. Tuttavia, se vuoi visualizzare tutte le località, imposta il numero massimo di versioni per la richiesta 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));

In Cloud Shell, esegui questo comando per visualizzare un elenco di latitudini e longitudini dell'autobus nell'arco dell'ora:

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

Puoi copiare e incollare le latitudini e le longitudini nell'app MapMaker per visualizzare i risultati. Dopo alcuni livelli, ti verrà chiesto di creare un account senza costi. Puoi creare un account o eliminare semplicemente i livelli esistenti. Se vuoi continuare, questo codelab include una visualizzazione per ogni passaggio. Ecco il risultato per questa prima query:

f1a1fac6051c6210.png

8. Esegui una scansione

Ora, visualizziamo tutti i dati relativi alla linea di autobus per quell'ora. Il codice di scansione è molto simile al codice GET. Imposti una posizione iniziale allo scanner e poi indichi che desideri solo righe per la linea di autobus M86-SBS entro l'ora indicata dal timestamp 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);

Esegui questo comando per ottenere i risultati.

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

c18a4ac6522d08a2.png

L'app Map Maker può visualizzare più elenchi contemporaneamente, in modo che tu possa vedere quali degli autobus sono il veicolo dalla prima query che hai eseguito.

234c1b51e3b201e.png

Una modifica interessante a questa query è la visualizzazione dei dati dell'intero mese relativi alla linea di autobus M86-SBS, operazione molto semplice da eseguire. Rimuovi il timestamp dal filtro della riga iniziale e del prefisso per ottenere il risultato.

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);

Esegui questo comando per ottenere i risultati. L'elenco dei risultati sarà molto lungo.

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

Se copi i risultati in MapMaker, puoi visualizzare una mappa termica del percorso in autobus. Le macchie arancioni indicano le fermate, mentre le macchie rosse vivaci indicano l'inizio e la fine del percorso.

346f52e61b3d8902.png

9. Introduci i filtri

Successivamente, filtrerai gli autobus diretti a est e quelli diretti a ovest e creerai una mappa termica separata per ciascuno.

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);

Esegui questo comando per visualizzare i risultati per gli autobus diretti a est.

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

Per portare gli autobus verso ovest, modifica la stringa in valueFilter:

BusQueries.java

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

Esegui questo comando per ottenere i risultati per gli autobus diretti a ovest.

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

Autobus in direzione est

76f6f62096a6847a.png

Autobus in direzione ovest

2b5771ee9046399f.png

Confrontando le due mappe termiche, puoi vedere le differenze nei percorsi e le differenze nel pacing. Un'interpretazione dei dati è che, lungo il percorso in direzione ovest, gli autobus si fermano maggiormente, soprattutto quando entrano a Central Park. E sugli autobus diretti a est, non ci sono molti punti di strozzamento.

10. Esegui una scansione multiintervallo

Per l'ultima query, dovrai rispondere al caso quando ti interessano molte linee di autobus in una zona:

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);

Esegui questo comando per ottenere i risultati.

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

7349d94f7d41f1d1.png

11. Completa la configurazione

Eseguire la pulizia per evitare addebiti

Per evitare che al tuo account Google Cloud Platform vengano addebitati costi relativi alle risorse utilizzate in questo codelab, devi eliminare la tua istanza.

gcloud bigtable instances delete $INSTANCE_ID

Argomenti trattati

  • Progettazione di uno schema
  • Configurazione di un'istanza, una tabella e una famiglia
  • Importazione di file di sequenza con Dataflow
  • Esecuzione di query con una ricerca, una scansione, una scansione con un filtro e una scansione multiintervallo

Passaggi successivi