1. Übersicht
Google Cloud Spanner ist ein vollständig verwalteter, horizontal skalierbarer, global verteilter, relationaler Datenbankdienst, der ACID-Transaktionen und SQL-Semantik bietet, ohne Leistung und Hochverfügbarkeit zu beeinträchtigen.
In diesem Lab erfahren Sie, wie Sie eine Cloud Spanner-Instanz einrichten. Sie durchlaufen die Schritte zum Erstellen einer Datenbank und eines Schemas, die für eine Bestenliste für Spiele verwendet werden können. Zuerst erstellen Sie eine Tabelle „Players“ zum Speichern von Spielerinformationen und eine Tabelle „Scores“ zum Speichern von Spielergebnissen.
Als Nächstes füllen Sie die Tabellen mit Beispieldaten. Zum Schluss führen Sie einige Beispielabfragen für die zehn wichtigsten Elemente aus und löschen dann die Instanz, um Ressourcen freizugeben.
Lerninhalte
- Cloud Spanner-Instanz einrichten
- Datenbank und Tabellen erstellen
- Commit-Zeitstempelspalte verwenden
- Daten mit Zeitstempeln in die Cloud Spanner-Datenbanktabelle laden
- Cloud Spanner-Datenbank abfragen
- Cloud Spanner-Instanz löschen
Was Sie benötigen
Wie werden Sie diese Anleitung verwenden?
Wie würden Sie Ihre Erfahrung mit der Google Cloud Platform bewerten?
2. Einrichtung und Anforderungen
Umgebung zum selbstbestimmten 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 Console ( console.cloud.google.com) an und erstellen Sie ein neues Projekt.
Wenn Sie bereits ein Projekt haben, klicken Sie oben links in der Console auf das Drop-down-Menü zur Projektauswahl:

Klicken Sie im angezeigten Dialogfeld auf die Schaltfläche „NEUES PROJEKT“, um ein neues Projekt zu erstellen:

Wenn Sie noch kein Projekt haben, wird ein Dialogfeld wie das folgende angezeigt, in dem Sie Ihr erstes Projekt erstellen können:

Im nachfolgenden Dialogfeld zum Erstellen von Projekten können Sie die Details Ihres neuen Projekts eingeben:

Merken Sie sich die Projekt-ID. Sie ist für alle Google Cloud-Projekte ein eindeutiger Name. Der Name oben ist bereits vergeben und kann nicht verwendet werden. Sie wird später in diesem Codelab als PROJECT_ID bezeichnet.
Als Nächstes müssen Sie, falls noch nicht geschehen, die Abrechnung in der Entwicklerkonsole aktivieren, um Google Cloud-Ressourcen verwenden zu können, und die Cloud Spanner API aktivieren.

Dieses Codelab sollte Sie nicht mehr als ein paar Dollar kosten, aber es könnte mehr sein, wenn Sie sich für mehr Ressourcen entscheiden oder wenn Sie sie laufen lassen (siehe Abschnitt „Bereinigen“ am Ende dieses Dokuments). Die Preise für Google Cloud Spanner sind hier dokumentiert.
Neuen Nutzern der Google Cloud Platform steht eine kostenlose Testversion mit einem Guthaben von 300$ zur Verfügung. Dieses Codelab sollte damit vollständig kostenlos sein.
Google Cloud Shell einrichten
Während Sie Google Cloud und Spanner von Ihrem Laptop aus per Fernzugriff nutzen können, wird in diesem Codelab Google Cloud Shell verwendet, eine Befehlszeilenumgebung, die in der Cloud ausgeführt wird.
Diese Debian-basierte virtuelle Maschine verfügt über alle Entwicklungstools, die Sie benötigen. Sie bietet ein Basisverzeichnis mit 5 GB nichtflüchtigem Speicher und läuft in Google Cloud, was die Netzwerkleistung und Authentifizierung erheblich verbessert. Für dieses Codelab benötigen Sie also nur einen Browser (es funktioniert auch auf einem Chromebook).
- Klicken Sie zum Aktivieren von Cloud Shell in der Cloud Console einfach auf Cloud Shell aktivieren
. Die Bereitstellung und Verbindung mit der Umgebung sollte nur wenige Augenblicke dauern.
Sobald die Verbindung mit der Cloud Shell hergestellt ist, sehen Sie, dass Sie bereits authentifiziert sind und für das Projekt schon Ihre PROJECT_ID eingestellt ist.
gcloud auth list
Befehlsausgabe
Credentialed accounts: - <myaccount>@<mydomain>.com (active)
gcloud config list project
Befehlsausgabe
[core] project = <PROJECT_ID>
Wenn das Projekt aus irgendeinem Grund nicht festgelegt ist, führen Sie einfach den folgenden Befehl aus:
gcloud config set project <PROJECT_ID>
Suchst du nach deinem PROJECT_ID? Sehen Sie nach, welche ID Sie in den Einrichtungsschritten verwendet haben, oder suchen Sie sie im Cloud Console-Dashboard:
In Cloud Shell werden auch einige Umgebungsvariablen standardmäßig festgelegt, die für zukünftige Befehle nützlich sein können.
echo $GOOGLE_CLOUD_PROJECT
Befehlsausgabe
<PROJECT_ID>
- Legen Sie zum Schluss 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.
Als Nächstes
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 nach dem Spanner-Eintrag
im Dreistrich-Menü oben links
oder suchen Sie nach Spanner, indem Sie „/“ drücken und „Spanner“ eingeben.

