1. Übersicht
Google Cloud Spanner ist ein vollständig verwalteter, horizontal skalierbarer, global verteilter relationaler Datenbankdienst, der ACID-Transaktionen und SQL-Semantik bietet, ohne auf Leistung und Hochverfügbarkeit zu verzichten.
In diesem Lab erfahren Sie, wie Sie eine Cloud Spanner-Instanz einrichten. Sie führen die Schritte zum Erstellen einer Datenbank und eines Schemas aus, die für eine Gaming-Bestenliste verwendet werden können. Zuerst erstellen Sie eine Spielertabelle zur Speicherung von Spielerinformationen und eine Punktzahltabelle, in der die Punktzahlen der Spieler gespeichert werden.
Als Nächstes füllen Sie die Tabellen mit Beispieldaten. Am Ende des Labs führen Sie einige Top-10-Beispielabfragen aus und löschen schließlich die Instanz, um Ressourcen freizugeben.
Aufgaben in diesem Lab
- Cloud Spanner-Instanz einrichten
- Datenbank und Tabellen erstellen
- So verwenden Sie eine Commit-Zeitstempelspalte.
- Daten in eine Cloud Spanner-Datenbanktabelle mit Zeitstempeln laden.
- Anleitung zum Abfragen Ihrer Cloud Spanner-Datenbank.
- Cloud Spanner-Instanz löschen
Voraussetzungen
Wie möchten Sie diese Anleitung nutzen?
<ph type="x-smartling-placeholder">Wie würden Sie Ihre Erfahrung mit der Google Cloud Platform bewerten?
<ph type="x-smartling-placeholder">2. Einrichtung und Anforderungen
Umgebung für das selbstbestimmte Lernen einrichten
Wenn Sie noch kein Google-Konto (Gmail oder Google Apps) haben, müssen Sie eines erstellen. Melden Sie sich in der Google Cloud Platform Console ( console.cloud.google.com) an und erstellen Sie ein neues Projekt.
Wenn Sie bereits ein Projekt haben, klicken Sie auf das Drop-down-Menü für die Projektauswahl oben links in der Konsole:
und klicken Sie auf „NEUES PROJEKT“, Schaltfläche zum Erstellen eines neuen Projekts:
Wenn Sie noch kein Projekt haben, sollten Sie ein Dialogfeld wie dieses sehen, um Ihr erstes zu erstellen:
Im nachfolgenden Dialog zur Projekterstellung können Sie die Details Ihres neuen Projekts eingeben:
Denken Sie an die Projekt-ID. Dies ist ein eindeutiger Name für alle Google Cloud-Projekte. Der oben angegebene Name ist bereits vergeben und funktioniert leider nicht für Sie. Sie wird in diesem Codelab später als PROJECT_ID
bezeichnet.
Falls noch nicht geschehen, müssen Sie als Nächstes in der Developers Console die Abrechnung aktivieren, um Google Cloud-Ressourcen nutzen und die Cloud Spanner API aktivieren zu können.
Dieses Codelab sollte nicht mehr als ein paar Euro kosten. Wenn Sie sich jedoch dazu entschließen, mehr Ressourcen zu verwenden oder diese weiter auszuführen (siehe Abschnitt „Bereinigen“ am Ende dieses Dokuments), Die Preise für Google Cloud Spanner finden Sie hier.
Neue Google Cloud Platform-Nutzer haben Anspruch auf eine kostenlose Testversion mit 300$Guthaben, wodurch das Codelab in der Regel kostenlos sein sollte.
Google Cloud Shell einrichten
Sie können Google Cloud und Spanner per Fernzugriff von Ihrem Laptop aus bedienen. In diesem Codelab verwenden wir jedoch Google Cloud Shell, eine Befehlszeilenumgebung, die in der Cloud ausgeführt wird.
Diese Debian-basierte virtuelle Maschine verfügt über alle erforderlichen Entwicklungstools. Es bietet ein Basisverzeichnis mit 5 GB nichtflüchtigem Speicher und wird in Google Cloud ausgeführt. Dadurch werden die Netzwerkleistung und die Authentifizierung erheblich verbessert. Für dieses Codelab benötigen Sie also nur einen Browser – ja, er funktioniert auf Chromebooks.
- Klicken Sie einfach auf Cloud Shell aktivieren , um Cloud Shell über die Cloud Console zu aktivieren. Die Bereitstellung und Verbindung mit der Umgebung dauert einen Moment.
Sobald Sie mit Cloud Shell verbunden sind, sollten Sie sehen, dass Sie bereits authentifiziert sind und dass das Projekt bereits auf Ihre PROJECT_ID
eingestellt ist.
gcloud auth list
Befehlsausgabe
Credentialed accounts: - <myaccount>@<mydomain>.com (active)
gcloud config list project
Befehlsausgabe
[core] project = <PROJECT_ID>
Sollte das Projekt aus irgendeinem Grund nicht eingerichtet sein, geben Sie einfach den folgenden Befehl ein:
gcloud config set project <PROJECT_ID>
Du suchst dein Gerät (PROJECT_ID
)? Sehen Sie nach, welche ID Sie bei den Einrichtungsschritten verwendet haben, oder rufen Sie sie im Dashboard der Cloud Console auf:
Cloud Shell legt außerdem standardmäßig einige Umgebungsvariablen fest, die bei der Ausführung zukünftiger Befehle nützlich sein können.
echo $GOOGLE_CLOUD_PROJECT
Befehlsausgabe
<PROJECT_ID>
- Legen Sie schließlich die Standardzone und die Projektkonfiguration fest.
gcloud config set compute/zone us-central1-f
Sie können verschiedene Zonen auswählen. Weitere Informationen finden Sie unter Regionen und Zonen.
Zusammenfassung
In diesem Schritt richten Sie Ihre Umgebung ein.
Nächstes Thema
Als Nächstes richten Sie eine Cloud Spanner-Instanz ein.
3. Cloud Spanner-Instanz einrichten
In diesem Schritt richten wir unsere Cloud Spanner-Instanz für dieses Codelab ein. Suchen Sie im linken Hamburger-Menü nach dem Spanner-Eintrag oder drücken Sie „/“, um nach Spanner zu suchen und geben Sie „Spanner“ ein
Klicken Sie als Nächstes auf und füllen Sie das Formular aus. Geben Sie dazu den Instanznamen cloudspanner-leaderboard für Ihre Instanz ein, wählen Sie eine Konfiguration aus (wählen Sie eine regionale Instanz aus) und legen Sie die Anzahl der Knoten fest. Für dieses Codelab benötigen wir nur einen Knoten. Für Produktionsinstanzen und zur Einhaltung des Cloud Spanner-SLA müssen Sie mindestens drei Knoten in Ihrer Cloud Spanner-Instanz ausführen.
Klicken Sie abschließend auf „Erstellen“. und innerhalb von Sekunden steht eine Cloud Spanner-Instanz zur Verfügung.
Im nächsten Schritt erstellen wir mithilfe der Go-Clientbibliothek eine Datenbank und ein Schema in der neuen Instanz.
4. Datenbank und Schema erstellen
In diesem Schritt erstellen wir die Beispieldatenbank und das Schema.
Erstellen wir mit der Go-Clientbibliothek zwei Tabellen: eine Spielertabelle mit Spielerinformationen und eine Punktzahltabelle zum Speichern der Punktzahlen der Spieler Gehen Sie dazu die Schritte zum Erstellen einer Go-Konsolenanwendung in Cloud Shell durch.
Klonen Sie zuerst den Beispielcode für dieses Codelab aus GitHub. Geben Sie dazu den folgenden Befehl in Cloud Shell ein:
export GO111MODULE=auto
go get -u github.com/GoogleCloudPlatform/golang-samples/spanner/...
Ändern Sie das Verzeichnis dann in „Leaderboard“. Verzeichnis, in dem Sie Ihre Anwendung erstellen.
cd gopath/src/github.com/GoogleCloudPlatform/golang-samples/spanner/spanner_leaderboard
Der gesamte Code, der für dieses Codelab erforderlich ist, befindet sich im vorhandenen Verzeichnis golang-samples/spanner/spanner_leaderboard/
als ausführbare Go-Anwendung namens leaderboard
, die als Referenz dient, während du das Codelab durchführst. Wir werden ein neues Verzeichnis erstellen und schrittweise eine Kopie der Leaderboard-Anwendung erstellen.
Erstellen Sie ein neues Verzeichnis mit dem Namen „codelab“. für die Anwendung und wechseln Sie mit dem folgenden Befehl in das entsprechende Verzeichnis:
mkdir codelab && cd $_
Erstellen wir nun eine einfache Go-Anwendung mit dem Namen "Leaderboard". die mithilfe der Spanner-Clientbibliothek eine aus zwei Tabellen bestehende Bestenliste erstellt. Spieler und Punktzahlen. Sie können dies direkt im Cloud Shell-Editor tun:
Öffnen Sie den Cloud Shell-Editor. Klicken Sie dazu auf „Editor öffnen“. unten hervorgehoben:
Erstellen Sie eine Datei mit dem Namen „leaderboard.go“. im Ordner ~/gopath/src/github.com/GoogleCloudPlatform/golang-samples/spanner/codelab.
- Vergewissern Sie sich zunächst, dass Sie das „Codelab“ Ordner, der in der Ordnerliste des Cloud Shell-Editors ausgewählt ist.
- Wählen Sie dann „Neue Datei“ aus. im Cloud Shell-Editor unter „Datei“ .
- Geben Sie „leaderboard.go“ ein als Namen für die neue Datei ein.
Dies ist die Hauptdatei der Anwendung, die unseren Anwendungscode und Referenzen enthält, um Abhängigkeiten einzuschließen.
Kopieren Sie den folgenden Go-Code mit Strg + P und fügen Sie ihn in die Datei leaderboard.go
ein, um die leaderboard
-Datenbank und die Tabellen Players
und Scores
zu erstellen:
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)
}
}
Klicken Sie auf „Speichern“, um die Änderungen an der Datei leaderboard.go
zu speichern im Cloud Shell-Editor unter „Datei“ .
Mit der Datei leaderboard.go
im Verzeichnis golang-samples/spanner/spanner_leaderboard
können Sie sich ein Beispiel dafür ansehen, wie Ihre Datei leaderboard.go
aussehen sollte, nachdem Sie den Code zum Aktivieren des Befehls createdatabase
hinzugefügt haben.
Führen Sie „go build“ aus, um Ihre Anwendung in Cloud Shell zu erstellen. aus dem Verzeichnis codelab
, in dem sich die Datei leaderboard.go
befindet:
go build leaderboard.go
Nachdem Ihre Anwendung erfolgreich erstellt wurde, führen Sie die Anwendung in Cloud Shell aus, indem Sie den folgenden Befehl eingeben:
./leaderboard
Die Ausgabe sollte etwa so aussehen:
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.
Dieser Antwort können wir entnehmen, dass dies die Anwendung Leaderboard
ist, die derzeit einen möglichen Befehl hat: createdatabase
. Wie Sie sehen, ist das erwartete Argument des Befehls createdatabase
ein String, der eine bestimmte Instanz-ID und Datenbank-ID enthält.
Führen Sie nun den folgenden Befehl aus. Ersetzen Sie my-project
durch die Projekt-ID, die Sie zu Beginn dieses Codelabs erstellt haben.
./leaderboard createdatabase projects/my-project/instances/cloudspanner-leaderboard/databases/leaderboard
Nach einigen Sekunden sollten Sie eine Antwort wie die folgende sehen:
Created database [projects/my-project/instances/cloudspanner-leaderboard/databases/leaderboard]
Im Abschnitt Cloud Spanner-Datenbanken – Übersicht der Cloud Console sollten Sie im linken Menü Ihre neue Datenbank und Ihre Tabellen sehen.
Im nächsten Schritt aktualisieren wir unsere Anwendung, um einige Daten in Ihre neue Datenbank zu laden.
5. Daten laden
Wir haben jetzt eine Datenbank mit dem Namen leaderboard
, die zwei Tabellen enthält. Players
und Scores
. Verwenden wir nun die Go-Clientbibliothek, um unsere Tabelle Players
mit Spielern und unsere Tabelle Scores
mit zufälligen Punktzahlen für jeden Spieler zu füllen.
Wenn der Cloud Shell-Editor noch nicht geöffnet ist, klicken Sie auf das unten hervorgehobene Symbol:
Fügen Sie als Nächstes der Datei leaderboard.go
im Cloud Shell-Editor einen insertplayers
-Befehl hinzu, mit dem 100 Spieler in die Tabelle Players
eingefügt werden können. Außerdem fügen wir einen insertscores
-Befehl hinzu, mit dem für jeden Spieler in der Tabelle Players
vier Zufallspunktzahlen in die Tabelle Scores
eingefügt werden können.
Aktualisieren Sie zuerst den Abschnitt imports
am Anfang der Datei leaderboard.go
und ersetzen Sie das, was aktuell dort vorhanden ist. Wenn Sie fertig sind, sollte es so aussehen:
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"
)
Fügen Sie als Nächstes einen neuen Befehlstyp und eine Liste von Befehlen oben in der Datei hinzu, direkt unter der Zeile, die mit „type adminCommand ...“ beginnt. Wenn Sie fertig sind, sollte sie so aussehen:
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,
}
)
Fügen Sie als Nächstes die Funktionen insertPlayers und insertScores unter der vorhandenen Funktion createdatabase()
hinzu:
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
}
// Initialize 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
}
// Initialize 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
}
Damit der insert
-Befehl funktioniert, fügen Sie im Befehl „run“ Ihrer Anwendung folgenden Code hinzu: -Funktion unter der Verarbeitungsanweisung createdatabase
ein, wobei die Anweisung return nil
ersetzt wird :
// 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
Wenn Sie fertig sind, sollte die Funktion run
so aussehen:
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
}
Der letzte Schritt zum Hinzufügen von „insert“ Funktionalität zu Ihrer Anwendung besteht darin, Hilfetext für "insertplayers" hinzuzufügen. und „insertscores“ an die Funktion flag.Usage()
. Fügen Sie der Funktion flag.Usage()
den folgenden Hilfetext für die Einfügebefehle hinzu:
Fügen Sie die beiden Befehle der Liste der möglichen Befehle hinzu:
Command can be one of: createdatabase, insertplayers, insertscores
Fügen Sie diesen zusätzlichen Hilfetext unter dem Hilfetext für den Befehl createdatabase
ein.
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.
Klicken Sie auf „Speichern“, um die Änderungen an der Datei leaderboard.go
zu speichern im Cloud Shell-Editor unter „Datei“ .
Mit der Datei leaderboard.go
im Verzeichnis golang-samples/spanner/spanner_leaderboard
können Sie sich ein Beispiel dafür ansehen, wie Ihre Datei leaderboard.go
aussehen sollte, nachdem Sie den Code zum Aktivieren der Befehle insertplayers
und insertscores
hinzugefügt haben.
Jetzt erstellen wir die Anwendung und führen sie aus, um zu prüfen, ob die neuen Befehle insertplayers
und insertscores
in der Liste möglicher Befehle der Anwendung enthalten sind. Führen Sie den folgenden Befehl aus, um die Anwendung zu erstellen:
go build leaderboard.go
Führen Sie die resultierende Anwendung in Cloud Shell aus, indem Sie den folgenden Befehl eingeben:
./leaderboard
Die Befehle insertplayers
und insertscores
sollten jetzt in der Standardausgabe der Anwendung enthalten sein:
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.
Führen wir nun den Befehl insertplayers
mit denselben Argumentwerten aus, die wir beim Aufrufen des Befehls createdatabase
verwendet haben. Ersetzen Sie my-project
durch die Projekt-ID, die Sie zu Beginn dieses Codelabs erstellt haben.
./leaderboard insertplayers projects/my-project/instances/cloudspanner-leaderboard/databases/leaderboard
Nach einigen Sekunden sollten Sie eine Antwort wie die folgende sehen:
Inserted players
Verwenden wir nun die Go-Clientbibliothek, um unsere Tabelle Scores
mit vier Zufallswerten und Zeitstempeln für jeden Spieler in der Tabelle Players
zu füllen.
Die Spalte Timestamp
der Tabelle „Scores
“ wurde als „Commit-Zeitstempel“ definiert Spalte über die folgende SQL-Anweisung, die ausgeführt wurde, als wir zuvor den Befehl create
ausgeführt haben:
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
Beachten Sie das Attribut OPTIONS(allow_commit_timestamp=true)
. Dadurch wird Timestamp
zu einem „Commit-Zeitstempel“ und kann automatisch mit dem genauen Transaktionszeitstempel für INSERT- und UPDATE-Vorgänge in einer bestimmten Tabellenzeile gefüllt werden.
Sie können auch Ihre eigenen Zeitstempelwerte in einen Commit-Zeitstempel einfügen solange Sie einen Zeitstempel mit einem Wert einfügen, der in der Vergangenheit liegt. In diesem Codelab machen wir das.
Führen wir nun den Befehl insertscores
mit denselben Argumentwerten aus, die wir beim Aufrufen des Befehls insertplayers
verwendet haben. Ersetzen Sie my-project
durch die Projekt-ID, die Sie zu Beginn dieses Codelabs erstellt haben.
./leaderboard insertscores projects/my-project/instances/cloudspanner-leaderboard/databases/leaderboard
Nach einigen Sekunden sollten Sie eine Antwort wie die folgende sehen:
Inserted scores
Beim Ausführen der Funktion insertScores
wird das folgende Code-Snippet verwendet, um einen zufällig generierten Zeitstempel mit einem Datum und einer Uhrzeit in der Vergangenheit einzufügen:
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,
},
})
Um die Spalte Timestamp
automatisch mit dem Zeitstempel zu füllen, der genau dem Zeitpunkt entspricht, an dem das Transaktion stattfindet, können Sie stattdessen die Go-Konstante spanner.CommitTimestamp
wie im folgenden Code-Snippet einfügen:
...
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,
},
})
Nachdem die Daten geladen wurden, überprüfen Sie die Werte, die Sie gerade in die neuen Tabellen im Cloud Spanner-Bereich der Cloud Console geschrieben haben. Wählen Sie zuerst die Datenbank leaderboard
und dann die Tabelle Players
aus. Klicken Sie auf den Tab Data
. Sie sollten sehen, dass sich in den Spalten PlayerId
und PlayerName
der Tabelle Daten befinden.
Prüfen Sie als Nächstes, ob die Tabelle „Scores“ auch Daten enthält. Klicken Sie dazu auf die Tabelle Scores
und wählen Sie den Tab Data
aus. Die Spalten PlayerId
, Timestamp
und Score
der Tabelle sollten Daten enthalten.
Gut gemacht! Wir aktualisieren nun unsere App und führen einige Abfragen aus, mit denen wir eine Gaming-Bestenliste erstellen können.
6. Abfragen zu Bestenlisten ausführen
Nachdem wir unsere Datenbank eingerichtet und Informationen in unsere Tabellen geladen haben, erstellen wir nun mit diesen Daten eine Bestenliste. Dazu müssen wir die folgenden vier Fragen beantworten:
- Welche Spieler sind die "Top Ten"? aller Zeiten?
- Welche Spieler sind die "Top Ten"? des Jahres?
- Welche Spieler sind die "Top Ten"? des Monats?
- Welche Spieler sind die "Top Ten"? der Woche?
Wir aktualisieren nun unsere Anwendung, um die SQL-Abfragen auszuführen, die diese Fragen beantworten.
Wir fügen einen query
-Befehl und einen queryWithTimespan
-Befehl hinzu, die eine Möglichkeit bieten, die Abfragen auszuführen, um die Fragen zu beantworten, die die für unsere Bestenliste erforderlichen Informationen liefern.
Bearbeiten Sie die Datei leaderboard.go
im Cloud Shell-Editor, um der Anwendung einen query
- und einen queryWithTimespan
-Befehl hinzuzufügen. Außerdem fügen wir eine formatWithCommas
-Hilfsfunktion hinzu, um die Punktzahlen mit Kommas zu formatieren.
Aktualisieren Sie zuerst den Abschnitt imports
am Anfang der Datei leaderboard.go
und ersetzen Sie das, was aktuell dort vorhanden ist. Wenn Sie fertig sind, sollte es so aussehen:
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"
)
Fügen Sie als Nächstes die folgenden beiden Funktionen und die Hilfsfunktion unter der vorhandenen Methode insertScores
hinzu:
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()
}
Fügen Sie dann am Anfang der Datei leaderboard.go
„query“ ein. als eine Befehlsoption in der Variablen commands
direkt unter der Option „insertscores": insertScores
“, sodass die Variable commands
so aussieht:
var (
commands = map[string]command{
"insertplayers": insertPlayers,
"insertscores": insertScores,
"query": query,
}
)
Fügen Sie als Nächstes „queryWithTimespan“ hinzu. als Befehlsoption in der Funktion run
unterhalb von „createdatabase“ über dem Befehl „insert and query“ Abschnitt zum Umgang mit Befehlen:
// 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
}
Wenn Sie fertig sind, sollte die Funktion run
so aussehen:
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
}
Damit der queryWithTimespan
-Befehl funktioniert, müssen Sie den Codeblock „flag.Parse()“ im Hauptbereich Ihrer Anwendung aktualisieren. , sodass sie so aussieht:
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)
}
Der letzte Schritt zum Hinzufügen von „query“ Ihrer Anwendung ist es, Hilfetext zur Abfrage hinzuzufügen und „querywithtimespan“ an die Funktion flag.Usage()
. Fügen Sie der Funktion flag.Usage()
die folgenden Codezeilen hinzu, um Hilfetext für die Abfragebefehle einzufügen:
Fügen Sie die beiden „Suchanfrage“ der Liste möglicher Befehle hinzu:
Command can be one of: createdatabase, insertplayers, insertscores, query, querywithtimespan
Fügen Sie diesen zusätzlichen Hilfetext unter dem Hilfetext für den Befehl insertscores
ein.
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.
Klicken Sie auf „Speichern“, um die Änderungen an der Datei leaderboard.go
zu speichern im Cloud Shell-Editor unter „Datei“ .
Mit der Datei leaderboard.go
im Verzeichnis golang-samples/spanner/spanner_leaderboard
können Sie sich ein Beispiel dafür ansehen, wie Ihre Datei leaderboard.go
aussehen sollte, nachdem Sie den Code zum Aktivieren der Befehle query
und querywithtimespan
hinzugefügt haben.
Jetzt erstellen wir die Anwendung und führen sie aus, um zu prüfen, ob die neuen Befehle query
und querywithtimespan
in der Liste möglicher Befehle der Anwendung enthalten sind.
Führen Sie in Cloud Shell den folgenden Befehl aus, um die Anwendung zu erstellen:
go build leaderboard.go
Führen Sie die resultierende Anwendung in Cloud Shell aus, indem Sie den folgenden Befehl eingeben:
./leaderboard
Die Befehle query
und querywithtimespan
sollten jetzt in der Standardausgabe der App als neue Befehlsoption enthalten sein:
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.
Der Antwort können Sie entnehmen, dass wir mit dem Befehl query
eine Liste der Top Ten abrufen können. aller Zeiten. Außerdem lässt sich mit dem Befehl querywithtimespan
eine Zeitspanne in Stunden angeben, die zum Filtern von Datensätzen basierend auf ihrem Wert in der Spalte Timestamp
der Tabelle Scores
verwendet werden soll.
Lassen Sie uns den Befehl query
mit denselben Argumentwerten ausführen, die wir beim Ausführen des Befehls create
verwendet haben. Ersetzen Sie my-project
durch die Projekt-ID, die Sie zu Beginn dieses Codelabs erstellt haben.
./leaderboard query projects/my-project/instances/cloudspanner-leaderboard/databases/leaderboard
Sie sollten nun eine Antwort sehen, die die Top Ten enthält. wie die folgenden:
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
Führen wir nun den Befehl querywithtimespan
mit den erforderlichen Argumenten aus, um die Top Ten abzufragen. Spieler des Jahres durch Angabe einer „Zeitspanne“ entspricht der Anzahl der Stunden in einem Jahr, also 8.760. Ersetzen Sie my-project
durch die Projekt-ID, die Sie zu Beginn dieses Codelabs erstellt haben.
./leaderboard querywithtimespan projects/my-project/instances/cloudspanner-leaderboard/databases/leaderboard 8760
Sie sollten nun eine Antwort sehen, die die Top Ten enthält. Spieler des Jahres:
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
Führen wir nun den Befehl querywithtimespan
aus, um die Top Ten abzufragen. Spieler des Monats durch Angabe einer Zeitspanne der Anzahl der Stunden pro Monat,
also 730. Ersetzen Sie my-project
durch die Projekt-ID, die Sie zu Beginn dieses Codelabs erstellt haben.
./leaderboard querywithtimespan projects/my-project/instances/cloudspanner-leaderboard/databases/leaderboard 730
Sie sollten nun eine Antwort sehen, die die Top Ten enthält. Spieler des Monats:
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
Führen wir nun den Befehl querywithtimespan
aus, um die Top Ten abzufragen. der Woche durch Angabe einer
„Zeitraum“-Zeitspanne der Anzahl der Stunden pro Woche (168). Ersetzen Sie my-project
durch die Projekt-ID, die Sie zu Beginn dieses Codelabs erstellt haben.
./leaderboard querywithtimespan projects/my-project/instances/cloudspanner-leaderboard/databases/leaderboard 168
Sie sollten nun eine Antwort sehen, die die Top Ten enthält. der Woche:
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
Super!
Wenn Sie jetzt Einträge hinzufügen, skaliert Cloud Spanner Ihre Datenbank auf eine beliebige Größe. Ganz gleich, wie stark Ihre Datenbank wächst – die Bestenliste Ihres Spiels kann mit Cloud Spanner und seiner Truetime-Technologie weiterhin präzise skaliert werden.
7. Bereinigen
Nach so viel Spaß beim Spielen mit Spanner müssen wir unseren Spielplatz aufräumen und dabei kostbare Ressourcen und Geld sparen. Dies ist jedoch ein einfacher Schritt. Rufen Sie einfach den Cloud Spanner-Bereich der Cloud Console auf und löschen Sie die Instanz, die Sie im Codelab-Schritt "Setup a Cloud Spanner Instance" (Cloud Spanner-Instanz einrichten) erstellt haben.
8. Glückwunsch!
Behandelte Themen:
- Google Cloud Spanner-Instanzen, -Datenbanken und -Tabellenschema für eine Bestenliste
- Go-Konsolenanwendung erstellen
- Spanner-Datenbank und -Tabellen mit der Go-Clientbibliothek erstellen
- Daten mithilfe der Go-Clientbibliothek in eine Spanner-Datenbank laden
- Abfrage der „Top Ten“ Ergebnisse aus Ihren Daten mithilfe von Spanner-Commit-Zeitstempeln und der Go-Clientbibliothek
Vorgehensweise:
- Spanner CAP-Whitepaper lesen
- Weitere Informationen zum Schemadesign und zu Best Practices für Abfragen
- Weitere Informationen zu Commit-Zeitstempeln in Cloud Spanner
Feedback geben
- Bitte nehmen Sie sich einen Moment Zeit, um an unserer kurzen Umfrage teilzunehmen.