Google Cloud Spanner adalah layanan database relasional skalabel dan terkelola sepenuhnya secara global yang menyediakan transaksi ACID dan semantik SQL tanpa merusak performa dan ketersediaan yang tinggi.
Di lab ini, Anda akan mempelajari cara menyiapkan instance Cloud Spanner. Anda akan menjalani langkah-langkah untuk membuat database dan skema yang dapat digunakan untuk papan peringkat game. Anda akan mulai dengan membuat tabel Pemain untuk menyimpan informasi pemain dan tabel Skor untuk menyimpan skor pemain.
Selanjutnya, Anda akan mengisi tabel dengan data contoh. Kemudian Anda akan mengakhiri lab dengan menjalankan kueri contoh Sepuluh Teratas dan menghapus instance untuk mengosongkan resource.
Yang akan Anda pelajari
- Cara menyiapkan instance Cloud Spanner.
- Cara membuat database dan tabel.
- Cara menggunakan kolom stempel waktu commit.
- Cara memuat data ke tabel database Cloud Spanner dengan stempel waktu.
- Cara membuat kueri database Cloud Spanner.
- Cara menghapus instance Cloud Spanner.
Yang akan Anda butuhkan
Bagaimana Anda akan menggunakan tutorial ini?
Bagaimana penilaian Anda terhadap pengalaman dengan Google Cloud Platform?
Penyiapan lingkungan mandiri
Jika belum memiliki Akun Google (Gmail atau Google Apps), Anda harus membuatnya. Login ke Google Cloud Platform console (console.cloud.google.com) dan buat project baru.
Jika Anda sudah memiliki project, klik menu pull-down pilihan project di kiri atas konsol:
dan klik tombol 'PROJECT BARU' dalam dialog yang dihasilkan untuk membuat project baru:
Jika belum memiliki project, Anda akan melihat dialog seperti ini untuk membuat project pertama:
Dialog pembuatan project berikutnya memungkinkan Anda memasukkan detail project baru:
Ingat project ID yang merupakan nama unik di semua project Google Cloud (maaf, nama di atas telah digunakan dan tidak akan berfungsi untuk Anda!) Project ID tersebut selanjutnya akan dirujuk di codelab ini sebagai PROJECT_ID
.
Selanjutnya, jika Anda belum melakukannya, Anda harus mengaktifkan penagihan di Developers Console untuk menggunakan resource Google Cloud dan mengaktifkan Cloud Spanner API.
Menjalankan melalui codelab ini tidak akan menghabiskan biaya lebih dari beberapa dolar, tetapi bisa lebih jika Anda memutuskan untuk menggunakan lebih banyak resource atau jika Anda membiarkannya berjalan (lihat bagian "pembersihan" di akhir dokumen ini). Harga Google Cloud Spanner didokumentasikan di sini.
Pengguna baru Google Cloud Platform memenuhi syarat untuk mendapatkan uji coba gratis senilai $300, yang menjadikan codelab ini sepenuhnya gratis.
Penyiapan Google Cloud Shell
Meskipun Google Cloud dan Spanner dapat dioperasikan dari jarak jauh menggunakan laptop Anda, dalam codelab ini, kita akan menggunakan Google Cloud Shell, lingkungan command line yang berjalan di Cloud.
Mesin virtual berbasis Debian ini memuat semua alat pengembangan yang akan Anda perlukan. Layanan ini menawarkan direktori beranda tetap sebesar 5 GB dan beroperasi di Google Cloud, sehingga sangat meningkatkan performa dan autentikasi jaringan. Ini berarti bahwa semua yang Anda perlukan untuk codelab ini adalah browser (ya, ini berfungsi di Chromebook).
- Untuk mengaktifkan Cloud Shell dari Cloud Console, cukup klik Aktifkan Cloud Shell (hanya perlu beberapa saat untuk melakukan provisioning dan terhubung ke lingkungan).
Setelah terhubung ke Cloud Shell, Anda akan melihat bahwa Anda sudah diautentikasi dan project sudah ditetapkan ke PROJECT_ID
.
gcloud auth list
Output perintah
Credentialed accounts: - <myaccount>@<mydomain>.com (active)
gcloud config list project
Output perintah
[core] project = <PROJECT_ID>
Jika, untuk beberapa alasan, project belum disetel, cukup jalankan perintah berikut:
gcloud config set project <PROJECT_ID>
Mencari PROJECT_ID
Anda? Periksa ID yang Anda gunakan di langkah-langkah penyiapan atau cari di dasbor Cloud Console:
Cloud Shell juga menetapkan beberapa variabel lingkungan secara default, yang mungkin berguna saat Anda menjalankan perintah di masa mendatang.
echo $GOOGLE_CLOUD_PROJECT
Output perintah
<PROJECT_ID>
- Terakhir, tetapkan zona dan konfigurasi project default.
gcloud config set compute/zone us-central1-f
Anda dapat memilih berbagai zona yang berbeda. Untuk informasi selengkapnya, lihat Region & Zona.
Ringkasan
Pada langkah ini, Anda menyiapkan lingkungan.
Berikutnya
Selanjutnya, Anda akan menyiapkan Instance Cloud Spanner.
Pada langkah ini kita menyiapkan Instance Cloud Spanner untuk codelab ini. Telusuri entri Spanner di kiri atas Menu Tiga Garis atau telusuri Spanner dengan menekan "/" dan ketik "Spanner"
Selanjutnya, klik dan isi formulir dengan memasukkan nama instance cloudspanner-leaderboard untuk instance Anda, memilih konfigurasi (pilih instance regional), dan menetapkan jumlah node, untuk codelab ini kita hanya perlu 1 node. Agar instance produksi dan untuk memenuhi syarat untuk SLA Cloud Spanner, Anda harus menjalankan 3 node atau lebih di instance Cloud Spanner.
Terakhir, namun tidak kalah penting, klik "Buat" dan dalam beberapa detik Anda sudah memiliki instance Cloud Spanner.
Pada langkah berikutnya, kita akan menggunakan library klien Go untuk membuat database dan skema dalam instance baru.
Pada langkah ini, kita akan membuat contoh database dan skema.
Mari menggunakan library klien Go untuk membuat dua tabel; tabel Pemain untuk info pemain dan tabel Skor untuk menyimpan skor pemain. Untuk melakukannya, kita akan memandu langkah-langkah pembuatan aplikasi konsol Go di Cloud Shell.
Pertama-tama, clone kode sampel untuk codelab ini dari GitHub dengan mengetik perintah berikut di Cloud Shell:
go get -u github.com/GoogleCloudPlatform/golang-samples/spanner/...
Kemudian, ubah direktori ke direktori "papan peringkat" tempat Anda akan membuat aplikasi.
cd gopath/src/github.com/GoogleCloudPlatform/golang-samples/spanner/spanner_leaderboard
Semua kode yang diperlukan untuk codelab ini terletak di direktori golang-samples/spanner/spanner_leaderboard/
yang ada sebagai aplikasi Go yang dapat dijalankan dengan nama leaderboard
untuk berfungsi sebagai referensi saat Anda melanjutkan codelab. Kita akan membuat direktori baru dan mem-build salinan aplikasi Papan Peringkat secara bertahap.
Buat direktori baru bernama "codelab" untuk aplikasi dan ubah direktori menjadi papan peringkat dengan perintah berikut:
mkdir codelab && cd $_
Sekarang mari kita perbarui pembuatan aplikasi Go dasar bernama "Papan Peringkat" yang menggunakan library klien Spanner untuk membuat papan peringkat yang terdiri dari dua tabel; Pemain dan Skor. Anda dapat melakukannya langsung di Cloud Shell Editor:
Buka Cloud Shell Editor, dengan mengklik ikon yang disorot di bawah:
Buat file bernama "leaderboard.go" dalam folder ~/gopath/src/github.com/GoogleCloudPlatform/golang-samples/spanner/codelab.
- Pertama, pastikan bahwa Anda telah memilih folder "codelab" di daftar folder Cloud Shell Editor.
- Kemudian pilih "File Baru" di bagian menu "File" pada Cloud Shell Editor.
- Masukkan "leaderboard.go" sebagai nama untuk file baru.
Ini adalah file utama aplikasi yang akan berisi kode aplikasi dan referensi untuk menyertakan dependensi.
Untuk membuat database leaderboard
serta tabel Players
dan Scores
, salin (Ctrl + P) dan tempel (Ctrl + V) kode Go berikut ke file leaderboard.go
:
package main
import (
"context"
"flag"
"fmt"
"io"
"log"
"os"
"regexp"
"time"
"cloud.google.com/go/spanner"
database "cloud.google.com/go/spanner/admin/database/apiv1"
adminpb "google.golang.org/genproto/googleapis/spanner/admin/database/v1"
)
type adminCommand func(ctx context.Context, w io.Writer, adminClient *database.DatabaseAdminClient, database string) error
func createDatabase(ctx context.Context, w io.Writer, adminClient *database.DatabaseAdminClient, db string) error {
matches := regexp.MustCompile("^(.*)/databases/(.*)$").FindStringSubmatch(db)
if matches == nil || len(matches) != 3 {
return fmt.Errorf("Invalid database id %s", db)
}
op, err := adminClient.CreateDatabase(ctx, &adminpb.CreateDatabaseRequest{
Parent: matches[1],
CreateStatement: "CREATE DATABASE `" + matches[2] + "`",
ExtraStatements: []string{
`CREATE TABLE Players(
PlayerId INT64 NOT NULL,
PlayerName STRING(2048) NOT NULL
) PRIMARY KEY(PlayerId)`,
`CREATE TABLE Scores(
PlayerId INT64 NOT NULL,
Score INT64 NOT NULL,
Timestamp TIMESTAMP NOT NULL
OPTIONS(allow_commit_timestamp=true)
) PRIMARY KEY(PlayerId, Timestamp),
INTERLEAVE IN PARENT Players ON DELETE NO ACTION`,
},
})
if err != nil {
return err
}
if _, err := op.Wait(ctx); err != nil {
return err
}
fmt.Fprintf(w, "Created database [%s]\n", db)
return nil
}
func createClients(ctx context.Context, db string) (*database.DatabaseAdminClient, *spanner.Client) {
adminClient, err := database.NewDatabaseAdminClient(ctx)
if err != nil {
log.Fatal(err)
}
dataClient, err := spanner.NewClient(ctx, db)
if err != nil {
log.Fatal(err)
}
return adminClient, dataClient
}
func run(ctx context.Context, adminClient *database.DatabaseAdminClient, dataClient *spanner.Client, w io.Writer,
cmd string, db string, timespan int) error {
// createdatabase command
if cmd == "createdatabase" {
err := createDatabase(ctx, w, adminClient, db)
if err != nil {
fmt.Fprintf(w, "%s failed with %v", cmd, err)
}
return err
}
return nil
}
func main() {
flag.Usage = func() {
fmt.Fprintf(os.Stderr, `Usage: leaderboard <command> <database_name> [command_option]
Command can be one of: createdatabase
Examples:
leaderboard createdatabase projects/my-project/instances/my-instance/databases/example-db
- Create a sample Cloud Spanner database along with sample tables in your project.
`)
}
flag.Parse()
flagCount := len(flag.Args())
if flagCount != 2 {
flag.Usage()
os.Exit(2)
}
cmd, db := flag.Arg(0), flag.Arg(1)
// Set timespan to zero, as it's not currently being used
var timespan int = 0
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute)
defer cancel()
adminClient, dataClient := createClients(ctx, db)
if err := run(ctx, adminClient, dataClient, os.Stdout, cmd, db, timespan); err != nil {
os.Exit(1)
}
}
Simpan perubahan yang Anda buat ke file leaderboard.go
dengan memilih "Simpan" di bagian menu "File" pada Cloud Shell Editor.
Anda dapat menggunakan file leaderboard.go
di direktori golang-samples/spanner/spanner_leaderboard
untuk melihat contoh tampilan file leaderboard.go
setelah menambahkan kode untuk mengaktifkan perintah createdatabase
.
Untuk membuat aplikasi Anda di Cloud Shell, jalankan "go build" dari direktori codelab
tempat file leaderboard.go
Anda berada:
go build leaderboard.go
Setelah aplikasi Anda berhasil dibuat, jalankan aplikasi yang dihasilkan di Cloud Shell dengan memasukkan perintah berikut:
./leaderboard
Anda akan melihat output seperti berikut:
Usage: leaderboard <command> <database_name> [command_option] Command can be one of: createdatabase Examples: leaderboard createdatabase projects/my-project/instances/my-instance/databases/example-db - Create a sample Cloud Spanner database along with sample tables in your project.
Dari respons ini, kita dapat melihat bahwa ini adalah aplikasi Leaderboard
yang saat ini memiliki satu kemungkinan perintah: createdatabase
. Kita dapat melihat bahwa argumen yang diharapkan dari perintah createdatabase
adalah string yang berisi ID Instance dan ID Database tertentu.
Sekarang jalankan perintah berikut. Pastikan Anda mengganti my-project
dengan Project ID yang Anda buat di awal codelab ini.
./leaderboard createdatabase projects/my-project/instances/cloudspanner-leaderboard/databases/leaderboard
Setelah beberapa detik, Anda akan melihat respons seperti berikut:
Created database [projects/my-project/instances/cloudspanner-leaderboard/databases/leaderboard]
Di bagian Cloud Spanner pada Cloud Console, Anda akan melihat database dan tabel baru muncul di menu sebelah kiri.
Pada langkah berikutnya, kita akan mengupdate aplikasi untuk memuat beberapa data ke database baru Anda.
Sekarang kita memiliki database yang disebut leaderboard
yang berisi dua tabel; Players
dan Scores
Sekarang mari kita gunakan library klien Go untuk mengisi tabel Players
dengan pemain dan tabel Scores
dengan skor acak untuk setiap pemain.
Jika belum terbuka, buka Cloud Shell Editor dengan mengklik ikon yang ditandai di bawah:
Selanjutnya, edit file leaderboard.go
di Cloud Shell Editor untuk menambahkan perintah insertplayers
yang dapat digunakan untuk menyisipkan 100 pemain ke dalam tabel Players
. Kita juga akan menambahkan perintah insertscores
yang dapat digunakan untuk memasukkan 4 skor acak dalam tabel Scores
untuk setiap pemain dalam tabel Players
.
Pertama-tama perbarui bagian imports
di bagian atas file leaderboard.go
, menggantikan apa yang saat ini ada, sehingga setelah selesai akan terlihat seperti berikut:
import (
"context"
"flag"
"fmt"
"io"
"log"
"math/rand"
"os"
"regexp"
"time"
"cloud.google.com/go/spanner"
database "cloud.google.com/go/spanner/admin/database/apiv1"
"google.golang.org/api/iterator"
adminpb "google.golang.org/genproto/googleapis/spanner/admin/database/v1"
)
Kemudian tambahkan tipe perintah baru bersama daftar perintah di bagian atas file, tepat di bawah baris yang dimulai dengan "type adminCommand ..." sehingga bila Anda sudah selesai akan terlihat seperti berikut:
type adminCommand func(ctx context.Context, w io.Writer, adminClient *database.DatabaseAdminClient, database string) error
type command func(ctx context.Context, w io.Writer, client *spanner.Client) error
var (
commands = map[string]command{
"insertplayers": insertPlayers,
"insertscores": insertScores,
}
)
Selanjutnya, tambahkan fungsi insertPlayers dan insertScores berikut di bawah fungsi createdatabase()
yang ada:
func insertPlayers(ctx context.Context, w io.Writer, client *spanner.Client) error {
// Get number of players to use as an incrementing value for each PlayerName to be inserted
stmt := spanner.Statement{
SQL: `SELECT Count(PlayerId) as PlayerCount FROM Players`,
}
iter := client.Single().Query(ctx, stmt)
defer iter.Stop()
row, err := iter.Next()
if err != nil {
return err
}
var numberOfPlayers int64 = 0
if err := row.Columns(&numberOfPlayers); err != nil {
return err
}
// Intialize values for random PlayerId
rand.Seed(time.Now().UnixNano())
min := 1000000000
max := 9000000000
// Insert 100 player records into the Players table
_, err = client.ReadWriteTransaction(ctx, func(ctx context.Context, txn *spanner.ReadWriteTransaction) error {
stmts := []spanner.Statement{}
for i := 1; i <= 100; i++ {
numberOfPlayers++
playerID := rand.Intn(max-min) + min
playerName := fmt.Sprintf("Player %d", numberOfPlayers)
stmts = append(stmts, spanner.Statement{
SQL: `INSERT INTO Players
(PlayerId, PlayerName)
VALUES (@playerID, @playerName)`,
Params: map[string]interface{}{
"playerID": playerID,
"playerName": playerName,
},
})
}
_, err := txn.BatchUpdate(ctx, stmts)
if err != nil {
return err
}
return nil
})
fmt.Fprintf(w, "Inserted players \n")
return nil
}
func insertScores(ctx context.Context, w io.Writer, client *spanner.Client) error {
playerRecordsFound := false
// Create slice for insert statements
stmts := []spanner.Statement{}
// Select all player records
stmt := spanner.Statement{SQL: `SELECT PlayerId FROM Players`}
iter := client.Single().Query(ctx, stmt)
defer iter.Stop()
// Insert 4 score records into the Scores table for each player in the Players table
for {
row, err := iter.Next()
if err == iterator.Done {
break
}
if err != nil {
return err
}
playerRecordsFound = true
var playerID int64
if err := row.ColumnByName("PlayerId", &playerID); err != nil {
return err
}
// Intialize values for random score and date
rand.Seed(time.Now().UnixNano())
min := 1000
max := 1000000
for i := 0; i < 4; i++ {
// Generate random score between 1,000 and 1,000,000
score := rand.Intn(max-min) + min
// Generate random day within the past two years
now := time.Now()
endDate := now.Unix()
past := now.AddDate(0, -24, 0)
startDate := past.Unix()
randomDateInSeconds := rand.Int63n(endDate-startDate) + startDate
randomDate := time.Unix(randomDateInSeconds, 0)
// Add insert statement to stmts slice
stmts = append(stmts, spanner.Statement{
SQL: `INSERT INTO Scores
(PlayerId, Score, Timestamp)
VALUES (@playerID, @score, @timestamp)`,
Params: map[string]interface{}{
"playerID": playerID,
"score": score,
"timestamp": randomDate,
},
})
}
}
if !playerRecordsFound {
fmt.Fprintln(w, "No player records currently exist. First insert players then insert scores.")
} else {
_, err := client.ReadWriteTransaction(ctx, func(ctx context.Context, txn *spanner.ReadWriteTransaction) error {
// Commit insert statements for all scores to be inserted as a single transaction
_, err := txn.BatchUpdate(ctx, stmts)
return err
})
if err != nil {
return err
}
fmt.Fprintln(w, "Inserted scores")
}
return nil
}
Kemudian, untuk membuat perintah insert
berfungsi, tambahkan kode berikut ke fungsi "run" Aplikasi Anda di bawah pernyataan penanganan createdatabase
, menggantikan pernyataan return nil
:
// insert and query commands
cmdFn := commands[cmd]
if cmdFn == nil {
flag.Usage()
os.Exit(2)
}
err := cmdFn(ctx, w, dataClient)
if err != nil {
fmt.Fprintf(w, "%s failed with %v", cmd, err)
}
return err
Setelah selesai, fungsi run
akan terlihat seperti berikut:
func run(ctx context.Context, adminClient *database.DatabaseAdminClient, dataClient *spanner.Client, w io.Writer,
cmd string, db string, timespan int) error {
// createdatabase command
if cmd == "createdatabase" {
err := createDatabase(ctx, w, adminClient, db)
if err != nil {
fmt.Fprintf(w, "%s failed with %v", cmd, err)
}
return err
}
// insert and query commands
cmdFn := commands[cmd]
if cmdFn == nil {
flag.Usage()
os.Exit(2)
}
err := cmdFn(ctx, w, dataClient)
if err != nil {
fmt.Fprintf(w, "%s failed with %v", cmd, err)
}
return err
}
Langkah terakhir untuk menyelesaikan penambahan fungsi "insert" ke aplikasi Anda adalah menambahkan teks bantuan untuk perintah "insertplayers" dan "insertscores" ke fungsi flag.Usage()
. Tambahkan teks bantuan berikut ke fungsi flag.Usage()
untuk menyertakan teks bantuan untuk perintah insert:
Tambahkan kedua perintah ke daftar kemungkinan perintah:
Command can be one of: createdatabase, insertplayers, insertscores
Dan tambahkan teks bantuan tambahan ini di bawah teks bantuan untuk perintah createdatabase
.
leaderboard insertplayers projects/my-project/instances/my-instance/databases/example-db
- Insert 100 sample Player records into the database.
leaderboard insertscores projects/my-project/instances/my-instance/databases/example-db
- Insert sample score data into Scores sample Cloud Spanner database table.
Simpan perubahan yang Anda buat ke file leaderboard.go
dengan memilih "Simpan" di bagian menu "File" pada Cloud Shell Editor.
Anda dapat menggunakan file leaderboard.go
di direktori golang-samples/spanner/spanner_leaderboard
untuk melihat contoh tampilan file leaderboard.go
setelah menambahkan kode untuk mengaktifkan perintah insertplayers
dan insertscores
.
Sekarang, mari kita buat dan jalankan aplikasi untuk mengonfirmasi bahwa perintah insertplayers
dan insertscores
baru disertakan dalam daftar kemungkinan perintah aplikasi. Jalankan perintah berikut untuk membuild aplikasi:
go build leaderboard.go
Jalankan aplikasi yang dihasilkan dalam Cloud Shell dengan memasukkan perintah berikut:
./leaderboard
Anda akan melihat perintah insertplayers
dan insertscores
kini disertakan dalam output default aplikasi:
Usage: leaderboard <command> <database_name> [command_option] Command can be one of: createdatabase, insertplayers, insertscores Examples: leaderboard createdatabase projects/my-project/instances/my-instance/databases/example-db - Create a sample Cloud Spanner database along with sample tables in your project. leaderboard insertplayers projects/my-project/instances/my-instance/databases/example-db - Insert 100 sample Player records into the database. leaderboard insertscores projects/my-project/instances/my-instance/databases/example-db - Insert sample score data into Scores sample Cloud Spanner database table.
Sekarang mari kita jalankan perintah insertplayers
dengan nilai argumen yang sama dengan yang kita gunakan saat memanggil perintah createdatabase
. Pastikan Anda mengganti my-project
dengan Project ID yang Anda buat di awal codelab ini.
./leaderboard insertplayers projects/my-project/instances/cloudspanner-leaderboard/databases/leaderboard
Setelah beberapa detik, Anda akan melihat respons seperti berikut:
Inserted players
Sekarang mari kita gunakan library klien Go untuk mengisi tabel Scores
dengan empat skor acak beserta stempel waktu untuk setiap pemain di tabel Players
.
Kolom Timestamp
pada tabel Scores
ditetapkan sebagai kolom "stempel waktu commit" melalui pernyataan SQL berikut yang dijalankan saat sebelumnya menjalankan perintah create
:
CREATE TABLE Scores(
PlayerId INT64 NOT NULL,
Score INT64 NOT NULL,
Timestamp TIMESTAMP NOT NULL OPTIONS(allow_commit_timestamp=true)
) PRIMARY KEY(PlayerId, Timestamp),
INTERLEAVE IN PARENT Players ON DELETE NO ACTION
Perhatikan atribut OPTIONS(allow_commit_timestamp=true)
. Ini membuat Timestamp
menjadi kolom "stempel waktu commit" dan mengaktifkannya untuk diisi secara otomatis dengan stempel waktu transaksi yang tepat untuk operasi INSERT dan UPDATE pada baris tabel tertentu.
Anda juga dapat menyisipkan nilai stempel waktu Anda sendiri ke dalam kolom "stempel waktu commit" selama Anda menyisipkan stempel waktu dengan nilai yang sudah berlalu, yang akan kita lakukan untuk tujuan codelab ini.
Sekarang, jalankan perintah insertscores
dengan nilai argumen yang sama dengan yang digunakan saat memanggil perintah insertplayers
. Pastikan Anda mengganti my-project
dengan Project ID yang Anda buat di awal codelab ini.
./leaderboard insertscores projects/my-project/instances/cloudspanner-leaderboard/databases/leaderboard
Setelah beberapa detik, Anda akan melihat respons seperti berikut:
Inserted scores
Menjalankan fungsi insertScores
menggunakan cuplikan kode berikut untuk menyisipkan stempel waktu yang dibuat secara acak dengan waktu tanggal yang terjadi di masa lalu:
now := time.Now()
endDate := now.Unix()
past := now.AddDate(0, -24, 0)
startDate := past.Unix()
randomDateInSeconds := rand.Int63n(endDate-startDate) + startDate
randomDate := time.Unix(randomDateInSeconds, 0)
stmts = append(stmts, spanner.Statement{
SQL: `INSERT INTO Scores
(PlayerId, Score, Timestamp)
VALUES (@playerID, @score, @timestamp)`,
Params: map[string]interface{}{
"playerID": playerID,
"score": score,
"timestamp": randomDate,
},
})
Untuk mengisi kolom Timestamp
secara otomatis dengan stempel waktu tepat saat transaksi "Insert" terjadi, Anda dapat menyisipkan konstanta Go spanner.CommitTimestamp
seperti dalam cuplikan kode berikut:
...
stmts = append(stmts, spanner.Statement{
SQL: `INSERT INTO Scores
(PlayerId, Score, Timestamp)
VALUES (@playerID, @score, @timestamp)`,
Params: map[string]interface{}{
"playerID": playerID,
"score": score,
"timestamp": spanner.CommitTimestamp,
},
})
Setelah kita menyelesaikan pemuatan data, verifikasi nilai yang baru saja ditulis ke tabel baru di bagian Cloud Spanner pada Cloud Console. Pertama-tama, pilih database leaderboard
, lalu pilih tabel Players
. Klik tab Data
. Anda akan melihat bahwa Anda memiliki data di kolom PlayerId
dan PlayerName
tabel.
Selanjutnya, pastikan tabel Skor juga memiliki data dengan mengklik tabel Scores
dan memilih tab Data
. Anda akan melihat bahwa Anda memiliki data di kolom PlayerId
, Timestamp
, dan Score
tabel.
Bagus! Mari mengupdate aplikasi untuk menjalankan beberapa kueri yang dapat kita gunakan untuk membuat papan peringkat game.
Setelah menyiapkan database dan memuat informasi ke dalam tabel, mari kita buat papan peringkat menggunakan data ini. Untuk melakukannya, kita harus menjawab empat pertanyaan berikut:
- Pemain mana yang masuk peringkat "Sepuluh Teratas" sepanjang waktu?
- Pemain mana yang masuk peringkat "Sepuluh Teratas" tahun ini?
- Pemain mana yang masuk peringkat "Sepuluh Teratas" bulan ini?
- Pemain mana yang masuk peringkat "Sepuluh Teratas" minggu ini?
Mari mengupdate aplikasi untuk menjalankan kueri SQL yang akan menjawab pertanyaan ini.
Kita akan menambahkan perintah query
dan perintah queryWithTimespan
yang akan memberikan cara untuk menjalankan kueri guna menjawab pertanyaan yang akan menghasilkan informasi yang diperlukan untuk papan peringkat.
Edit file leaderboard.go
di Cloud Shell Editor untuk mengupdate aplikasi guna menambahkan perintah query
dan perintah queryWithTimespan
. Kita juga akan menambahkan fungsi bantuan formatWithCommas
untuk memformat skor dengan koma.
Pertama-tama perbarui bagian imports
di bagian atas file leaderboard.go
, menggantikan apa yang saat ini ada, sehingga setelah selesai akan terlihat seperti berikut:
import (
"bytes"
"context"
"flag"
"fmt"
"io"
"log"
"math/rand"
"os"
"regexp"
"strconv"
"time"
"cloud.google.com/go/spanner"
database "cloud.google.com/go/spanner/admin/database/apiv1"
"google.golang.org/api/iterator"
adminpb "google.golang.org/genproto/googleapis/spanner/admin/database/v1"
)
Selanjutnya, tambahkan dua fungsi berikut dan fungsi bentuan di bawah metode insertScores
yang ada:
func query(ctx context.Context, w io.Writer, client *spanner.Client) error {
stmt := spanner.Statement{
SQL: `SELECT p.PlayerId, p.PlayerName, s.Score, s.Timestamp
FROM Players p
JOIN Scores s ON p.PlayerId = s.PlayerId
ORDER BY s.Score DESC LIMIT 10`}
iter := client.Single().Query(ctx, stmt)
defer iter.Stop()
for {
row, err := iter.Next()
if err == iterator.Done {
return nil
}
if err != nil {
return err
}
var playerID, score int64
var playerName string
var timestamp time.Time
if err := row.Columns(&playerID, &playerName, &score, ×tamp); err != nil {
return err
}
fmt.Fprintf(w, "PlayerId: %d PlayerName: %s Score: %s Timestamp: %s\n",
playerID, playerName, formatWithCommas(score), timestamp.String()[0:10])
}
}
func queryWithTimespan(ctx context.Context, w io.Writer, client *spanner.Client, timespan int) error {
stmt := spanner.Statement{
SQL: `SELECT p.PlayerId, p.PlayerName, s.Score, s.Timestamp
FROM Players p
JOIN Scores s ON p.PlayerId = s.PlayerId
WHERE s.Timestamp > TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL @Timespan HOUR)
ORDER BY s.Score DESC LIMIT 10`,
Params: map[string]interface{}{"Timespan": timespan},
}
iter := client.Single().Query(ctx, stmt)
defer iter.Stop()
for {
row, err := iter.Next()
if err == iterator.Done {
return nil
}
if err != nil {
return err
}
var playerID, score int64
var playerName string
var timestamp time.Time
if err := row.Columns(&playerID, &playerName, &score, ×tamp); err != nil {
return err
}
fmt.Fprintf(w, "PlayerId: %d PlayerName: %s Score: %s Timestamp: %s\n",
playerID, playerName, formatWithCommas(score), timestamp.String()[0:10])
}
}
func formatWithCommas(n int64) string {
numberAsString := strconv.FormatInt(n, 10)
numberLength := len(numberAsString)
if numberLength < 4 {
return numberAsString
}
var buffer bytes.Buffer
comma := []rune(",")
bufferPosition := numberLength % 3
if (bufferPosition) > 0 {
bufferPosition = 3 - bufferPosition
}
for i := 0; i < numberLength; i++ {
if bufferPosition == 3 {
buffer.WriteRune(comma[0])
bufferPosition = 0
}
bufferPosition++
buffer.WriteByte(numberAsString[i])
}
return buffer.String()
}
Selanjutnya, di bagian atas file leaderboard.go
tambahkan "query" sebagai satu opsi perintah dalam variabel commands
, tepat di bawah opsi "insertscores": insertScores
sehingga variabel commands
terlihat seperti ini:
var (
commands = map[string]command{
"insertplayers": insertPlayers,
"insertscores": insertScores,
"query": query,
}
)
Selanjutnya, tambahkan "queryWithTimespan" sebagai opsi perintah dalam fungsi run
, di bawah bagian perintah "createdatabase" dan di atas bagian penanganan perintah "insert and query":
// querywithtimespan command
if cmd == "querywithtimespan" {
err := queryWithTimespan(ctx, w, dataClient, timespan)
if err != nil {
fmt.Fprintf(w, "%s failed with %v", cmd, err)
}
return err
}
Setelah selesai, fungsi run
akan terlihat seperti berikut:
func run(ctx context.Context, adminClient *database.DatabaseAdminClient, dataClient *spanner.Client, w io.Writer,
cmd string, db string, timespan int) error {
// createdatabase command
if cmd == "createdatabase" {
err := createDatabase(ctx, w, adminClient, db)
if err != nil {
fmt.Fprintf(w, "%s failed with %v", cmd, err)
}
return err
}
// querywithtimespan command
if cmd == "querywithtimespan" {
if timespan == 0 {
flag.Usage()
os.Exit(2)
}
err := queryWithTimespan(ctx, w, dataClient, timespan)
if err != nil {
fmt.Fprintf(w, "%s failed with %v", cmd, err)
}
return err
}
// insert and query commands
cmdFn := commands[cmd]
if cmdFn == nil {
flag.Usage()
os.Exit(2)
}
err := cmdFn(ctx, w, dataClient)
if err != nil {
fmt.Fprintf(w, "%s failed with %v", cmd, err)
}
return err
}
Kemudian, untuk membuat perintah queryWithTimespan
berfungsi, update blok kode flag.Parse() di metode "utama" aplikasi Anda agar terlihat seperti berikut:
flag.Parse()
flagCount := len(flag.Args())
if flagCount < 2 || flagCount > 3 {
flag.Usage()
os.Exit(2)
}
cmd, db := flag.Arg(0), flag.Arg(1)
// If query timespan flag is specified, parse to int
var timespan int = 0
if flagCount == 3 {
parsedTimespan, err := strconv.Atoi(flag.Arg(2))
if err != nil {
fmt.Println(err)
os.Exit(2)
}
timespan = parsedTimespan
}
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute)
defer cancel()
adminClient, dataClient := createClients(ctx, db)
if err := run(ctx, adminClient, dataClient, os.Stdout, cmd, db, timespan); err != nil {
os.Exit(1)
}
Langkah terakhir untuk menyelesaikan penambahan fungsi "query" ke aplikasi Anda adalah menambahkan teks bantuan untuk perintah "query" dan "querywithtimespan" ke fungsi flag.Usage()
. Tambahkan baris kode berikut ke fungsi flag.Usage()
untuk menyertakan teks bantuan untuk perintah kueri:
Tambahkan kedua perintah "query" ke daftar perintah yang memungkinkan:
Command can be one of: createdatabase, insertplayers, insertscores, query, querywithtimespan
Dan tambahkan teks bantuan tambahan berikut di bawah teks bantuan untuk perintah insertscores
.
leaderboard query projects/my-project/instances/my-instance/databases/example-db
- Query players with top ten scores of all time.
leaderboard querywithtimespan projects/my-project/instances/my-instance/databases/example-db 168
- Query players with top ten scores within a timespan specified in hours.
Simpan perubahan yang Anda buat ke file leaderboard.go
dengan memilih "Simpan" di bagian menu "File" pada Cloud Shell Editor.
Anda dapat menggunakan file leaderboard.go
di direktori golang-samples/spanner/spanner_leaderboard
untuk melihat contoh tampilan file leaderboard.go
setelah menambahkan kode untuk mengaktifkan perintah query
dan querywithtimespan
.
Sekarang, mari kita buat dan jalankan aplikasi untuk mengonfirmasi bahwa perintah query
dan querywithtimespan
baru disertakan dalam daftar kemungkinan perintah aplikasi.
Jalankan perintah berikut di Cloud Shell untuk membuild aplikasi:
go build leaderboard.go
Jalankan aplikasi yang dihasilkan dalam Cloud Shell dengan memasukkan perintah berikut:
./leaderboard
Anda akan melihat perintah query
dan querywithtimespan
kini disertakan dalam output default aplikasi sebagai opsi perintah baru:
Usage: leaderboard <command> <database_name> [command_option] Command can be one of: createdatabase, insertplayers, insertscores, query, querywithtimespan Examples: leaderboard createdatabase projects/my-project/instances/my-instance/databases/example-db - Create a sample Cloud Spanner database along with sample tables in your project. leaderboard insertplayers projects/my-project/instances/my-instance/databases/example-db - Insert 100 sample Player records into the database. leaderboard insertscores projects/my-project/instances/my-instance/databases/example-db - Insert sample score data into Scores sample Cloud Spanner database table. leaderboard query projects/my-project/instances/my-instance/databases/example-db - Query players with top ten scores of all time. leaderboard querywithtimespan projects/my-project/instances/my-instance/databases/example-db 168 - Query players with top ten scores within a timespan specified in hours.
Anda dapat melihat dari respons bahwa kita dapat menggunakan perintah query
untuk mendapatkan daftar pemain "Sepuluh Teratas" sepanjang waktu. Kita juga dapat melihat bahwa perintah querywithtimespan
memungkinkan kita menentukan rentang waktu dalam jumlah jam yang bisa digunakan untuk memfilter data berdasarkan nilainya di kolom Timestamp
pada tabel Scores
.
Mari jalankan perintah query
menggunakan nilai argumen yang sama dengan yang digunakan saat menjalankan perintah create
. Pastikan Anda mengganti my-project
dengan Project ID yang Anda buat di awal codelab ini.
./leaderboard query projects/my-project/instances/cloudspanner-leaderboard/databases/leaderboard
Anda akan melihat respons yang menyertakan pemain "Sepuluh Teratas" sepanjang masa seperti berikut:
PlayerId: 4018687297 PlayerName: Player 83 Score: 999,618 Timestamp: 2017-07-01
PlayerId: 4018687297 PlayerName: Player 83 Score: 998,956 Timestamp: 2017-09-02
PlayerId: 4285713246 PlayerName: Player 51 Score: 998,648 Timestamp: 2017-12-01
PlayerId: 5267931774 PlayerName: Player 49 Score: 997,733 Timestamp: 2017-11-09
PlayerId: 1981654448 PlayerName: Player 35 Score: 997,480 Timestamp: 2018-12-06
PlayerId: 4953940705 PlayerName: Player 87 Score: 995,184 Timestamp: 2018-09-14
PlayerId: 2456736905 PlayerName: Player 84 Score: 992,881 Timestamp: 2017-04-14
PlayerId: 8234617611 PlayerName: Player 19 Score: 992,399 Timestamp: 2017-12-27
PlayerId: 1788051688 PlayerName: Player 76 Score: 992,265 Timestamp: 2018-11-22
PlayerId: 7127686505 PlayerName: Player 97 Score: 992,038 Timestamp: 2017-12-02
Sekarang mari kita jalankan perintah querywithtimespan
dengan argumen yang diperlukan untuk membuat kueri pemain "Sepuluh Teratas" tahun ini dengan menentukan "timespan" yang sama dengan jumlah jam dalam setahun, yakni 8.760. Pastikan Anda mengganti my-project
dengan Project ID yang Anda buat di awal codelab ini.
./leaderboard querywithtimespan projects/my-project/instances/cloudspanner-leaderboard/databases/leaderboard 8760
Anda akan melihat respons yang menyertakan pemain "Sepuluh Teratas" tahun ini seperti berikut:
PlayerId: 1981654448 PlayerName: Player 35 Score: 997,480 Timestamp: 2018-12-06
PlayerId: 4953940705 PlayerName: Player 87 Score: 995,184 Timestamp: 2018-09-14
PlayerId: 1788051688 PlayerName: Player 76 Score: 992,265 Timestamp: 2018-11-22
PlayerId: 6862349579 PlayerName: Player 30 Score: 990,877 Timestamp: 2018-09-14
PlayerId: 5529627211 PlayerName: Player 16 Score: 989,142 Timestamp: 2018-03-30
PlayerId: 9743904155 PlayerName: Player 1 Score: 988,765 Timestamp: 2018-05-30
PlayerId: 6809119884 PlayerName: Player 7 Score: 986,673 Timestamp: 2018-05-16
PlayerId: 2132710638 PlayerName: Player 54 Score: 983,108 Timestamp: 2018-09-11
PlayerId: 2320093590 PlayerName: Player 79 Score: 981,373 Timestamp: 2018-05-07
PlayerId: 9554181430 PlayerName: Player 80 Score: 981,087 Timestamp: 2018-06-21
Sekarang, mari kita jalankan perintah querywithtimespan
untuk membuat kueri pemain "Sepuluh Teratas" bulan ini dengan menentukan "timespan" yang sama dengan jumlah jam dalam sebulan, yaitu 730. Pastikan Anda mengganti my-project
dengan Project ID yang Anda buat di awal codelab ini.
./leaderboard querywithtimespan projects/my-project/instances/cloudspanner-leaderboard/databases/leaderboard 730
Anda akan melihat respons yang menyertakan pemain "Sepuluh Teratas" bulan ini seperti berikut:
PlayerId: 3869829195 PlayerName: Player 69 Score: 949,686 Timestamp: 2019-02-19
PlayerId: 7448359883 PlayerName: Player 20 Score: 938,998 Timestamp: 2019-02-07
PlayerId: 1981654448 PlayerName: Player 35 Score: 929,003 Timestamp: 2019-02-22
PlayerId: 9336678658 PlayerName: Player 44 Score: 914,106 Timestamp: 2019-01-27
PlayerId: 6968576389 PlayerName: Player 40 Score: 898,041 Timestamp: 2019-02-21
PlayerId: 5529627211 PlayerName: Player 16 Score: 896,433 Timestamp: 2019-01-29
PlayerId: 9395039625 PlayerName: Player 59 Score: 879,495 Timestamp: 2019-02-09
PlayerId: 2094604854 PlayerName: Player 39 Score: 860,434 Timestamp: 2019-02-01
PlayerId: 9395039625 PlayerName: Player 59 Score: 849,955 Timestamp: 2019-02-21
PlayerId: 4285713246 PlayerName: Player 51 Score: 805,654 Timestamp: 2019-02-02
Sekarang mari kita jalankan perintah querywithtimespan
untuk membuat kueri pemain "Sepuluh Teratas" minggu ini dengan menetapkan "timespan" yang sama dengan jumlah jam dalam seminggu, yaitu 168. Pastikan Anda mengganti my-project
dengan Project ID yang Anda buat di awal codelab ini.
./leaderboard querywithtimespan projects/my-project/instances/cloudspanner-leaderboard/databases/leaderboard 168
Anda akan melihat respons yang menyertakan pemain "Sepuluh Teratas" minggu ini seperti berikut:
PlayerId: 3869829195 PlayerName: Player 69 Score: 949,686 Timestamp: 2019-02-19
PlayerId: 1981654448 PlayerName: Player 35 Score: 929,003 Timestamp: 2019-02-22
PlayerId: 6968576389 PlayerName: Player 40 Score: 898,041 Timestamp: 2019-02-21
PlayerId: 9395039625 PlayerName: Player 59 Score: 849,955 Timestamp: 2019-02-21
PlayerId: 5954045812 PlayerName: Player 8 Score: 795,639 Timestamp: 2019-02-22
PlayerId: 3889939638 PlayerName: Player 71 Score: 775,252 Timestamp: 2019-02-21
PlayerId: 5529627211 PlayerName: Player 16 Score: 604,695 Timestamp: 2019-02-19
PlayerId: 9006728426 PlayerName: Player 3 Score: 457,208 Timestamp: 2019-02-22
PlayerId: 8289497066 PlayerName: Player 58 Score: 227,697 Timestamp: 2019-02-20
PlayerId: 8065482904 PlayerName: Player 99 Score: 198,429 Timestamp: 2019-02-24
Bagus!
Kini setelah Anda menambahkan data, Cloud Spanner akan mengubah skala database Anda menjadi sebesar apa pun kebutuhan Anda. Berapa pun pertumbuhan database Anda, papan peringkat game dapat terus diskalakan dengan akurat menggunakan Cloud Spanner dan teknologi Truetime-nya.
Setelah asyik bermain dengan Spanner, kita perlu membersihkan tempat bermain kita, menghemat resource dan uang yang berharga. Untungnya ini adalah langkah yang mudah. Cukup buka bagian Cloud Spanner dari Cloud Console dan hapus instance yang kita buat di langkah codelab bernama "Siapkan Instance Cloud Spanner".
Yang telah kita bahas:
- Instance, Database, dan Skema Tabel Google Cloud Spanner untuk papan peringkat
- Cara membuat aplikasi konsol Go
- Cara membuat Database Spanner dan Tabel menggunakan library klien Go
- Cara memuat data ke Database Spanner menggunakan library klien Go
- Cara membuat kueri hasil "Sepuluh Teratas" dari data Anda menggunakan stempel waktu commit Spanner dan library klien Go
Langkah Berikutnya:
- Baca Laporan resmi Spanner CAP
- Pelajari tentang Desain Skema dan Praktik terbaik kueri
- Pelajari lebih lanjut stempel waktu commit Cloud Spanner
Kirimkan masukan Anda
- Luangkan waktu Anda untuk menyelesaikan survei singkat kami