Klicken Sie dann auf
und füllen Sie das Formular aus, indem Sie den Instanznamen cloudspanner-leaderboard für Ihre Instanz eingeben, eine Konfiguration auswählen (regionale Instanz) und die Anzahl der Knoten festlegen. Für dieses Codelab benötigen wir nur einen Knoten. Für Produktionsinstanzen und um das Cloud Spanner-SLA zu erfüllen, müssen Sie mindestens drei Knoten in Ihrer Cloud Spanner-Instanz ausführen.
Klicken Sie abschließend auf „Erstellen“. Innerhalb von Sekunden steht Ihnen eine Cloud Spanner-Instanz zur Verfügung.

Im nächsten Schritt verwenden wir die C#-Clientbibliothek, um eine Datenbank und ein Schema in unserer neuen Instanz zu erstellen.
4. Datenbank und Schema erstellen
In diesem Schritt erstellen wir unsere Beispieldatenbank und unser Schema.
Wir verwenden die C#-Clientbibliothek, um zwei Tabellen zu erstellen: eine „Players“-Tabelle für Spielerinformationen und eine „Scores“-Tabelle zum Speichern von Spielergebnissen. Dazu erstellen wir eine C#-Konsolenanwendung in Cloud Shell.
Klonen Sie zuerst den Beispielcode für dieses Codelab von GitHub, indem Sie den folgenden Befehl in Cloud Shell eingeben:
git clone https://github.com/GoogleCloudPlatform/dotnet-docs-samples.git
Wechseln Sie dann in das Verzeichnis „applications“, in dem Sie Ihre Anwendung erstellen.
cd dotnet-docs-samples/applications/
Der gesamte für dieses Codelab erforderliche Code befindet sich im vorhandenen Verzeichnis dotnet-docs-samples/applications/leaderboard als ausführbare C#-Anwendung mit dem Namen Leaderboard. Sie dient als Referenz, während Sie das Codelab durcharbeiten. Wir erstellen ein neues Verzeichnis und erstellen nach und nach eine Kopie der Bestenlistenanwendung.
Erstellen Sie mit dem folgenden Befehl ein neues Verzeichnis mit dem Namen „codelab“ für die Anwendung und wechseln Sie dorthin:
mkdir codelab && cd $_
Erstellen Sie mit dem folgenden Befehl eine neue .NET C#-Konsolenanwendung mit dem Namen „Leaderboard“:
dotnet new console -n Leaderboard
Mit diesem Befehl wird eine einfache Konsolenanwendung erstellt, die aus zwei primären Dateien besteht: der Projektdatei Leaderboard.csproj und der Programmdatei Program.cs.
Führen wir sie aus. Wechseln Sie in das neu erstellte Verzeichnis „Leaderboard“, in dem sich die Anwendung befindet:
cd Leaderboard
Geben Sie dann den folgenden Befehl ein, um ihn auszuführen.
dotnet run
Sie sollten die Anwendungsausgabe „Hello World!“ sehen.
Aktualisieren Sie nun die Konsolenanwendung, indem Sie Program.cs bearbeiten, um mit der C#-Spanner-Clientbibliothek eine Bestenliste mit den beiden Tabellen „Players“ und „Scores“ zu erstellen. Das können Sie direkt im Cloud Shell-Editor tun:
Öffnen Sie den Cloud Shell-Editor, indem Sie auf das unten hervorgehobene Symbol klicken:

Öffnen Sie als Nächstes die Datei Program.cs im Cloud Shell-Editor und ersetzen Sie den vorhandenen Code der Datei durch den Code, der zum Erstellen der Datenbank leaderboard und der Tabellen Players und Scores erforderlich ist. Fügen Sie dazu den folgenden C#-Anwendungscode in die Datei Program.cs ein:
using System;
using System.Threading.Tasks;
using Google.Cloud.Spanner.Data;
using CommandLine;
namespace GoogleCloudSamples.Leaderboard
{
[Verb("create", HelpText = "Create a sample Cloud Spanner database "
+ "along with sample 'Players' and 'Scores' tables in your project.")]
class CreateOptions
{
[Value(0, HelpText = "The project ID of the project to use "
+ "when creating Cloud Spanner resources.", Required = true)]
public string projectId { get; set; }
[Value(1, HelpText = "The ID of the instance where the sample database "
+ "will be created.", Required = true)]
public string instanceId { get; set; }
[Value(2, HelpText = "The ID of the sample database to create.",
Required = true)]
public string databaseId { get; set; }
}
public class Program
{
enum ExitCode : int
{
Success = 0,
InvalidParameter = 1,
}
public static object Create(string projectId,
string instanceId, string databaseId)
{
var response =
CreateAsync(projectId, instanceId, databaseId);
Console.WriteLine("Waiting for operation to complete...");
response.Wait();
Console.WriteLine($"Operation status: {response.Status}");
Console.WriteLine($"Created sample database {databaseId} on "
+ $"instance {instanceId}");
return ExitCode.Success;
}
public static async Task CreateAsync(
string projectId, string instanceId, string databaseId)
{
// Initialize request connection string for database creation.
string connectionString =
$"Data Source=projects/{projectId}/instances/{instanceId}";
using (var connection = new SpannerConnection(connectionString))
{
string createStatement = $"CREATE DATABASE `{databaseId}`";
string[] createTableStatements = new string[] {
// Define create table statement for Players table.
@"CREATE TABLE Players(
PlayerId INT64 NOT NULL,
PlayerName STRING(2048) NOT NULL
) PRIMARY KEY(PlayerId)",
// Define create table statement for Scores table.
@"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" };
// Make the request.
var cmd = connection.CreateDdlCommand(
createStatement, createTableStatements);
try
{
await cmd.ExecuteNonQueryAsync();
}
catch (SpannerException e) when
(e.ErrorCode == ErrorCode.AlreadyExists)
{
// OK.
}
}
}
public static int Main(string[] args)
{
var verbMap = new VerbMap<object>();
verbMap
.Add((CreateOptions opts) => Create(
opts.projectId, opts.instanceId, opts.databaseId))
.NotParsedFunc = (err) => 1;
return (int)verbMap.Run(args);
}
}
}
Das folgende Diagramm zeigt das Programm mit seinen wichtigsten Komponenten:

