1. Przegląd
Google Cloud Spanner to w pełni zarządzana, skalowana w poziomie, globalnie rozproszona relacyjna baza danych, która zapewnia transakcje ACID i semantykę SQL bez utraty wydajności i wysokiej dostępności.
Z tego modułu dowiesz się, jak skonfigurować instancję Cloud Spanner. Poznasz kolejne etapy tworzenia bazy danych i schematu, które można wykorzystać w tabeli wyników w grze. Zacznij od utworzenia tabeli Players do przechowywania informacji o graczach i tabeli Scores do przechowywania wyników graczy.
Następnie wypełnisz tabele przykładowymi danymi. Następnie zakończysz laboratorium, uruchamiając kilka przykładowych zapytań z listy Top Ten i usuwając instancję, aby zwolnić zasoby.
Czego się nauczysz
- Jak skonfigurować instancję Cloud Spanner.
- Jak utworzyć bazę danych i tabele.
- Jak korzystać z kolumny sygnatury czasowej zatwierdzenia.
- Jak wczytać dane do tabeli bazy danych Cloud Spanner ze znacznikami czasu.
- Jak wysyłać zapytania do bazy danych Cloud Spanner.
- Jak usunąć instancję Cloud Spanner.
Czego potrzebujesz
Jak zamierzasz korzystać z tego samouczka?
Jak oceniasz korzystanie z Google Cloud Platform?
2. Konfiguracja i wymagania
Samodzielne konfigurowanie środowiska
Jeśli nie masz jeszcze konta Google (Gmail lub Google Apps), musisz je utworzyć. Zaloguj się w konsoli Google Cloud Platform ( console.cloud.google.com) i utwórz nowy projekt.
Jeśli masz już projekt, kliknij menu wyboru projektu w lewym górnym rogu konsoli:

i w wyświetlonym oknie kliknij przycisk „NOWY PROJEKT”, aby utworzyć nowy projekt:

Jeśli nie masz jeszcze projektu, powinien wyświetlić się taki dialog, w którym możesz utworzyć pierwszy projekt:

W kolejnym oknie dialogowym tworzenia projektu możesz wpisać szczegóły nowego projektu:

Zapamiętaj identyfikator projektu, który jest unikalną nazwą we wszystkich projektach Google Cloud (podana powyżej nazwa jest już zajęta i nie będzie działać w Twoim przypadku). W dalszej części tego laboratorium będzie on nazywany PROJECT_ID.
Następnie, jeśli nie zostało to jeszcze zrobione, musisz włączyć płatności w Konsoli deweloperów, aby korzystać z zasobów Google Cloud i włączyć interfejs Cloud Spanner API.

