Introducción a Cloud Bigtable

1. Introducción

En este codelab, aprenderás a usar Cloud Bigtable con el cliente de HBase de Java.

Obtendrás información para hacer las siguientes acciones

  • Cómo evitar errores comunes con el diseño del esquema
  • Importa datos en un archivo de secuencias
  • Consulta tus datos

Cuando termines, tendrás varios mapas con datos de autobuses de la ciudad de Nueva York. Por ejemplo, crearás este mapa de calor de los viajes en autobús en Manhattan:

7349d94f7d41f1d1.png

¿Cómo calificarías tu experiencia en el uso de Google Cloud Platform?

Principiante Intermedio Avanzado

¿Cómo usarás este instructivo?

Solo lectura Léelo y completa los ejercicios. .
.

2. Acerca del conjunto de datos

Observarás un conjunto de datos sobre los autobuses de la ciudad de Nueva York. Hay más de 300 rutas de autobuses y 5,800 vehículos que siguen esas rutas. Nuestro conjunto de datos es un registro que incluye el nombre del destino, el ID del vehículo, la latitud, la longitud, la hora de llegada esperada y la hora de llegada programada. El conjunto de datos se compone de instantáneas tomadas cada 10 minutos aproximadamente en junio de 2017.

3. Diseño de esquemas

Para obtener el mejor rendimiento de Cloud Bigtable, debes diseñar tu esquema con cuidado. En Cloud Bigtable, los datos se ordenan automáticamente de forma lexicográfica, por lo que, si diseñas bien tu esquema, es muy eficiente consultar datos relacionados. Cloud Bigtable permite realizar consultas con búsquedas de puntos por clave de fila o análisis de rangos de filas que muestran un conjunto contiguo de filas. Sin embargo, si tu esquema no está bien pensado, podrías tener que unir varias búsquedas de filas o, peor aún, realizar análisis completos de tablas, que son operaciones extremadamente lentas.

Planifica las consultas

Nuestros datos contienen una gran variedad de información, pero, en este codelab, usarás la ubicación y el destino del autobús.

Con esa información, podrías realizar las siguientes consultas:

  • Obtener la ubicación de un solo autobús durante una hora determinada
  • Obtén los datos correspondientes a un día completo para una línea de autobús o un autobús específico.
  • Encuentra todos los autobuses en un rectángulo en un mapa.
  • Obtener las ubicaciones actuales de todos los autobuses (si estabas transfiriendo estos datos en tiempo real)

Este conjunto de consultas no se puede realizar juntas de forma óptima. Por ejemplo, si ordenas por tiempo, no puedes realizar un análisis basado en una ubicación sin hacer un análisis completo de la tabla. Debes establecer prioridades según las consultas que ejecutas con mayor frecuencia.

En este codelab, te enfocarás en optimizar y ejecutar el siguiente conjunto de consultas:

  • Obtener las ubicaciones de un vehículo específico en un plazo de una hora
  • Obtén las ubicaciones de toda una línea de autobús en un plazo de una hora.
  • Obtén la ubicación de todos los autobuses de Manhattan en una hora.
  • Obtén las ubicaciones más recientes de todos los autobuses de Manhattan en una hora.
  • Obtener las ubicaciones de toda una línea de autobuses durante el mes
  • Obtén las ubicaciones de toda una línea de autobús con un destino determinado en un plazo de una hora.

Cómo diseñar la clave de fila

En este codelab, trabajarás con un conjunto de datos estático, pero diseñarás un esquema para la escalabilidad. Diseñarás un esquema que te permita transmitir más datos de bus en la tabla y que, aun así, tenga un buen rendimiento.

Este es el esquema propuesto para la clave de fila:

[Empresa de autobús/Línea de autobús/Marca de tiempo redondeada hacia abajo a la hora/ID del vehículo]. Cada fila tiene una hora de datos y cada celda contiene varias versiones de los datos con marca de tiempo.

En este codelab, usarás una familia de columnas para evitar complicaciones. Esta es una vista de ejemplo de cómo se ven los datos. Los datos se ordenan por clave de fila.

Clave de fila

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. Crear instancia, tabla y familia

A continuación, crearás una tabla de Cloud Bigtable.

Primero, crea un proyecto nuevo. Usar Cloud Shell integrado, que puedes abrir haciendo clic en "Activar Cloud Shell" en la esquina superior derecha.

a74d156ca7862b28.png

Configura las siguientes variables de entorno para facilitar la copia y el pegado de los comandos 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 ya incluye las herramientas que usarás en este codelab, la herramienta de línea de comandos de gcloud, la interfaz de línea de comandos de cbt y Maven, ya instalados.

Habilita las API de Cloud Bigtable mediante la ejecución de este comando.

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

Ejecuta el siguiente comando para crear una instancia:

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

Después de crear la instancia, propaga el archivo de configuración de cbt y, luego, crea una tabla y una familia de columnas con los siguientes comandos:

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