In der Datei Program.cs im Verzeichnis dotnet-docs-samples/applications/leaderboard/step4 finden Sie ein Beispiel dafür, wie Ihre Program.cs-Datei aussehen sollte, nachdem Sie den Code zum Aktivieren des Befehls create hinzugefügt haben.
Öffnen Sie als Nächstes die Projektdatei des Programms Leaderboard.csproj mit dem Cloud Shell-Editor und bearbeiten Sie sie so, dass sie dem folgenden Code entspricht. Speichern Sie alle Änderungen über das Menü „Datei“ des Cloud Shell-Editors.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Google.Cloud.Spanner.Data" Version="3.3.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\commandlineutil\Lib\CommandLineUtil.csproj" />
</ItemGroup>
</Project>
Durch diese Änderung wurde ein Verweis auf das C#-Spanner-NuGet-Paket Google.Cloud.Spanner.Data hinzugefügt, das für die Interaktion mit der Cloud Spanner API erforderlich ist. Durch diese Änderung wird auch ein Verweis auf das CommandLineUtil-Projekt hinzugefügt, das Teil des dotnet-doc-samples-GitHub-Repositorys ist und eine nützliche „verbmap“-Erweiterung für das Open-Source-Projekt CommandLineParser bietet. CommandLineParser ist eine praktische Bibliothek für die Verarbeitung von Befehlszeileneingaben für Konsolenanwendungen.
In der Datei Leaderboard.csproj im Verzeichnis dotnet-docs-samples/applications/leaderboard/step4 finden Sie ein Beispiel dafür, wie Ihre Leaderboard.csproj-Datei aussehen sollte, nachdem Sie den Code zum Aktivieren des Befehls create hinzugefügt haben.
Jetzt können Sie das aktualisierte Beispiel ausführen. Geben Sie Folgendes ein, um die Standardantwort Ihrer aktualisierten Anwendung zu sehen:
dotnet run
Die Ausgabe sollte etwa so aussehen:
Leaderboard 1.0.0 Copyright (C) 2018 Leaderboard ERROR(S): No verb selected. create Create a sample Cloud Spanner database along with sample 'Players' and 'Scores' tables in your project. help Display more information on a specific command. version Display version information.
Aus dieser Antwort geht hervor, dass es sich um die Anwendung Leaderboard handelt, die mit einem von drei möglichen Befehlen ausgeführt werden kann: create, help und version.
Wir probieren den Befehl create aus, um eine Spanner-Datenbank und -Tabellen zu erstellen. Führen Sie den Befehl ohne Argumente aus, um die erwarteten Argumente des Befehls zu sehen.
dotnet run create
Sie sollten eine Antwort wie die folgende sehen:
Leaderboard 1.0.0 Copyright (C) 2018 Leaderboard ERROR(S): A required value not bound to option name is missing. --help Display this help screen. --version Display version information. value pos. 0 Required. The project ID of the project to use when creating Cloud Spanner resources. value pos. 1 Required. The ID of the instance where the sample database will be created. value pos. 2 Required. The ID of the sample database to create.
Hier sehen wir, dass die erwarteten Argumente des Befehls create Projekt-ID, Instanz-ID und Datenbank-ID sind.
Führen Sie nun den folgenden Befehl aus. Achten Sie darauf, PROJECT_ID durch die Projekt-ID zu ersetzen, die Sie am Anfang dieses Codelabs erstellt haben.
dotnet run create PROJECT_ID cloudspanner-leaderboard leaderboard
Nach einigen Sekunden sollte eine Antwort wie die folgende angezeigt werden:
Waiting for operation to complete... Operation status: RanToCompletion Created sample database leaderboard on instance cloudspanner-leaderboard
Im Cloud Spanner-Bereich der Cloud Console sollten Ihre neue Datenbank und die neuen Tabellen im Menü auf der linken Seite angezeigt werden.

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. Jetzt verwenden wir die C#-Clientbibliothek, um die Tabelle Players mit Spielern und die Tabelle Scores mit zufälligen Punktzahlen für jeden Spieler zu füllen.
Öffnen Sie den Cloud Shell-Editor, indem Sie auf das unten hervorgehobene Symbol klicken:

