Desarrolla aplicaciones con GoogleSQL para Bigtable

Puedes usar GoogleSQL para Bigtable para ejecutar consultas desde tus aplicaciones y transferir funciones de procesamiento a Bigtable. Esto reduce la necesidad de procesamiento del cliente. GoogleSQL es un lenguaje de consulta estructurado compatible con ANSI que se implementó para Bigtable y otros Google Cloud servicios. Para obtener más información sobre otros usos de GoogleSQL para Bigtable, consulta Descripción general de GoogleSQL para Bigtable.

Antes de comenzar

Asegúrate de tener lo siguiente:

Situación de ejemplo

En los ejemplos de esta guía, se usa una tabla de muestra weather-data a la que puedes acceder desde tu instancia de prueba gratuita en la Google Cloud consola. Supongamos que tu tabla almacena información de diferentes estaciones meteorológicas que se identifican con un station_id. Cada estación registra la temperatura en la escala Fahrenheit en una columna llamada temp_f. En lugar de calcular la temperatura en tu aplicación, puedes usar una consulta en SQL para convertir estas temperaturas a la escala Celsius directamente en Bigtable antes de enviar los resultados a tu aplicación. Para obtener más información sobre cómo consultar la tabla de datos meteorológicos, consulta Consulta datos de muestra data.

Prácticas recomendadas

Para optimizar el rendimiento y el uso de recursos, considera las siguientes prácticas recomendadas cuando desarrolles aplicaciones:

  • Reutiliza el cliente: Crea un solo BigtableDataClient para tu aplicación. El cliente controla la administración de canales gRPC y está diseñado para una alta simultaneidad. Te recomendamos que crees este cliente una vez después de que se inicie la aplicación y, luego, lo reutilices para cada consulta.
  • Usa consultas con parámetros: Usa PreparedStatement para separar la lógica de consulta de los valores de datos. Esto permite que Bigtable almacene en caché el plan de ejecución y evita la inyección de SQL.
  • Reutiliza las instrucciones preparadas: Llama a PreparedStatement solo una vez por cadena de SQL. Almacena y reutiliza la instrucción en varias solicitudes.

Crea el cliente de Bigtable

Para controlar la conexión entre tu aplicación y Bigtable, crea un BigtableDataClient. En el siguiente ejemplo, se muestra cómo compartir una sola instancia de cliente en tu aplicación para administrar las conexiones de manera eficiente:

BigtableDataSettings settings = BigtableDataSettings.newBuilder()
    .setProject(PROJECT_ID)
    .setInstance(INSTANCE_ID)
    .build();

// The client is thread-safe and should be reused.
try (BigtableDataClient dataClient = BigtableDataClient.create(settings)) {
    System.out.println("Connected to: " + INSTANCE_ID);
}

Prepara la consulta

En lugar de colocar los datos directamente en una cadena de SQL, te recomendamos que uses consultas con parámetros. En GoogleSQL para Bigtable, las consultas con parámetros son el equivalente de una instrucción preparada. Esto te permite separar la lógica de consulta de los valores de datos.

En el siguiente ejemplo, se muestra cómo definir tu consulta de GoogleSQL con marcadores de posición de parámetros, como @sid. Luego, crea un PreparedStatement especificando la cadena de SQL y los tipos para cada parámetro de modo que la consulta se pueda ejecutar más rápido la próxima vez:

Map<String, SqlType<?>> paramTypes = new HashMap<>();
paramTypes.put("sid", SqlType.string());

String sql = "SELECT " +
             "ROUND((CAST(CAST(weather['temp_f'] AS STRING) AS INT64) - 32) * 5 / 9, 2) AS temp_celsius " +
             "FROM " + TABLE_NAME + " WHERE station_id = @sid LIMIT 1";

// Create and reuse the PreparedStatement.
PreparedStatement preparedStatement = dataClient.prepareStatement(sql, paramTypes);

Ejecuta la consulta

Proporciona valores de parámetros y ejecuta la consulta. En el siguiente ejemplo, se muestra cómo usar PreparedStatement para vincular valores a tus parámetros para crear un BoundStatement. Luego, ejecuta la consulta y recorre los resultados para imprimir el ResultSet, que es la temperatura en grados Celsius:

BoundStatement boundStatement = preparedStatement.bind()
    .setStringParam("sid", stationId)
    .build();

try (ResultSet resultSet = dataClient.executeQuery(boundStatement)) {
    while (resultSet.next()) {
        System.out.println("Temp: " + resultSet.getDouble("temp_celsius"));
    }
}

Verifica la estructura de datos

Cuando desarrolles o explores SQL, te recomendamos que explores los metadatos asociados con la tabla para comprender la estructura de datos. Debido a que Bigtable tiene un esquema flexible, si usas consultas SELECT *, los resultados de la consulta pueden cambiar según los datos. Por lo tanto, te recomendamos que no uses consultas SELECT * en aplicaciones de producción.

En el siguiente ejemplo, se observan los metadatos del conjunto de resultados (como los nombres y tipos de columnas) y, luego, se envían de vuelta al cliente de forma independiente de los datos. Esto te permite obtener primero la información de la columna y, luego, crear la estructura de datos que necesitas para procesar los datos en la aplicación.

ResultSetMetadata metadata = resultSet.getMetadata();
System.out.print("Columns: ");
metadata.getColumnsList().forEach(col -> System.out.print(col.getName() + " "));
System.out.println();

Trabaja con familias de columnas

Bigtable almacena datos en familias de columnas. GoogleSQL muestra estas familias como listas de nombres y valores que se conocen como mapas de calificadores y valores de columnas. Cuando se lee una familia de columnas, Bigtable muestra el tipo SqlType.Map. Cuando se lee una familia de columnas de una tabla con historial, como SELECT column FROM my_table(with_history=>true), Bigtable muestra un SqlType.historicalMap() tipo, que es un mapa de calificadores de columnas y un array de marcas de tiempo y valores de celdas. Para obtener más ejemplos de tipos SqlType, consulta SqlType.

En el siguiente ejemplo, se muestra cómo, en lugar de obtener una columna a la vez, puedes obtener un grupo completo de columnas a la vez. El código almacena la familia de columnas en un mapa para que puedas recorrer todos los nombres y valores:

Map<String, SqlType<?>> paramTypes = new HashMap<>();
paramTypes.put("keyPrefix", SqlType.bytes());
String sql = String.format("SELECT weather FROM %s WHERE STARTS_WITH(_key, @keyPrefix)", TABLE_NAME);
PreparedStatement preparedStatement = dataClient.prepareStatement(sql, paramTypes);
BoundStatement boundStatement = preparedStatement.bind()
   .setBytesParam("keyPrefix", ByteString.copyFromUtf8(key))
   .build();


try (ResultSet resultSet = dataClient.executeQuery(boundStatement)) {
   while (resultSet.next()) {
     SqlType.Map<ByteString, ByteString> mapType = SqlType.mapOf(SqlType.bytes(), SqlType.bytes());
     Map<ByteString, ByteString> weatherValues = resultSet.getMap("weather", mapType);
     for (Map.Entry<ByteString, ByteString> entry : weatherValues.entrySet()) {
       System.out.printf("%s = %s\n",
         entry.getKey().toStringUtf8(),
         entry.getValue().toStringUtf8());
   }
 }
}

¿Qué sigue?