cbt createtable $TABLE_ID
cbt createfamily $TABLE_ID cf

5. Importar datos

Importa un conjunto de archivos de secuencias para este codelab desde gs://cloud-bigtable-public-datasets/bus-data siguiendo estos pasos:

Habilita la API de Cloud Dataflow mediante la ejecución de este comando.

gcloud services enable dataflow.googleapis.com

Ejecuta los siguientes comandos para importar la tabla.

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

Supervisa la importación

Puedes supervisar el trabajo en la IU de Cloud Dataflow. Además, puedes ver la carga en tu instancia de Cloud Bigtable con su IU de supervisión. La importación completa debería tardar 5 minutos.

6. Obtén el código

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

Para cambiar a Java 11, ejecuta los siguientes comandos:

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

7. Realiza una búsqueda

La primera consulta que realizarás es una búsqueda simple de filas. Recibirás los datos de un autobús de la línea M86-SBS el 1 de junio de 2017, de 12:00 a.m. a 1:00 a.m. En ese momento, hay un vehículo con el ID NYCT_5824 en la línea de autobús.

Con esa información y conociendo el diseño del esquema (empresa de autobús/línea de autobús/marca de tiempo redondeada hacia abajo a la hora/ID del vehículo), puedes deducir que la clave de fila es:

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

El resultado debería contener la ubicación más reciente del autobús en esa hora. Pero deseas ver todas las ubicaciones, por lo que debes establecer el número máximo de versiones en la solicitud 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));

En Cloud Shell, ejecuta el siguiente comando para obtener una lista de las latitudes y longitudes de ese autobús durante la hora:

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

Puedes copiar y pegar las latitudes y longitudes en la aplicación de MapMaker para visualizar los resultados. Después de algunas capas, se te indicará que crees una cuenta gratuita. Puedes crear una cuenta o simplemente eliminar las capas existentes que tengas. En este codelab, se incluye una visualización de cada paso, si solo deseas continuar. Este es el resultado de la primera consulta:

f1a1fac6051c6210.png

8. Realiza un análisis

Ahora, veamos todos los datos de la línea de autobuses para esa hora. El código de escaneo es bastante similar al código get. Le das al escáner una posición inicial y, luego, le indicas que solo quieres filas para la línea de autobús M86-SBS dentro de la hora indicada por la marca de tiempo 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);

Ejecuta el siguiente comando para obtener los resultados.

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

c18a4ac6522d08a2.png

La app de Map Maker puede mostrar varias listas a la vez para que puedas ver cuáles de los autobuses son el vehículo en la primera consulta que realizaste.

234c1b51e3b201e.png

Una modificación interesante de esta consulta es ver todo el mes de datos para la línea de autobús M86-SBS, y esto es muy fácil de hacer. Quita la marca de tiempo de la fila de inicio y el filtro de prefijo para obtener el 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);

Ejecuta el siguiente comando para obtener los resultados. (Habrá una lista larga de resultados).

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

Si copias los resultados en MapMaker, puedes ver un mapa de calor de la ruta del autobús. Las manchas de color naranja indican las paradas, y las manchas de color rojo brillante indican el comienzo y el final de la ruta.

346f52e61b3d8902.png

9. Incorporar filtros

A continuación, filtrarás los autobuses que van hacia el este y los que van hacia el oeste, y crearás un mapa de calor separado para cada uno.

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

Ejecuta el siguiente comando para obtener los resultados de los autobuses que van hacia el este.

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

Para obtener los autobuses que van hacia el oeste, cambia la cadena en valueFilter:

BusQueries.java

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

Ejecuta el siguiente comando para obtener los resultados de los autobuses que van hacia el oeste.

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

Autobuses rumbo al este

82f6f62096a6847a.png

Autobuses rumbo al oeste

2b5771ee9046399f.png

Al comparar los dos mapas de calor, puedes ver las diferencias en las rutas, así como las diferencias en el ritmo. Una interpretación de los datos es que, en la ruta hacia el oeste, los autobuses están más detenidos, especialmente cuando entran en Central Park. Y en los autobuses que van hacia el este, realmente no hay muchos puntos de estrangulamiento.

10. Realiza un análisis de varios rangos

En la última consulta, abordarás el caso en el que te interesan muchas líneas de autobús en un á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);

Ejecuta el siguiente comando para obtener los resultados.

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

7349d94f7d41f1d1.png

11. Terminar

Realiza una limpieza para evitar cargos

Para evitar que se apliquen cargos a tu cuenta de Google Cloud Platform por los recursos que usaste en este codelab, debes borrar tu instancia.

gcloud bigtable instances delete $INSTANCE_ID

Temas abordados

  • Diseño de esquemas
  • Configura una instancia, una tabla y una familia
  • Importa archivos de secuencias con Dataflow
  • Realizar consultas con una búsqueda, un análisis, un análisis con un filtro y un análisis de varios rangos

Próximos pasos