Bearbeiten Sie als Nächstes die Datei Program.cs im Cloud Shell Editor, um einen insert-Befehl hinzuzufügen, mit dem 100 Spieler in die Tabelle Players eingefügt werden können. Alternativ kann der Befehl verwendet werden, um für jeden Spieler in der Tabelle Players vier zufällige Ergebnisse in die Tabelle Scores einzufügen.
Fügen Sie zuerst im „Verbmap“-Abschnitt oben im Programm unter dem vorhandenen create-Befehlsblock einen neuen insert-Befehlsblock hinzu:
[Verb("insert", HelpText = "Insert sample 'players' records or 'scores' records "
+ "into the database.")]
class InsertOptions
{
[Value(0, HelpText = "The project ID of the project to use "
+ "when managing Cloud Spanner resources.", Required = true)]
public string projectId { get; set; }
[Value(1, HelpText = "The ID of the instance where the sample database resides.",
Required = true)]
public string instanceId { get; set; }
[Value(2, HelpText = "The ID of the database where the sample database resides.",
Required = true)]
public string databaseId { get; set; }
[Value(3, HelpText = "The type of insert to perform, 'players' or 'scores'.",
Required = true)]
public string insertType { get; set; }
}
Fügen Sie als Nächstes die folgenden Insert-, InsertPlayersAsync- und InsertScoresAsync-Methoden unter der vorhandenen CreateAsync-Methode ein:
public static object Insert(string projectId,
string instanceId, string databaseId, string insertType)
{
if (insertType.ToLower() == "players")
{
var responseTask =
InsertPlayersAsync(projectId, instanceId, databaseId);
Console.WriteLine("Waiting for insert players operation to complete...");
responseTask.Wait();
Console.WriteLine($"Operation status: {responseTask.Status}");
}
else if (insertType.ToLower() == "scores")
{
var responseTask =
InsertScoresAsync(projectId, instanceId, databaseId);
Console.WriteLine("Waiting for insert scores operation to complete...");
responseTask.Wait();
Console.WriteLine($"Operation status: {responseTask.Status}");
}
else
{
Console.WriteLine("Invalid value for 'type of insert'. "
+ "Specify 'players' or 'scores'.");
return ExitCode.InvalidParameter;
}
Console.WriteLine($"Inserted {insertType} into sample database "
+ $"{databaseId} on instance {instanceId}");
return ExitCode.Success;
}
public static async Task InsertPlayersAsync(string projectId,
string instanceId, string databaseId)
{
string connectionString =
$"Data Source=projects/{projectId}/instances/{instanceId}"
+ $"/databases/{databaseId}";
long numberOfPlayers = 0;
using (var connection = new SpannerConnection(connectionString))
{
await connection.OpenAsync();
await connection.RunWithRetriableTransactionAsync(async (transaction) =>
{
// Execute a SQL statement to get current number of records
// in the Players table to use as an incrementing value
// for each PlayerName to be inserted.
var cmd = connection.CreateSelectCommand(
@"SELECT Count(PlayerId) as PlayerCount FROM Players");
numberOfPlayers = await cmd.ExecuteScalarAsync<long>();
// Insert 100 player records into the Players table.
SpannerBatchCommand cmdBatch = connection.CreateBatchDmlCommand();
for (int i = 0; i < 100; i++)
{
numberOfPlayers++;
SpannerCommand cmdInsert = connection.CreateDmlCommand(
"INSERT INTO Players "
+ "(PlayerId, PlayerName) "
+ "VALUES (@PlayerId, @PlayerName)",
new SpannerParameterCollection {
{"PlayerId", SpannerDbType.Int64},
{"PlayerName", SpannerDbType.String}});
cmdInsert.Parameters["PlayerId"].Value =
Math.Abs(Guid.NewGuid().GetHashCode());
cmdInsert.Parameters["PlayerName"].Value =
$"Player {numberOfPlayers}";
cmdBatch.Add(cmdInsert);
}
await cmdBatch.ExecuteNonQueryAsync();
});
}
Console.WriteLine("Done inserting player records...");
}
public static async Task InsertScoresAsync(
string projectId, string instanceId, string databaseId)
{
string connectionString =
$"Data Source=projects/{projectId}/instances/{instanceId}"
+ $"/databases/{databaseId}";
// Insert 4 score records into the Scores table for each player
// in the Players table.
using (var connection = new SpannerConnection(connectionString))
{
await connection.OpenAsync();
await connection.RunWithRetriableTransactionAsync(async (transaction) =>
{
Random r = new Random();
bool playerRecordsFound = false;
SpannerBatchCommand cmdBatch =
connection.CreateBatchDmlCommand();
var cmdLookup =
connection.CreateSelectCommand("SELECT * FROM Players");
using (var reader = await cmdLookup.ExecuteReaderAsync())
{
while (await reader.ReadAsync())
{
playerRecordsFound = true;
for (int i = 0; i < 4; i++)
{
DateTime randomTimestamp = DateTime.Now
.AddYears(r.Next(-2, 1))
.AddMonths(r.Next(-12, 1))
.AddDays(r.Next(-28, 0))
.AddHours(r.Next(-24, 0))
.AddSeconds(r.Next(-60, 0))
.AddMilliseconds(r.Next(-100000, 0));
SpannerCommand cmdInsert =
connection.CreateDmlCommand(
"INSERT INTO Scores "
+ "(PlayerId, Score, Timestamp) "
+ "VALUES (@PlayerId, @Score, @Timestamp)",
new SpannerParameterCollection {
{"PlayerId", SpannerDbType.Int64},
{"Score", SpannerDbType.Int64},
{"Timestamp",
SpannerDbType.Timestamp}});
cmdInsert.Parameters["PlayerId"].Value =
reader.GetFieldValue<int>("PlayerId");
cmdInsert.Parameters["Score"].Value =
r.Next(1000, 1000001);
cmdInsert.Parameters["Timestamp"].Value =
randomTimestamp.ToString("o");
cmdBatch.Add(cmdInsert);
}
}
if (!playerRecordsFound)
{
Console.WriteLine("Parameter 'scores' is invalid "
+ "since no player records currently exist. First "
+ "insert players then insert scores.");
Environment.Exit((int)ExitCode.InvalidParameter);
}
else
{
await cmdBatch.ExecuteNonQueryAsync();
Console.WriteLine(
"Done inserting score records..."
);
}
}
});
}
}
Damit der Befehl insert funktioniert, fügen Sie der „Main“-Methode Ihres Programms den folgenden Code hinzu:
.Add((InsertOptions opts) => Insert(
opts.projectId, opts.instanceId, opts.databaseId, opts.insertType))
In der Datei Program.cs im Verzeichnis dotnet-docs-samples/applications/leaderboard/step5 finden Sie ein Beispiel dafür, wie Ihre Program.cs-Datei aussehen sollte, nachdem Sie den Code zum Aktivieren des Befehls insert hinzugefügt haben.
Führen Sie das Programm nun aus, um zu bestätigen, dass der neue insert-Befehl in der Liste der möglichen Befehle des Programms enthalten ist. Führen Sie dazu diesen Befehl aus:
dotnet run
Der Befehl insert sollte jetzt in der Standardausgabe des Programms enthalten sein:
Leaderboard 1.0.0 Copyright (C) 2018 Leaderboard ERROR(S): No verb selected. create Create a sample Cloud Spanner database along with sample 'Players' and 'Scores' tables in your project. insert Insert sample 'players' records or 'scores' records into the database. help Display more information on a specific command. version Display version information.
Führen wir nun den Befehl insert aus, um die Eingabeargumente zu sehen. Geben Sie den folgenden Befehl ein.
dotnet run insert
Sie sollten die folgende Antwort erhalten:
Leaderboard 1.0.0 Copyright (C) 2018 Leaderboard ERROR(S): A required value not bound to option name is missing. --help Display this help screen. --version Display version information. value pos. 0 Required. The project ID of the project to use when managing Cloud Spanner resources. value pos. 1 Required. The ID of the instance where the sample database resides. value pos. 2 Required. The ID of the database where the sample database resides. value pos. 3 Required. The type of insert to perform, 'players' or 'scores'.
Aus der Antwort geht hervor, dass neben der Projekt‑ID, der Instanz‑ID und der Datenbank‑ID ein weiteres Argument value pos. 3 erwartet wird, nämlich der Typ des einzufügenden Elements. Dieses Argument kann den Wert „players“ oder „scores“ haben.
Führen wir nun den Befehl insert mit denselben Argumentwerten aus, die wir beim Aufrufen des Befehls create verwendet haben. Wir fügen „players“ als zusätzliches Argument für „type of insert“ hinzu. Achten Sie darauf, PROJECT_ID durch die Projekt-ID zu ersetzen, die Sie am Anfang dieses Codelabs erstellt haben.
dotnet run insert PROJECT_ID cloudspanner-leaderboard leaderboard players
Nach einigen Sekunden sollte eine Antwort wie die folgende angezeigt werden:
Waiting for insert players operation to complete... Done inserting player records... Operation status: RanToCompletion Inserted players into sample database leaderboard on instance cloudspanner-leaderboard
Jetzt verwenden wir die C#-Clientbibliothek, um die Tabelle Scores mit vier zufälligen Punktzahlen sowie Zeitstempeln für jeden Spieler in der Tabelle Players zu füllen.
Die Spalte Timestamp der Tabelle Scores wurde mit der folgenden SQL-Anweisung, die beim vorherigen Ausführen des Befehls create ausgeführt wurde, als „Commit-Zeitstempel“-Spalte definiert:
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 einer „Commit-Zeitstempel“-Spalte, die bei INSERT- und UPDATE-Vorgängen für eine bestimmte Tabellenzeile automatisch mit dem genauen Transaktionszeitstempel gefüllt werden kann.
Sie können auch eigene Zeitstempelwerte in eine Commit-Zeitstempelspalte einfügen, sofern Sie einen Zeitstempel mit einem Wert in der Vergangenheit einfügen. Das werden wir in diesem Codelab tun.
Führen wir nun den Befehl insert mit denselben Argumentwerten aus, die wir beim Aufrufen des Befehls create verwendet haben, und fügen „scores“ als zusätzliches Argument „type of insert“ hinzu. Achten Sie darauf, PROJECT_ID durch die Projekt-ID zu ersetzen, die Sie am Anfang dieses Codelabs erstellt haben.
dotnet run insert PROJECT_ID cloudspanner-leaderboard leaderboard scores
Nach einigen Sekunden sollte eine Antwort wie die folgende angezeigt werden:
Waiting for insert players operation to complete... Done inserting player records... Operation status: RanToCompletion Inserted players into sample database leaderboard on instance cloudspanner-leaderboard
Wenn Sie insert mit dem als scores angegebenen „type of insert“ (Einfügetyp) ausführen, wird die Methode InsertScoresAsync aufgerufen, die die folgenden Code-Snippets verwendet, um einen zufällig generierten Zeitstempel mit einem Datum und einer Uhrzeit in der Vergangenheit einzufügen:
DateTime randomTimestamp = DateTime.Now
.AddYears(r.Next(-2, 1))
.AddMonths(r.Next(-12, 1))
.AddDays(r.Next(-28, 0))
.AddHours(r.Next(-24, 0))
.AddSeconds(r.Next(-60, 0))
.AddMilliseconds(r.Next(-100000, 0));
...
cmdInsert.Parameters["Timestamp"].Value = randomTimestamp.ToString("o");
Wenn Sie die Spalte Timestamp automatisch mit dem Zeitstempel des genauen Zeitpunkts der „Insert“-Transaktion füllen möchten, können Sie stattdessen die C#-Konstante SpannerParameter.CommitTimestamp einfügen, wie im folgenden Code-Snippet:
cmd.Parameters["Timestamp"].Value = SpannerParameter.CommitTimestamp;
Nachdem wir die Daten geladen haben, überprüfen wir die Werte, die wir gerade in unsere neuen Tabellen 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 die Spalten PlayerId und PlayerName der Tabelle Daten enthalten.