Wykonanie tego samouczka nie powinno kosztować więcej niż kilka dolarów, ale może okazać się droższe, jeśli zdecydujesz się wykorzystać więcej zasobów lub pozostawisz je uruchomione (patrz sekcja „Czyszczenie” na końcu tego dokumentu). Ceny Google Cloud Spanner są opisane tutaj.
Nowi użytkownicy Google Cloud Platform mogą skorzystać z bezpłatnego okresu próbnego, w którym mają do dyspozycji środki w wysokości 300 USD, co powinno sprawić, że ten samouczek będzie całkowicie bezpłatny.
Konfiguracja Google Cloud Shell
Z Google Cloud i Spanner można korzystać zdalnie na laptopie, ale w tym ćwiczeniu programistycznym będziemy używać Google Cloud Shell, czyli środowiska wiersza poleceń działającego w chmurze.
Ta maszyna wirtualna oparta na Debianie zawiera wszystkie potrzebne narzędzia dla programistów. Zawiera również stały katalog domowy o pojemności 5 GB i działa w Google Cloud, co znacznie zwiększa wydajność sieci i usprawnia proces uwierzytelniania. Oznacza to, że do ukończenia tego ćwiczenia potrzebujesz tylko przeglądarki (działa ona na Chromebooku).
- Aby aktywować Cloud Shell w konsoli Cloud, kliknij Aktywuj Cloud Shell
(udostępnienie środowiska i połączenie się z nim powinno zająć tylko kilka chwil).
Po połączeniu z Cloud Shell zobaczysz, że uwierzytelnianie zostało już przeprowadzone, a projekt jest już ustawiony na Twój identyfikator projektu PROJECT_ID.
gcloud auth list
Wynik polecenia
Credentialed accounts: - <myaccount>@<mydomain>.com (active)
gcloud config list project
Wynik polecenia
[core] project = <PROJECT_ID>
Jeśli z jakiegoś powodu projekt nie jest ustawiony, po prostu wydaj to polecenie:
gcloud config set project <PROJECT_ID>
Szukasz urządzenia PROJECT_ID? Sprawdź, jakiego identyfikatora użyto w krokach konfiguracji, lub wyszukaj go w panelu konsoli Cloud:
Cloud Shell domyślnie ustawia też niektóre zmienne środowiskowe, które mogą być przydatne podczas wykonywania kolejnych poleceń.
echo $GOOGLE_CLOUD_PROJECT
Wynik polecenia
<PROJECT_ID>
- Na koniec ustaw domyślną strefę i konfigurację projektu.
gcloud config set compute/zone us-central1-f
Możesz wybrać różne strefy. Więcej informacji znajdziesz w artykule Regiony i strefy.
Podsumowanie
W tym kroku skonfigurujesz środowisko.
Dalsze czynności
Następnie skonfigurujesz instancję Cloud Spanner.
3. Konfigurowanie instancji Cloud Spanner
W tym kroku skonfigurujemy instancję Cloud Spanner na potrzeby tych ćwiczeń z programowania. Wyszukaj wpis Spanner
w menu hamburger po lewej stronie u góry
lub wyszukaj Spanner, naciskając „/” i wpisując „Spanner”.

Następnie kliknij
i wypełnij formularz, wpisując nazwę instancji cloudspanner-leaderboard, wybierając konfigurację (wybierz instancję regionalną) i ustawiając liczbę węzłów. W tym laboratorium potrzebny będzie tylko 1 węzeł. W przypadku instancji produkcyjnych i aby kwalifikować się do gwarancji jakości usług Cloud Spanner, musisz uruchomić co najmniej 3 węzły w instancji Cloud Spanner.
Na koniec kliknij „Utwórz”. W ciągu kilku sekund będziesz mieć do dyspozycji instancję Cloud Spanner.

W następnym kroku użyjemy biblioteki klienta C#, aby utworzyć bazę danych i schemat w nowej instancji.
4. Tworzenie bazy danych i schematu
W tym kroku utworzymy przykładową bazę danych i schemat.
Za pomocą biblioteki klienta C# utworzymy 2 tabele: Players (informacje o graczach) i Scores (wyniki graczy). W tym celu pokażemy Ci, jak utworzyć aplikację konsolową w C# w Cloud Shell.
Najpierw sklonuj przykładowy kod do tego ćwiczenia z GitHuba, wpisując w Cloud Shell to polecenie:
git clone https://github.com/GoogleCloudPlatform/dotnet-docs-samples.git
Następnie przejdź do katalogu „applications”, w którym utworzysz aplikację.
cd dotnet-docs-samples/applications/
Cały kod wymagany w tym ćwiczeniu znajduje się w katalogu dotnet-docs-samples/applications/leaderboard jako uruchamiana aplikacja w języku C# o nazwie Leaderboard. Będzie ona służyć jako punkt odniesienia w trakcie wykonywania ćwiczenia. Utworzymy nowy katalog i stopniowo skopiujemy aplikację Tabele wyników.
Utwórz nowy katalog o nazwie „codelab” na potrzeby aplikacji i przejdź do niego za pomocą tego polecenia:
mkdir codelab && cd $_
Utwórz nową aplikację konsolową .NET C# o nazwie „Leaderboard”, używając tego polecenia:
dotnet new console -n Leaderboard
To polecenie tworzy prostą aplikację konsolową składającą się z 2 głównych plików: pliku projektu Leaderboard.csproj i pliku programu Program.cs.
Zacznijmy. Przejdź do nowo utworzonego katalogu tabeli wyników, w którym znajduje się aplikacja:
cd Leaderboard
Następnie wpisz to polecenie, aby je uruchomić.
dotnet run
Powinny wyświetlić się dane wyjściowe aplikacji „Hello World!”.
Teraz zaktualizujmy aplikację konsolową, edytując plik Program.cs, aby używać biblioteki klienta Spanner w języku C# do utworzenia tabeli wyników składającej się z 2 tabel: Players i Scores. Możesz to zrobić bezpośrednio w edytorze Cloud Shell:
Otwórz edytor Cloud Shell, klikając ikonę wyróżnioną poniżej:

