Mengembangkan aplikasi dengan GoogleSQL untuk Bigtable

Anda dapat menggunakan GoogleSQL untuk Bigtable guna menjalankan kueri dari aplikasi Anda untuk memindahkan fungsi komputasi ke Bigtable. Hal ini mengurangi kebutuhan akan pemrosesan sisi klien. GoogleSQL adalah bahasa kueri terstruktur yang sesuai dengan ANSI dan diimplementasikan untuk Bigtable dan layanan Google Cloud lainnya. Untuk mengetahui informasi selengkapnya tentang penggunaan GoogleSQL untuk Bigtable lainnya, lihat Ringkasan GoogleSQL untuk Bigtable.

Sebelum memulai

Pastikan Anda memiliki:

Contoh skenario

Contoh dalam panduan ini menggunakan tabel weather-data contoh yang dapat Anda akses dari instance uji coba gratis di Google Cloud console. Misalkan tabel Anda menyimpan informasi dari berbagai stasiun cuaca yang diidentifikasi oleh station_id. Setiap stasiun mencatat suhu dalam skala Fahrenheit dalam kolom bernama temp_f. Daripada menghitung suhu di aplikasi, Anda dapat menggunakan kueri SQL untuk mengonversi suhu ini ke skala Celsius langsung di Bigtable sebelum mengirimkan hasilnya kembali ke aplikasi Anda. Untuk mengetahui informasi selengkapnya tentang cara membuat kueri tabel data cuaca, lihat Membuat kueri contoh data.

Praktik terbaik

Untuk mengoptimalkan performa dan penggunaan resource, pertimbangkan praktik terbaik berikut saat Anda mengembangkan aplikasi:

  • Menggunakan kembali klien: buat satu BigtableDataClient untuk aplikasi Anda. Klien menangani pengelolaan saluran gRPC dan dirancang untuk konkurensi tinggi. Sebaiknya Anda membuat klien ini sekali setelah aplikasi Anda dimulai, lalu menggunakannya kembali untuk setiap kueri.
  • Gunakan kueri berparameter: gunakan PreparedStatement untuk memisahkan logika kueri dari nilai data. Dengan begitu, Bigtable dapat meng-cache rencana eksekusi dan mencegah injeksi SQL.
  • Gunakan kembali pernyataan yang disiapkan: hanya panggil PreparedStatement satu kali per string SQL. Menyimpan dan menggunakan kembali pernyataan di beberapa permintaan.

Buat klien Bigtable

Untuk menangani koneksi antara aplikasi dan Bigtable, buat BigtableDataClient. Contoh berikut menunjukkan cara membagikan satu instance klien di seluruh aplikasi Anda untuk mengelola koneksi secara efisien:

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

Menyiapkan kueri

Daripada memasukkan data Anda langsung ke string SQL, sebaiknya Anda menggunakan kueri berparameter. Di GoogleSQL untuk Bigtable, kueri berparameter setara dengan pernyataan yang disiapkan. Dengan demikian, Anda dapat memisahkan logika kueri dari nilai data.

Contoh berikut menunjukkan cara menentukan kueri GoogleSQL menggunakan placeholder parameter, seperti @sid. Kemudian, buat PreparedStatement dengan menentukan string SQL dan jenis untuk setiap parameter sehingga kueri dapat berjalan lebih cepat pada waktu berikutnya:

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

Menjalankan kueri

Berikan nilai parameter dan jalankan kueri. Contoh berikut menunjukkan cara menggunakan PreparedStatement untuk mengikat nilai ke parameter Anda guna membuat BoundStatement. Kemudian, kueri akan berjalan dan mengulang hasil untuk mencetak ResultSet, yang merupakan suhu dalam derajat 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"));
    }
}

Periksa struktur data

Saat mengembangkan atau menjelajahi SQL, sebaiknya jelajahi metadata yang terkait dengan tabel untuk memahami struktur data. Karena Bigtable memiliki skema yang fleksibel, jika Anda menggunakan kueri SELECT *, hasil kueri Anda dapat berubah berdasarkan data. Oleh karena itu, sebaiknya Anda tidak menggunakan kueri SELECT * dalam aplikasi produksi.

Contoh berikut melihat metadata set hasil (seperti nama dan jenis kolom), lalu mengirimkannya kembali ke klien secara terpisah dari data. Dengan demikian, Anda dapat memperoleh informasi kolom terlebih dahulu, lalu membuat struktur data yang diperlukan untuk memproses data dalam aplikasi.

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

Bekerja dengan grup kolom

Bigtable menyimpan data dalam grup kolom. GoogleSQL menampilkan grup ini sebagai daftar nama dan nilai yang dikenal sebagai peta kualifikasi kolom dan nilai. Saat membaca grup kolom, Bigtable akan menampilkan jenis SqlType.Map. Saat membaca grup kolom dari tabel dengan histori, seperti SELECT column FROM my_table(with_history=>true), Bigtable akan menampilkan jenis SqlType.historicalMap(), yang merupakan peta penentu kolom dan array stempel waktu serta nilai sel. Untuk contoh jenis SqlType lainnya, lihat SqlType.

Contoh berikut menunjukkan cara mendapatkan seluruh grup kolom sekaligus, bukan mendapatkan satu kolom dalam satu waktu. Kode menyimpan grup kolom dalam peta sehingga Anda dapat melakukan loop di semua nama dan nilai:

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

Langkah berikutnya