Introduzione a Cloud Bigtable

1. Introduzione

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

Imparerai come

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

Al termine, avrai diverse mappe che mostrano i dati degli autobus di New York City. Ad esempio, creerai questa mappa termica dei viaggi in autobus a Manhattan:

7349d94f7d41f1d1.png

Come valuti la tua esperienza di utilizzo di Google Cloud Platform?

Principiante Intermedio Avanzato

Come utilizzerai questo tutorial?

Leggila solo Leggila e completa gli esercizi

2. Informazioni sul set di dati

Visualizzerai un set di dati sugli autobus di New York City. Esistono più di 300 linee di autobus e 5800 veicoli che le seguono. Il nostro set di dati è un log che include nome della destinazione, ID veicolo, latitudine, longitudine, orario di arrivo previsto e orario di arrivo pianificato. Il set di dati è costituito da snapshot acquisiti circa ogni 10 minuti per il mese di giugno 2017.

3. Progettazione di uno schema

Per ottenere le migliori prestazioni da Cloud Bigtable, devi prestare attenzione alla progettazione dello schema. I dati in Cloud Bigtable vengono ordinati automaticamente in ordine lessicografico, quindi se progetti bene lo schema, l'esecuzione di query per i dati correlati è molto efficiente. Cloud Bigtable consente query che utilizzano ricerche puntuali per chiave di riga o scansioni di intervalli di righe che restituiscono un insieme contiguo di righe. Tuttavia, se lo schema non è ben progettato, potresti ritrovarti a mettere insieme più ricerche di righe o, peggio ancora, a eseguire scansioni complete delle tabelle, che sono operazioni estremamente lente.

Pianificare le query

I nostri dati contengono una serie di informazioni, ma per questo codelab utilizzerai la posizione e la destinazione dell'autobus.

Con queste informazioni, puoi eseguire le seguenti query:

  • Ottieni la posizione di un singolo autobus in un'ora specifica.
  • Visualizza i dati di un giorno 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 hai importato questi dati in tempo reale).

Questo insieme di query non può essere eseguito in modo ottimale. Ad esempio, se ordini per ora, non puoi eseguire una scansione in base a una località senza eseguire una scansione completa della tabella. Devi dare la priorità in base alle query che esegui più spesso.

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

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

Progetta 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 degli autobus nella tabella e di ottenere comunque buone prestazioni.

Ecco lo schema proposto per la chiave di riga:

[Azienda di trasporti in autobus/Linea di autobus/Timestamp arrotondato all'ora/ID veicolo]. Ogni riga contiene un'ora di dati e ogni cella contiene più versioni dei dati con timestamp.

Per questo codelab, utilizzerai una famiglia di colonne per semplificare le cose. Ecco una visualizzazione di esempio dell'aspetto dei dati. I dati sono ordinati per chiave di riga.

Chiave di riga

cf:VehicleLocation.Latitude

cf:VehicleLocation.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

A questo punto, crea una tabella Cloud Bigtable.

Innanzitutto, crea un nuovo progetto. Utilizza Cloud Shell integrato, che puoi aprire facendo clic sul pulsante "Attiva Cloud Shell" nell'angolo in alto a destra.

a74d156ca7862b28.png

Imposta le seguenti variabili di ambiente per semplificare la copia e l'incollatura 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 e poi crea una tabella e una famiglia di colonne eseguendo i seguenti 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 sulla tua istanza Cloud Bigtable con la relativa interfaccia utente 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. Eseguire una ricerca

La prima query che eseguirai è una semplice ricerca di righe. Riceverai i dati di un autobus della linea M86-SBS il 1° giugno 2017 dalle ore 00:00 alle ore 01:00. Un veicolo con ID NYCT_5824 si trova su questa linea di autobus.

Con queste informazioni e conoscendo la struttura dello schema (azienda di autobus/linea di autobus/timestamp arrotondato 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 deve contenere la posizione più recente dell'autobus entro quell'ora. Tuttavia, vuoi visualizzare tutte le posizioni, quindi imposta il numero massimo di versioni nella 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 ottenere un elenco di latitudini e longitudini per l'autobus nell'arco di un'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 Map Maker per visualizzare i risultati. Dopo alcuni livelli, ti verrà chiesto di creare un account senza costi. Puoi creare un account o semplicemente eliminare i livelli esistenti. Questo codelab include una visualizzazione per ogni passaggio, se vuoi solo seguire le istruzioni. Ecco il risultato della prima query:

f1a1fac6051c6210.png

8. Eseguire una scansione

Ora visualizziamo tutti i dati della linea di autobus per quell'ora. Il codice di scansione è molto simile al codice. Fornisci allo scanner una posizione iniziale e poi indichi che vuoi solo le 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 da poter vedere quale degli autobus è il veicolo della prima query eseguita.

234c1b51e3b201e.png

Una modifica interessante a questa query è la visualizzazione dell'intero mese di dati per la linea di autobus M86-SBS, che è 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. Verrà visualizzato un lungo elenco di risultati.

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

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

346f52e61b3d8902.png

9. Introduzione dei filtri

Successivamente, filtrerai gli autobus che vanno a est e quelli che vanno 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 ottenere i risultati per gli autobus che vanno verso est.

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

Per ottenere gli autobus che vanno 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 che vanno verso ovest.

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

Autobus che si dirigono verso est

76f6f62096a6847a.png

Autobus che si dirigono verso ovest

2b5771ee9046399f.png

Confrontando le due mappe termiche, puoi notare le differenze nei percorsi e nel ritmo. Un'interpretazione dei dati è che sull'itinerario in direzione ovest gli autobus vengono fermati più spesso, soprattutto quando entrano a Central Park. Sugli autobus diretti a est, non si vedono molti punti critici.

10. Eseguire una scansione multiraggio

Per l'ultima query, affronterai il caso in cui 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

Eseguire la pulizia per evitare addebiti

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

gcloud bigtable instances delete $INSTANCE_ID

Argomenti trattati

  • Progettazione di uno schema
  • Configurazione di un'istanza, una tabella e una famiglia
  • Importazione dei file di sequenza con Dataflow
  • Eseguire query con una ricerca, una scansione, una scansione con un filtro e una scansione multi-intervallo

Passaggi successivi