Następnie otwórz plik Program.cs w edytorze Cloud Shell i zastąp dotychczasowy kod pliku kodem wymaganym do utworzenia bazy danych leaderboard oraz tabel Players i Scores. Wklej do pliku Program.cs ten kod aplikacji w języku C#:
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);
}
}
}
Aby lepiej przedstawić kod Programu, poniżej znajdziesz diagram Programu z oznaczonymi głównymi komponentami:

W pliku Program.cs w katalogu dotnet-docs-samples/applications/leaderboard/step4 możesz zobaczyć przykład tego, jak powinien wyglądać plik Program.cs po dodaniu kodu umożliwiającego działanie polecenia create.
Następnie otwórz i edytuj plik projektu programu Leaderboard.csproj w edytorze Cloud Shell, aby wyglądał jak poniższy kod. Pamiętaj, aby zapisać wszystkie zmiany za pomocą menu „Plik” w edytorze Cloud Shell.
<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>
Ta zmiana dodała odwołanie do pakietu C# Spanner Nuget Google.Cloud.Spanner.Data, którego potrzebujemy do interakcji z interfejsem Cloud Spanner API. Ta zmiana dodaje też odwołanie do CommandLineUtil projektu, który jest częścią repozytorium GitHub dotnet-doc-samples i udostępnia przydatne rozszerzenie „verbmap” do CommandLineParser o otwartym kodzie źródłowym, czyli wygodnej biblioteki do obsługi danych wejściowych wiersza poleceń w aplikacjach konsolowych.
W pliku Leaderboard.csproj w katalogu dotnet-docs-samples/applications/leaderboard/step4 możesz zobaczyć przykład tego, jak powinien wyglądać plik Leaderboard.csproj po dodaniu kodu umożliwiającego działanie polecenia create.
Możesz teraz uruchomić zaktualizowany przykład. Aby zobaczyć domyślną odpowiedź zaktualizowanej aplikacji, wpisz to polecenie:
dotnet run
Powinny pojawić się dane wyjściowe podobne do tych:
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.
Z tej odpowiedzi wynika, że jest to aplikacja Leaderboard, którą można uruchomić za pomocą jednego z 3 poleceń: create, help i version.
Wypróbujmy polecenie create, aby utworzyć bazę danych i tabele Spanner. Uruchom polecenie bez argumentów, aby zobaczyć oczekiwane argumenty polecenia.
dotnet run create
Powinna pojawić się odpowiedź podobna do tej:
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.
Widzimy, że oczekiwane argumenty polecenia create to identyfikator projektu, identyfikator instancji i identyfikator bazy danych.
Teraz uruchom to polecenie. Pamiętaj, aby zastąpić PROJECT_ID identyfikatorem projektu utworzonym na początku tych ćwiczeń z programowania.
dotnet run create PROJECT_ID cloudspanner-leaderboard leaderboard
Po kilku sekundach powinna pojawić się odpowiedź podobna do tej:
Waiting for operation to complete... Operation status: RanToCompletion Created sample database leaderboard on instance cloudspanner-leaderboard
W sekcji Cloud Spanner w Cloud Console w menu po lewej stronie powinna być widoczna nowa baza danych i tabele.

W następnym kroku zaktualizujemy aplikację, aby wczytać dane do nowej bazy danych.
5. Wczytaj dane
Mamy teraz bazę danych o nazwie leaderboard zawierającą 2 tabele: Players i Scores. Teraz użyjemy biblioteki klienta C#, aby wypełnić tabelę Players graczami, a tabelę Scores losowymi wynikami każdego gracza.
Otwórz edytor Cloud Shell, klikając ikonę wyróżnioną poniżej:

