Sviluppa applicazioni con GoogleSQL per Bigtable

Puoi utilizzare GoogleSQL per Bigtable per eseguire query dalle tue applicazioni per trasferire le funzioni di calcolo su Bigtable. In questo modo si riduce la necessità di elaborazione lato client. GoogleSQL è un linguaggio di query strutturato conforme allo standard ANSI implementato per Bigtable e altri servizi. Google Cloud Per ulteriori informazioni su altri utilizzi di GoogleSQL per Bigtable, consulta la panoramica di GoogleSQL per Bigtable.

Prima di iniziare

Assicurati di disporre di quanto segue:

Scenario di esempio

Gli esempi in questa guida utilizzano una tabella weather-data di esempio a cui puoi accedere dall'istanza della prova senza costi nella consoleGoogle Cloud . Supponiamo che la tabella memorizzi informazioni provenienti da diverse stazioni meteo identificate da un station_id. Ogni stazione registra la temperatura in gradi Fahrenheit in una colonna denominata temp_f. Anziché calcolare la temperatura nella tua applicazione, puoi utilizzare una query SQL per convertire queste temperature in gradi Celsius direttamente in Bigtable prima di inviare i risultati alla tua applicazione. Per maggiori informazioni sull'esecuzione di query sulla tabella dei dati meteo, consulta Query di esempio sui dati.

Best practice

Per ottimizzare il rendimento e l'utilizzo delle risorse, tieni presente le seguenti best practice quando sviluppi applicazioni:

  • Riutilizza il client: crea un singolo BigtableDataClient per la tua applicazione. Il client gestisce la gestione dei canali gRPC ed è progettato per un'elevata concorrenza. Ti consigliamo di creare questo client una sola volta dopo l'avvio dell'applicazione e di riutilizzarlo per ogni query.
  • Utilizza query con parametri:utilizza PreparedStatement per separare la logica della query dai valori dei dati. In questo modo, Bigtable può memorizzare nella cache il piano di esecuzione e impedire l'SQL injection.
  • Riutilizza le istruzioni preparate:chiama PreparedStatement una sola volta per stringa SQL. Memorizzare e riutilizzare l'affermazione in più richieste.

Crea il client Bigtable

Per gestire la connessione tra l'applicazione e Bigtable, crea un BigtableDataClient. Il seguente esempio mostra come condividere una singola istanza client nell'applicazione per gestire le connessioni in modo efficiente:

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 query

Anziché inserire i dati direttamente in una stringa SQL, ti consigliamo di utilizzare query con parametri. In GoogleSQL per Bigtable, le query con parametri sono l'equivalente di un'istruzione preparata. In questo modo puoi separare la logica della query dai valori dei dati.

Il seguente esempio mostra come definire la query GoogleSQL utilizzando segnaposto dei parametri, ad esempio @sid. Poi crea un PreparedStatement specificando la stringa SQL e i tipi per ogni parametro in modo che la query possa essere eseguita più velocemente la volta successiva:

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

Esegui la query

Fornisci i valori dei parametri ed esegui la query. Il seguente esempio mostra come utilizzare PreparedStatement per associare valori ai parametri per creare un BoundStatement. Dopodiché esegue la query e scorre i risultati per stampare ResultSet, ovvero la temperatura in gradi 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"));
    }
}

Controllare la struttura dei dati

Quando sviluppi o esplori SQL, ti consigliamo di esaminare i metadati associati alla tabella per comprendere la struttura dei dati. Poiché Bigtable ha uno schema flessibile, se utilizzi query SELECT *, i risultati della query possono variare in base ai dati. Pertanto, ti consigliamo di non utilizzare query SELECT * nelle applicazioni di produzione.

Il seguente esempio esamina i metadati del set di risultati (ad esempio i nomi e i tipi delle colonne) e li invia di nuovo al client indipendentemente dai dati. In questo modo puoi ottenere prima le informazioni sulle colonne e poi creare la struttura dei dati necessaria per elaborare i dati nell'applicazione.

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

Lavorare con le famiglie di colonne

Bigtable archivia i dati nelle famiglie di colonne. GoogleSQL restituisce queste famiglie come elenchi di nomi e valori noti come mappe di qualificatori di colonne e valori. Quando legge una famiglia di colonne, Bigtable restituisce il tipo SqlType.Map. Quando leggi una famiglia di colonne da una tabella con cronologia, ad esempio SELECT column FROM my_table(with_history=>true), Bigtable restituisce un tipo SqlType.historicalMap(), che è una mappa dei qualificatori di colonna e un array di timestamp e valori delle celle. Per altri esempi di tipi di SqlType, vedi SqlType.

L'esempio seguente mostra come, anziché ottenere una colonna alla volta, puoi ottenere un intero gruppo di colonne contemporaneamente. Il codice memorizza la famiglia di colonne in una mappa in modo da poter scorrere tutti i nomi e i valori:

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

Passaggi successivi