Als Nächstes prüfen wir, ob die Tabelle „Scores“ auch Daten enthält. Klicken Sie dazu auf die Tabelle Scores und wählen Sie den Tab Data aus. Sie sollten sehen, dass die Spalten PlayerId, Timestamp und Score der Tabelle Daten enthalten.

Gut gemacht! Wir aktualisieren unser Programm, um einige Abfragen auszuführen, mit denen wir eine Bestenliste für Spiele erstellen können.
6. Bestenlistenabfragen ausführen
Nachdem wir unsere Datenbank eingerichtet und Informationen in unsere Tabellen geladen haben, erstellen wir nun eine Bestenliste mit diesen Daten. Dazu müssen wir die folgenden vier Fragen beantworten:
- Welche Spieler gehören zu den „Top 10“ aller Zeiten?
- Welche Spieler gehören zu den „Top Ten“ des Jahres?
- Welche Spieler gehören zu den Top 10 des Monats?
- Welche Spieler gehören zu den „Top Ten“ der Woche?
Aktualisieren wir unser Programm, damit die SQL-Abfragen ausgeführt werden, mit denen diese Fragen beantwortet werden.
Wir fügen einen query-Befehl hinzu, mit dem die Abfragen ausgeführt werden können, um die Fragen zu beantworten, die die für unsere Bestenliste erforderlichen Informationen liefern.
Bearbeiten Sie die Datei Program.cs im Cloud Shell-Editor, um das Programm zu aktualisieren und einen query-Befehl hinzuzufügen.
Fügen Sie zuerst im „Verbmap“-Abschnitt oben im Programm unter dem vorhandenen insert-Befehlsblock einen neuen query-Befehlsblock hinzu:
[Verb("query", HelpText = "Query players with 'Top Ten' scores within a specific timespan "
+ "from sample Cloud Spanner database table.")]
class QueryOptions
{
[Value(0, HelpText = "The project ID of the project to use "
+ "when managing Cloud Spanner resources.", Required = true)]
public string projectId { get; set; }
[Value(1, HelpText = "The ID of the instance where the sample data resides.",
Required = true)]
public string instanceId { get; set; }
[Value(2, HelpText = "The ID of the database where the sample data resides.",
Required = true)]
public string databaseId { get; set; }
[Value(3, Default = 0, HelpText = "The timespan in hours that will be used to filter the "
+ "results based on a record's timestamp. The default will return the "
+ "'Top Ten' scores of all time.")]
public int timespan { get; set; }
}
Fügen Sie als Nächstes die folgenden Query- und QueryAsync-Methoden unter der vorhandenen InsertScoresAsync-Methode ein:
public static object Query(string projectId,
string instanceId, string databaseId, int timespan)
{
var response = QueryAsync(
projectId, instanceId, databaseId, timespan);
response.Wait();
return ExitCode.Success;
}
public static async Task QueryAsync(
string projectId, string instanceId, string databaseId, int timespan)
{
string connectionString =
$"Data Source=projects/{projectId}/instances/"
+ $"{instanceId}/databases/{databaseId}";
// Create connection to Cloud Spanner.
using (var connection = new SpannerConnection(connectionString))
{
string sqlCommand;
if (timespan == 0)
{
// No timespan specified. Query Top Ten scores of all time.
sqlCommand =
@"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";
}
else
{
// Query Top Ten scores filtered by the timepan specified.
sqlCommand =
$@"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.ToString()} HOUR)
ORDER BY s.Score DESC LIMIT 10";
}
var cmd = connection.CreateSelectCommand(sqlCommand);
using (var reader = await cmd.ExecuteReaderAsync())
{
while (await reader.ReadAsync())
{
Console.WriteLine("PlayerId : "
+ reader.GetFieldValue<string>("PlayerId")
+ " PlayerName : "
+ reader.GetFieldValue<string>("PlayerName")
+ " Score : "
+ string.Format("{0:n0}",
Int64.Parse(reader.GetFieldValue<string>("Score")))
+ " Timestamp : "
+ reader.GetFieldValue<string>("Timestamp").Substring(0, 10));
}
}
}
}
Damit der Befehl query funktioniert, fügen Sie der „Main“-Methode Ihres Programms den folgenden Code hinzu:
.Add((QueryOptions opts) => Query(
opts.projectId, opts.instanceId, opts.databaseId, opts.timespan))
In der Datei Program.cs im Verzeichnis dotnet-docs-samples/applications/leaderboard/step6 finden Sie ein Beispiel dafür, wie Ihre Program.cs-Datei aussehen sollte, nachdem Sie den Code zum Aktivieren des Befehls query hinzugefügt haben.
Führen Sie das Programm nun aus, um zu bestätigen, dass der neue query-Befehl in der Liste der möglichen Befehle des Programms enthalten ist. Führen Sie dazu diesen Befehl aus:
dotnet run
Der Befehl query sollte jetzt als neue Befehlsoption in der Standardausgabe des Programms enthalten sein:
Leaderboard 1.0.0 Copyright (C) 2018 Leaderboard ERROR(S): No verb selected. create Create a sample Cloud Spanner database along with sample 'Players' and 'Scores' tables in your project. insert Insert sample 'players' records or 'scores' records into the database. query Query players with 'Top Ten' scores within a specific timespan from sample Cloud Spanner database table. help Display more information on a specific command. version Display version information.
Führen wir nun den Befehl query aus, um die Eingabeargumente zu sehen. Geben Sie den folgenden Befehl ein:
dotnet run query
Daraufhin wird die folgende Antwort zurückgegeben:
Leaderboard 1.0.0 Copyright (C) 2018 Leaderboard ERROR(S): A required value not bound to option name is missing. --help Display this help screen. --version Display version information. value pos. 0 Required. The project ID of the project to use when managing Cloud Spanner resources. value pos. 1 Required. The ID of the instance where the sample data resides. value pos. 2 Required. The ID of the database where the sample data resides. value pos. 3 (Default: 0) The timespan in hours that will be used to filter the results based on a record's timestamp. The default will return the 'Top Ten' scores of all time.
Aus der Antwort geht hervor, dass neben der Projekt-ID, der Instanz-ID und der Datenbank-ID ein weiteres Argument value pos. 3 erwartet wird. Damit können wir einen Zeitraum in Stunden angeben, der zum Filtern von Datensätzen anhand ihres Werts in der Spalte Timestamp der Tabelle Scores verwendet werden soll. Dieses Argument hat den Standardwert 0. Das bedeutet, dass keine Datensätze nach Zeitstempeln gefiltert werden. Wir können also den Befehl query ohne einen „timespan“-Wert verwenden, um eine Liste der zehn besten Spieler aller Zeiten zu erhalten.
Führen wir den Befehl query ohne Angabe eines „Zeitraums“ aus. Verwenden Sie dazu dieselben Argumentwerte wie beim Ausführen des Befehls create. Achten Sie darauf, PROJECT_ID durch die Projekt-ID zu ersetzen, die Sie am Anfang dieses Codelabs erstellt haben.
dotnet run query PROJECT_ID cloudspanner-leaderboard leaderboard
Sie sollten eine Antwort mit den zehn besten Spielern aller Zeiten sehen, die in etwa so aussieht:
PlayerId : 1843159180 PlayerName : Player 87 Score : 998,955 Timestamp : 2016-03-23
PlayerId : 61891198 PlayerName : Player 19 Score : 998,720 Timestamp : 2016-03-26
PlayerId : 340906298 PlayerName : Player 48 Score : 993,302 Timestamp : 2015-08-27
PlayerId : 541473117 PlayerName : Player 22 Score : 991,368 Timestamp : 2018-04-30
PlayerId : 857460496 PlayerName : Player 68 Score : 988,010 Timestamp : 2015-05-25
PlayerId : 1826646419 PlayerName : Player 91 Score : 984,022 Timestamp : 2016-11-26
PlayerId : 1002199735 PlayerName : Player 35 Score : 982,933 Timestamp : 2015-09-26
PlayerId : 2002563755 PlayerName : Player 23 Score : 979,041 Timestamp : 2016-10-25
PlayerId : 1377548191 PlayerName : Player 2 Score : 978,632 Timestamp : 2016-05-02
PlayerId : 1358098565 PlayerName : Player 65 Score : 973,257 Timestamp : 2016-10-30
Führen Sie nun den Befehl query mit den erforderlichen Argumenten aus, um die zehn besten Spieler des Jahres abzufragen. Geben Sie dazu einen „timespan“ an, der der Anzahl der Stunden in einem Jahr entspricht, also 8.760. Achten Sie darauf, PROJECT_ID durch die Projekt-ID zu ersetzen, die Sie am Anfang dieses Codelabs erstellt haben.
dotnet run query PROJECT_ID cloudspanner-leaderboard leaderboard 8760
Sie sollten eine Antwort mit den zehn besten Spielern des Jahres sehen, die so aussieht:
PlayerId : 541473117 PlayerName : Player 22 Score : 991,368 Timestamp : 2018-04-30
PlayerId : 228469898 PlayerName : Player 82 Score : 967,177 Timestamp : 2018-01-26
PlayerId : 1131343000 PlayerName : Player 26 Score : 944,725 Timestamp : 2017-05-26
PlayerId : 396780730 PlayerName : Player 41 Score : 929,455 Timestamp : 2017-09-26
PlayerId : 61891198 PlayerName : Player 19 Score : 921,251 Timestamp : 2018-05-01
PlayerId : 634269851 PlayerName : Player 54 Score : 909,379 Timestamp : 2017-07-24
PlayerId : 821111159 PlayerName : Player 55 Score : 908,402 Timestamp : 2017-05-25
PlayerId : 228469898 PlayerName : Player 82 Score : 889,040 Timestamp : 2017-12-26
PlayerId : 1408782275 PlayerName : Player 27 Score : 874,124 Timestamp : 2017-09-24
PlayerId : 1002199735 PlayerName : Player 35 Score : 864,758 Timestamp : 2018-04-24
Führen Sie nun den Befehl query aus, um die zehn besten Spieler des Monats abzufragen. Geben Sie dazu einen „timespan“ an, der der Anzahl der Stunden in einem Monat entspricht, also 730. Achten Sie darauf, PROJECT_ID durch die Projekt-ID zu ersetzen, die Sie am Anfang dieses Codelabs erstellt haben.
dotnet run query PROJECT_ID cloudspanner-leaderboard leaderboard 730
Sie sollten eine Antwort mit den zehn besten Spielern des Monats sehen, die in etwa so aussieht:
PlayerId : 541473117 PlayerName : Player 22 Score : 991,368 Timestamp : 2018-04-30
PlayerId : 61891198 PlayerName : Player 19 Score : 921,251 Timestamp : 2018-05-01
PlayerId : 1002199735 PlayerName : Player 35 Score : 864,758 Timestamp : 2018-04-24
PlayerId : 1228490432 PlayerName : Player 11 Score : 682,033 Timestamp : 2018-04-26
PlayerId : 648239230 PlayerName : Player 92 Score : 653,895 Timestamp : 2018-05-02
PlayerId : 70762849 PlayerName : Player 77 Score : 598,074 Timestamp : 2018-04-22
PlayerId : 1671215342 PlayerName : Player 62 Score : 506,770 Timestamp : 2018-04-28
PlayerId : 1208850523 PlayerName : Player 21 Score : 216,008 Timestamp : 2018-04-30
PlayerId : 1587692674 PlayerName : Player 63 Score : 188,157 Timestamp : 2018-04-25
PlayerId : 992391797 PlayerName : Player 37 Score : 167,175 Timestamp : 2018-04-30
Führen Sie nun den Befehl query aus, um die zehn besten Spieler der Woche abzufragen. Geben Sie dazu einen „timespan“ (Zeitraum) an, der der Anzahl der Stunden in einer Woche entspricht, also 168. Achten Sie darauf, PROJECT_ID durch die Projekt-ID zu ersetzen, die Sie am Anfang dieses Codelabs erstellt haben.
dotnet run query PROJECT_ID cloudspanner-leaderboard leaderboard 168
Sie sollten eine Antwort mit den zehn besten Spielern der Woche sehen, die so aussieht:
PlayerId : 541473117 PlayerName : Player 22 Score : 991,368 Timestamp : 2018-04-30
PlayerId : 61891198 PlayerName : Player 19 Score : 921,251 Timestamp : 2018-05-01
PlayerId : 228469898 PlayerName : Player 82 Score : 853,602 Timestamp : 2018-04-28
PlayerId : 1131343000 PlayerName : Player 26 Score : 695,318 Timestamp : 2018-04-30
PlayerId : 1228490432 PlayerName : Player 11 Score : 682,033 Timestamp : 2018-04-26
PlayerId : 1408782275 PlayerName : Player 27 Score : 671,827 Timestamp : 2018-04-27
PlayerId : 648239230 PlayerName : Player 92 Score : 653,895 Timestamp : 2018-05-02
PlayerId : 816861444 PlayerName : Player 83 Score : 622,277 Timestamp : 2018-04-27
PlayerId : 162043954 PlayerName : Player 75 Score : 572,634 Timestamp : 2018-05-02
PlayerId : 1671215342 PlayerName : Player 62 Score : 506,770 Timestamp : 2018-04-28
Super!
Wenn Sie jetzt Datensätze hinzufügen, skaliert Spanner Ihre Datenbank auf die gewünschte Größe.
Unabhängig davon, wie stark Ihre Datenbank wächst, kann die Bestenliste Ihres Spiels mit Spanner und der TrueTime-Technologie weiterhin genau skaliert werden.
7. Bereinigen
Nachdem wir uns mit Spanner ausgetobt haben, müssen wir unseren Spielplatz aufräumen, um wertvolle Ressourcen und Geld zu sparen. Glücklicherweise ist das ein einfacher Schritt. Rufen Sie einfach die Entwicklerkonsole auf und löschen Sie die Instanz, die wir im Codelab-Schritt „Cloud Spanner-Instanz einrichten“ erstellt haben.
8. Glückwunsch!
Behandelte Themen:
- Google Cloud Spanner-Instanzen, -Datenbanken und Tabellenschema für eine Bestenliste
- .NET Core C#-Konsolenanwendung erstellen
- Spanner-Datenbank und -Tabellen mit der C#-Clientbibliothek erstellen
- Daten mit der C#-Clientbibliothek in eine Spanner-Datenbank laden
- „Top 10“-Ergebnisse aus Ihren Daten mit Spanner-Commit-Zeitstempeln und der C#-Clientbibliothek abfragen
Vorgehensweise:
- CAP-Whitepaper zu Spanner lesen
- Best Practices für Schemadesign und Abfragen
- Weitere Informationen zu Commit-Zeitstempeln in Cloud Spanner
Feedback geben
- Bitte nehmen Sie sich einen Moment Zeit, um an unserer kurzen Umfrage teilzunehmen.