Następnie w edytorze Cloud Shell zmodyfikuj plik Program.cs, aby dodać polecenie insert, które można wykorzystać do wstawienia 100 graczy do tabeli Players lub do wstawienia 4 losowych wyników do tabeli Scores dla każdego gracza w tabeli Players.
Najpierw dodaj nowy blok poleceń insert w sekcji „Verbmap” u góry programu, pod istniejącym blokiem poleceń create:
[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; }
}
Następnie dodaj te metody: Insert, InsertPlayersAsync i InsertScoresAsync poniżej istniejącej metody CreateAsync:
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..."
);
}
}
});
}
}
Aby polecenie insert działało, dodaj ten kod do metody „Main” programu:
.Add((InsertOptions opts) => Insert(
opts.projectId, opts.instanceId, opts.databaseId, opts.insertType))
W pliku Program.cs w katalogu dotnet-docs-samples/applications/leaderboard/step5 możesz zobaczyć przykład tego, jak powinien wyglądać plik Program.cs po dodaniu kodu umożliwiającego działanie polecenia insert.
Teraz uruchom program, aby potwierdzić, że nowe polecenie insert jest uwzględnione na liście możliwych poleceń programu. Uruchom to polecenie:
dotnet run
W domyślnych danych wyjściowych programu powinno się teraz pojawić polecenie insert:
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.
Teraz uruchom polecenie insert, aby wyświetlić jego argumenty wejściowe. Wpisz to polecenie.
dotnet run insert
Powinna pojawić się taka odpowiedź:
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'.
Z odpowiedzi wynika, że oprócz identyfikatora projektu, identyfikatora instancji i identyfikatora bazy danych oczekiwany jest jeszcze jeden argument value pos. 3, czyli „typ wstawienia” do wykonania. Ten argument może mieć wartość „players” lub „scores”.
Teraz uruchom polecenie insert z tymi samymi wartościami argumentów, których użyliśmy podczas wywoływania polecenia create, dodając „players” jako dodatkowy argument „type of insert”. Pamiętaj, aby zastąpić PROJECT_ID identyfikatorem projektu utworzonym na początku tych ćwiczeń z programowania.
dotnet run insert PROJECT_ID cloudspanner-leaderboard leaderboard players
Po kilku sekundach powinna pojawić się odpowiedź podobna do tej:
Waiting for insert players operation to complete... Done inserting player records... Operation status: RanToCompletion Inserted players into sample database leaderboard on instance cloudspanner-leaderboard
Teraz użyjemy biblioteki klienta C#, aby wypełnić tabelę Scores 4 losowymi wynikami wraz z sygnaturami czasowymi dla każdego gracza w tabeli Players.
Kolumna Timestamp tabeli Scores została zdefiniowana jako kolumna „sygnatura czasowa zatwierdzenia” za pomocą następującej instrukcji SQL, która została wykonana, gdy wcześniej uruchomiliśmy polecenie 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
Zwróć uwagę na atrybut OPTIONS(allow_commit_timestamp=true). Dzięki temu kolumna Timestamp staje się „sygnaturą czasową zatwierdzenia” i może być automatycznie wypełniana dokładną sygnaturą czasową transakcji w przypadku operacji INSERT i UPDATE w danym wierszu tabeli.
Możesz też wstawiać własne wartości sygnatury czasowej do kolumny „commit timestamp”, o ile wstawisz sygnaturę czasową z wartością z przeszłości. Tak właśnie zrobimy na potrzeby tego kursu.
Teraz uruchom polecenie insert z tymi samymi wartościami argumentów, których użyliśmy podczas wywoływania polecenia create, dodając „scores” jako dodatkowy argument „type of insert” (typ wstawienia). Pamiętaj, aby zastąpić PROJECT_ID identyfikatorem projektu utworzonym na początku tych ćwiczeń z programowania.
dotnet run insert PROJECT_ID cloudspanner-leaderboard leaderboard scores
Po kilku sekundach powinna pojawić się odpowiedź podobna do tej:
Waiting for insert players operation to complete... Done inserting player records... Operation status: RanToCompletion Inserted players into sample database leaderboard on instance cloudspanner-leaderboard
Uruchomienie insert z określonym „typem wstawienia” jako scores wywołuje metodę InsertScoresAsync, która używa tych fragmentów kodu do wstawiania losowo wygenerowanej sygnatury czasowej z datą i godziną z przeszłości:
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");
Aby automatycznie wypełnić kolumnę Timestamp sygnaturą czasową dokładnie w momencie, gdy nastąpi transakcja „Wstaw”, możesz zamiast tego wstawić stałą C# SpannerParameter.CommitTimestamp, jak w tym fragmencie kodu:
cmd.Parameters["Timestamp"].Value = SpannerParameter.CommitTimestamp;
Załadowaliśmy już dane, więc sprawdźmy teraz wartości, które właśnie zapisaliśmy w nowych tabelach. Najpierw wybierz bazę danych leaderboard, a potem tabelę Players. Kliknij kartę Data. W kolumnach PlayerId i PlayerName tabeli powinny być widoczne dane.

