Proses Kueri Spanner

Klien

Spanner mendukung kueri SQL. Berikut contoh kueri:

SELECT s.SingerId, s.FirstName, s.LastName, s.SingerInfo
FROM Singers AS s
WHERE s.FirstName = @firstName;

Konstruk @firstName adalah referensi ke parameter kueri. Anda dapat menggunakan parameter kueri di mana saja nilai literal dapat digunakan. Penggunaan parameter di API terprogram sangat direkomendasikan. Penggunaan parameter kueri membantu menghindari serangan injeksi SQL dan kueri yang dihasilkan lebih mungkin diuntungkan dari berbagai cache sisi server. Untuk mengetahui informasi selengkapnya, lihat Menyimpan ke dalam cache.

Parameter kueri harus terikat ke nilai saat kueri dijalankan. Misalnya:

Statement statement =
    Statement.newBuilder("SELECT s.SingerId...").bind("firstName").to("Jimi").build();
try (ResultSet resultSet = dbClient.singleUse().executeQuery(statement)) {
 while (resultSet.next()) {
 ...
 }
}

Setelah menerima panggilan API, Spanner akan menganalisis kueri dan parameter terikat untuk menentukan node server Spanner mana yang harus memproses kueri. Server mengirim kembali aliran baris hasil yang digunakan oleh panggilan ke ResultSet.next().

Eksekusi kueri

Eksekusi kueri dimulai dengan kedatangan permintaan "execute query" di beberapa server Spanner. Server melakukan langkah-langkah berikut:

  • Validasi permintaan
  • Mengurai teks kueri
  • Membuat aljabar kueri awal
  • Membuat aljabar kueri yang dioptimalkan
  • Membuat rencana kueri yang dapat dieksekusi
  • Jalankan rencana (periksa izin, baca data, enkode hasil, dll.)

Diagram alur eksekusi kueri yang menampilkan klien, server root, dan server leaf

Penguraian

Parser SQL menganalisis teks kueri dan mengonversinya menjadi pohon sintaksis abstrak. Layanan ini mengekstrak struktur kueri dasar (SELECT … FROM … WHERE …) dan melakukan pemeriksaan sintaksis.

Aljabar

Sistem jenis Spanner dapat merepresentasikan skalar, array, struktur, dll. Aljabar kueri menentukan operator untuk pemindaian tabel, pemfilteran, pengurutan/pengelompokan, semua jenis gabungan, agregasi, dan banyak lagi. Aljabar kueri awal dibangun dari output parser. Referensi nama kolom di pohon parse diselesaikan menggunakan skema database. Kode ini juga memeriksa error semantik (misalnya, jumlah parameter yang salah, ketidakcocokan jenis, dan sebagainya).

Langkah berikutnya ("pengoptimalan kueri") mengambil aljabar awal dan menghasilkan aljabar yang lebih optimal. Hal ini mungkin lebih sederhana, lebih efisien, atau lebih sesuai dengan kemampuan mesin eksekusi. Misalnya, aljabar awal mungkin hanya menentukan "gabungan", sedangkan aljabar yang dioptimalkan menentukan "gabungan hash".

Eksekusi

Rencana kueri yang dapat dieksekusi akhir dibuat dari aljabar yang ditulis ulang. Pada dasarnya, rencana yang dapat dieksekusi adalah directed acyclic graph dari "iterator". Setiap iterator mengekspos urutan nilai. Iterator dapat menggunakan input untuk menghasilkan output (misalnya, iterator pengurutan). Kueri yang melibatkan satu pemisahan dapat dieksekusi oleh satu server (yang menyimpan data). Server akan memindai rentang dari berbagai tabel, menjalankan gabungan, melakukan penggabungan, dan semua operasi lain yang ditentukan oleh aljabar kueri.

Kueri yang melibatkan beberapa pemisahan akan difaktorkan menjadi beberapa bagian. Sebagian kueri akan terus dieksekusi di server utama (root). Subkueri parsial lainnya diserahkan ke node daun (node yang memiliki pemisahan yang sedang dibaca). Penyerahan ini dapat diterapkan secara rekursif untuk kueri yang kompleks, sehingga menghasilkan hierarki eksekusi server. Semua server menyetujui stempel waktu sehingga hasil kueri adalah ringkasan data yang konsisten. Setiap server leaf mengirim kembali aliran hasil parsial. Untuk kueri yang melibatkan agregasi, ini bisa berupa hasil yang sebagian diagregasi. Server root kueri memproses hasil dari server leaf dan menjalankan sisa rencana kueri. Untuk mengetahui informasi selengkapnya, lihat Rencana eksekusi kueri.

Jika kueri melibatkan beberapa pemisahan, Spanner dapat mengeksekusi kueri secara paralel di seluruh pemisahan. Tingkat paralelisme bergantung pada rentang data yang dipindai kueri, rencana eksekusi kueri, dan distribusi data di seluruh pemisahan. Spanner otomatis menetapkan tingkat paralelisme maksimum untuk kueri berdasarkan ukuran instance dan konfigurasi instance (regional atau multi-region) untuk mencapai performa kueri yang optimal dan menghindari membebani CPU secara berlebihan.

Caching

Banyak artefak pemrosesan kueri di-cache dan digunakan kembali secara otomatis untuk kueri berikutnya. Hal ini mencakup aljabar kueri, paket kueri yang dapat dieksekusi, dll. Penayangan cache didasarkan pada teks kueri, nama, dan jenis parameter terikat. Itulah sebabnya menggunakan parameter terikat (seperti @firstName dalam contoh di atas) lebih baik daripada menggunakan nilai literal dalam teks kueri. Yang pertama dapat di-cache sekali dan digunakan kembali terlepas dari nilai terikat yang sebenarnya. Lihat Mengoptimalkan Performa Kueri Spanner untuk mengetahui detail selengkapnya.

Penanganan error

Metode executeQuery (atau executeStreamingSql) dan streamingRead menampilkan aliran pesan PartialResultSet. Agar efisien, satu nilai baris atau kolom dapat dibagi di beberapa pesan PartialResultSet, terutama untuk data besar.

Streaming ini dapat terganggu oleh error jaringan sementara, pengalihan yang terpisah, atau server yang dimulai ulang. Pengalihan terpisah dapat terjadi selama load balancing dan server dapat dimulai ulang selama upgrade.

Untuk menangani gangguan ini, Spanner menyertakan string resume_token buram resume_token dalam beberapa pesan PartialResultSet.

Poin penting tentang resume_token:

  • Tidak setiap PartialResultSet berisi resume_token.
  • resume_token biasanya hanya disertakan di akhir baris lengkap, menandai titik kelanjutan yang aman.
  • PartialResultSet dengan chunked_value (untuk nilai besar yang dibagi di seluruh pesan) tidak akan memiliki resume_token hingga seluruh nilai dan baris dikirim.
  • Untuk melanjutkan streaming yang terganggu, kirim permintaan baru dengan last received resume_token yang tidak kosong.

Library klien Spanner mengelola buffering dan pemulihan ini secara otomatis. Mereka merakit baris lengkap dari PartialResultSet pesan dan melacak resume_token terbaru. Jika koneksi terputus, library akan menggunakan token valid terakhir untuk memulai ulang streaming, dengan menghapus data parsial yang diterima setelah token tersebut. Proses ini memastikan Anda melihat aliran baris lengkap yang berkelanjutan dan bebas duplikat, meskipun terjadi kegagalan sementara.