Następnie sprawdź, czy tabela Wyniki też zawiera dane. Aby to zrobić, kliknij tabelę Scores i wybierz kartę Data. Powinny być widoczne dane w kolumnach PlayerId, Timestamp i Score tabeli.

Brawo! Zaktualizujmy nasz program, aby uruchomić zapytania, których możemy użyć do utworzenia tabeli wyników w grze.
6. Uruchamianie zapytań dotyczących tabeli wyników
Skonfigurowaliśmy już bazę danych i wczytaliśmy informacje do tabel. Teraz utwórzmy na podstawie tych danych tabelę wyników. Aby to zrobić, musimy odpowiedzieć na te 4 pytania:
- Którzy gracze należą do „Top Ten” wszech czasów?
- Którzy gracze są w „Top 10” roku?
- Którzy gracze należą do „Top 10” miesiąca?
- Którzy gracze znaleźli się w „Top 10” tygodnia?
Zaktualizujmy program, aby uruchamiał zapytania SQL, które pozwolą odpowiedzieć na te pytania.
Dodamy polecenie query, które umożliwi uruchamianie zapytań w celu uzyskania odpowiedzi na pytania, które dostarczą informacji potrzebnych do utworzenia naszej tabeli wyników.
Edytuj plik Program.cs w edytorze Cloud Shell, aby zaktualizować program i dodać polecenie query.
Najpierw dodaj nowy blok poleceń query w sekcji „Verbmap” u góry programu, pod istniejącym blokiem poleceń insert:
[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; }
}
Następnie dodaj te metody Query i QueryAsync poniżej istniejącej metody InsertScoresAsync:
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));
}
}
}
}
Aby polecenie query działało, dodaj ten kod do metody „Main” programu:
.Add((QueryOptions opts) => Query(
opts.projectId, opts.instanceId, opts.databaseId, opts.timespan))
W pliku Program.cs w katalogu dotnet-docs-samples/applications/leaderboard/step6 możesz zobaczyć przykład tego, jak powinien wyglądać plik Program.cs po dodaniu kodu umożliwiającego działanie polecenia query.
Teraz uruchom program, aby potwierdzić, że nowe polecenie query jest uwzględnione na liście możliwych poleceń programu. Uruchom to polecenie:
dotnet run
Polecenie query powinno być teraz uwzględnione w domyślnych danych wyjściowych programu jako nowa opcja polecenia:
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.
Teraz uruchom polecenie query, aby wyświetlić jego argumenty wejściowe. Wpisz to polecenie:
dotnet run query
Spowoduje to wyświetlenie takiej odpowiedzi:
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.
Z odpowiedzi wynika, że oprócz identyfikatora projektu, identyfikatora instancji i identyfikatora bazy danych jest jeszcze jeden argument value pos. 3, który pozwala określić przedział czasu w godzinach, aby filtrować rekordy na podstawie ich wartości w kolumnie Timestamp tabeli Scores. Ten argument ma wartość domyślną 0, co oznacza, że żadne rekordy nie będą filtrowane według sygnatur czasowych. Możemy więc użyć polecenia query bez wartości „timespan”, aby uzyskać listę 10 najlepszych graczy wszech czasów.
Uruchommy polecenie query bez określania „timespan”, używając tych samych wartości argumentów, których użyliśmy podczas uruchamiania polecenia create. Pamiętaj, aby zastąpić PROJECT_ID identyfikatorem projektu utworzonym na początku tych ćwiczeń z programowania.
dotnet run query PROJECT_ID cloudspanner-leaderboard leaderboard
Powinna pojawić się odpowiedź zawierająca 10 najlepszych graczy wszech czasów, np. taka:
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
Teraz uruchom polecenie query z odpowiednimi argumentami, aby wyszukać 10 najlepszych graczy w danym roku. W tym celu określ „timespan” (okres) równy liczbie godzin w roku, czyli 8760. Pamiętaj, aby zastąpić PROJECT_ID identyfikatorem projektu utworzonym na początku tych ćwiczeń z programowania.
dotnet run query PROJECT_ID cloudspanner-leaderboard leaderboard 8760
Powinna pojawić się odpowiedź zawierająca 10 najlepszych graczy roku, np. taka:
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
Teraz uruchom polecenie query, aby wyszukać 10 najlepszych graczy w danym miesiącu, określając „timespan” (okres) równy liczbie godzin w miesiącu, czyli 730. Pamiętaj, aby zastąpić PROJECT_ID identyfikatorem projektu utworzonym na początku tych ćwiczeń z programowania.
dotnet run query PROJECT_ID cloudspanner-leaderboard leaderboard 730
Powinna pojawić się odpowiedź zawierająca „10 najlepszych” graczy w danym miesiącu, np. taka:
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
Teraz uruchom polecenie query, aby wysłać zapytanie o „Najlepszą dziesiątkę” graczy tygodnia, określając „timespan” równy liczbie godzin w tygodniu, czyli 168. Pamiętaj, aby zastąpić PROJECT_ID identyfikatorem projektu utworzonym na początku tych ćwiczeń z programowania.
dotnet run query PROJECT_ID cloudspanner-leaderboard leaderboard 168
Powinna się wyświetlić odpowiedź zawierająca „10 najlepszych” graczy tygodnia, np. taka:
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
Doskonale!
Teraz, gdy dodajesz rekordy, Spanner będzie skalować bazę danych do dowolnego rozmiaru.
Niezależnie od tego, jak bardzo rozrośnie się Twoja baza danych, tablica wyników w grze może być nadal skalowana z dokładnością dzięki Spannerowi i technologii Truetime.
7. Czyszczenie
Po zabawie z usługą Spanner musimy posprzątać nasz plac zabaw, aby zaoszczędzić cenne zasoby i pieniądze. Na szczęście to łatwy krok. Wystarczy otworzyć konsolę dewelopera i usunąć instancję utworzoną w ramach ćwiczenia „Konfigurowanie instancji Cloud Spanner”.
8. Gratulacje!
Omówione zagadnienia:
- Instancje, bazy danych i schemat tabeli Google Cloud Spanner dla tablicy wyników
- Jak utworzyć aplikację konsolową w C# w .NET Core
- Tworzenie bazy danych i tabel w Spannerze za pomocą biblioteki klienta w języku C#
- Wczytywanie danych do bazy danych Spanner za pomocą biblioteki klienta C#
- Jak wysyłać zapytania o wyniki „Top Ten” z danych za pomocą sygnatur czasowych zatwierdzenia Spanner i biblioteki klienta C#
Dalsze kroki:
- Przeczytaj dokument dotyczący spójności w Spannerze.
- Dowiedz się więcej o projektowaniu schematu i sprawdzonych metodach dotyczących zapytań.
- Dowiedz się więcej o sygnaturach czasu zatwierdzenia w Cloud Spanner.
Prześlij nam swoją opinię
- Poświęć chwilę na wypełnienie naszej krótkiej ankiety.