1. Panoramica
Google Cloud Spanner è un servizio di database relazionale completamente gestito, scalabile orizzontalmente e distribuito a livello globale che fornisce transazioni ACID e semantica SQL senza rinunciare a prestazioni e alta disponibilità.
In questo lab imparerai a configurare un'istanza Cloud Spanner. Seguirai la procedura per creare un database e uno schema da utilizzare per una classifica dei giochi. Inizierai creando una tabella dei giocatori per le informazioni sui giocatori e una tabella dei punteggi per memorizzare i punteggi dei giocatori.
Successivamente, completerai le tabelle con dati di esempio. Quindi concluderai il lab eseguendo alcune query di esempio principali e infine eliminando l'istanza per liberare risorse.
Cosa imparerai a fare
- Come configurare un'istanza Cloud Spanner.
- Come creare un database e le tabelle.
- Come utilizzare una colonna del timestamp di commit.
- Come caricare dati nella tabella di database Cloud Spanner con timestamp.
- Come eseguire query sul database Cloud Spanner.
- Come eliminare l'istanza Cloud Spanner.
Che cosa serve
Come utilizzerai questo tutorial?
Come giudichi la tua esperienza con la piattaforma Google Cloud?
2. Configurazione e requisiti
Configurazione dell'ambiente da seguire in modo autonomo
Se non disponi già di un account Google (Gmail o Google Apps), devi crearne uno. Accedi alla console della piattaforma Google Cloud ( console.cloud.google.com) e crea un nuovo progetto.
Se hai già un progetto, fai clic sul menu a discesa per la selezione del progetto in alto a sinistra nella console:
e fai clic su "NUOVO PROGETTO" nella finestra di dialogo risultante per creare un nuovo progetto:
Se non hai ancora un progetto, dovresti visualizzare una finestra di dialogo come questa per crearne uno:
La finestra di dialogo di creazione del progetto successiva ti consente di inserire i dettagli del nuovo progetto:
Ricorda l'ID progetto, che è un nome univoco tra tutti i progetti Google Cloud (il nome precedente è già in uso e non funzionerà per te). Verrà indicato più avanti in questo codelab come PROJECT_ID
.
Successivamente, se non l'hai ancora fatto, dovrai abilitare la fatturazione in Developers Console per utilizzare le risorse Google Cloud e abilitare l'API Cloud Spanner.
L'esecuzione di questo codelab non dovrebbe costare più di qualche euro, ma potrebbe essere più costoso se decidi di utilizzare più risorse o se le lasci in esecuzione (consulta la sezione relativa alla pulizia alla fine di questo documento). I prezzi di Google Cloud Spanner sono documentati qui.
I nuovi utenti di Google Cloud Platform hanno diritto a una prova senza costi di 300$, che dovrebbe rendere questo codelab completamente senza costi.
Configurazione di Google Cloud Shell
Mentre Google Cloud e Spanner possono essere gestiti da remoto dal tuo laptop, in questo codelab utilizzeremo Google Cloud Shell, un ambiente a riga di comando in esecuzione nel cloud.
Questa macchina virtuale basata su Debian viene caricata con tutti gli strumenti di sviluppo necessari. Offre una home directory permanente da 5 GB e viene eseguita in Google Cloud, migliorando notevolmente le prestazioni di rete e l'autenticazione. Ciò significa che per questo codelab sarà sufficiente un browser (sì, funziona su Chromebook).
- Per attivare Cloud Shell dalla console Cloud, fai clic su Attiva Cloud Shell (il provisioning e la connessione all'ambiente dovrebbero richiedere solo pochi minuti).
Una volta stabilita la connessione a Cloud Shell, dovresti vedere che hai già eseguito l'autenticazione e che il progetto è già impostato su PROJECT_ID
.
gcloud auth list
Output comando
Credentialed accounts: - <myaccount>@<mydomain>.com (active)
gcloud config list project
Output comando
[core] project = <PROJECT_ID>
Se, per qualche motivo, il progetto non è impostato, invia semplicemente il seguente comando:
gcloud config set project <PROJECT_ID>
Stai cercando il tuo PROJECT_ID
? Controlla l'ID utilizzato nei passaggi di configurazione o cercalo nella dashboard della console Cloud:
Cloud Shell imposta anche alcune variabili di ambiente per impostazione predefinita, cosa che può essere utile quando eseguirai comandi futuri.
echo $GOOGLE_CLOUD_PROJECT
Output comando
<PROJECT_ID>
- Infine, imposta la zona e la configurazione del progetto predefinite.
gcloud config set compute/zone us-central1-f
Puoi scegliere zone diverse. Per ulteriori informazioni, consulta Regioni e zone.
Riepilogo
In questo passaggio devi configurare il tuo ambiente.
Prossimo articolo
Ora devi configurare un'istanza Cloud Spanner.
3. Configura un'istanza Cloud Spanner
In questo passaggio configuriamo la nostra istanza Cloud Spanner per il codelab. Cerca la voce Spanner nel menu a tre linee in alto a sinistra o cerca Spanner premendo "/" e digita "Spanner"
Quindi, fai clic su e compila il modulo inserendo il nome dell'istanza cloudspanner-leaderboard per la tua istanza, scegliendo una configurazione (seleziona un'istanza a livello di regione) e imposta il numero di nodi. Per questo codelab avremo bisogno di un solo nodo. Per le istanze di produzione e per l'idoneità allo SLA (accordo sul livello del servizio) di Cloud Spanner, devi eseguire 3 o più nodi nella tua istanza Cloud Spanner.
Infine, fai clic su "Crea" e in pochi secondi hai a disposizione un'istanza Cloud Spanner.
Nel passaggio successivo utilizzeremo la libreria client C# per creare un database e uno schema nella nostra nuova istanza.
4. Crea un database e uno schema
In questo passaggio creeremo il database e lo schema di esempio.
Usiamo la libreria client C# per creare due tabelle; una tabella dei giocatori per le informazioni sui giocatori e una tabella dei punteggi per memorizzare i punteggi dei giocatori. Per farlo, seguiamo i passaggi per creare un'applicazione della console C# in Cloud Shell.
Per prima cosa, clona il codice campione per questo codelab da GitHub digitando il seguente comando in Cloud Shell:
git clone https://github.com/GoogleCloudPlatform/dotnet-docs-samples.git
Poi cambia la directory con "applications" in cui creerai l'applicazione.
cd dotnet-docs-samples/applications/
Tutto il codice necessario per questo codelab si trova nella directory dotnet-docs-samples/applications/leaderboard
esistente come applicazione C# eseguibile denominata Leaderboard
, che fungerà da riferimento man mano che procedi nel codelab. Creeremo una nuova directory e creeremo una copia dell'applicazione Leaderboard gradualmente.
Crea una nuova directory denominata "codelab" dell'applicazione e passare alla directory al suo interno con il seguente comando:
mkdir codelab && cd $_
Crea una nuova applicazione console .NET C# denominata "Leaderboard" utilizzando il seguente comando:
dotnet new console -n Leaderboard
Questo comando crea una semplice applicazione console composta da due file principali, il file di progetto Leaderboard.csproj
e il file di programma Program.cs
.
Eseguiamola. Passa alla directory Leaderboard appena creata, in cui si trova l'applicazione:
cd Leaderboard
Quindi, inserisci questo comando per eseguirlo.
dotnet run
Dovresti vedere l'output dell'applicazione "Hello World!".
Ora aggiorniamo l'app per console modificando Program.cs
in modo che utilizzi la libreria client Spanner C# per creare una classifica composta da due tabelle Giocatori e Punteggi. Puoi farlo direttamente nell'editor di Cloud Shell:
Apri l'editor di Cloud Shell facendo clic sull'icona evidenziata di seguito:
Successivamente, apri il file Program.cs
nell'editor di Cloud Shell e sostituisci il codice esistente del file con il codice necessario per creare il database leaderboard
e le tabelle Players
e Scores
incollando il seguente codice dell'applicazione C# nel file Program.cs
:
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);
}
}
}
Per fornire un quadro più chiaro del codice del Programma, ecco un diagramma del Programma con i suoi componenti principali etichettati:
Puoi utilizzare il file Program.cs
nella directory dotnet-docs-samples/applications/leaderboard/step4
per vedere un esempio dell'aspetto del file Program.cs
dopo aver aggiunto il codice per abilitare il comando create
.
Dopodiché usa l'editor di Cloud Shell per aprire e modificare il file di progetto del programma Leaderboard.csproj
, aggiornandolo in modo che abbia l'aspetto del codice riportato di seguito. Assicurati di salvare tutte le modifiche utilizzando il campo "File". dell'editor di 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>
Questa modifica ha aggiunto un riferimento al pacchetto Nuget di Spanner C# Google.Cloud.Spanner.Data
di cui abbiamo bisogno per interagire con l'API Cloud Spanner. Questa modifica aggiunge anche un riferimento al progetto CommandLineUtil
che fa parte del repository GitHub dotnet-doc-samples e fornisce un'utile "verbmap" all'open source CommandLineParser
; una pratica libreria per gestire l'input della riga di comando per le applicazioni della console.
Puoi utilizzare il file Leaderboard.csproj
nella directory dotnet-docs-samples/applications/leaderboard/step4
per vedere un esempio dell'aspetto del file Leaderboard.csproj
dopo aver aggiunto il codice per abilitare il comando create
.
Ora è tutto pronto per eseguire l'esempio aggiornato. Digita quanto segue per visualizzare la risposta predefinita dell'applicazione aggiornata:
dotnet run
Dovresti vedere un output simile al seguente:
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.
Da questa risposta possiamo vedere che si tratta dell'applicazione Leaderboard
che può essere eseguita con uno dei tre comandi possibili: create
, help
e version
.
Proviamo il comando create
per creare un database e le tabelle Spanner. Esegui il comando senza argomenti per visualizzare quelli previsti.
dotnet run create
Dovresti vedere una risposta simile alla seguente:
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.
Qui possiamo vedere che gli argomenti previsti per il comando create
sono ID progetto, ID istanza e ID database.
Ora esegui questo comando. Assicurati di sostituire PROJECT_ID
con l'ID progetto che hai creato all'inizio di questo codelab.
dotnet run create PROJECT_ID cloudspanner-leaderboard leaderboard
Dopo un paio di secondi, dovresti visualizzare una risposta come la seguente:
Waiting for operation to complete... Operation status: RanToCompletion Created sample database leaderboard on instance cloudspanner-leaderboard
Nella sezione Cloud Spanner della console Cloud dovresti vedere il nuovo database e le nuove tabelle nel menu a sinistra.
Nel passaggio successivo aggiorneremo la nostra applicazione per caricare alcuni dati nel nuovo database.
5. Carica dati
Ora abbiamo un database chiamato leaderboard
contenente due tabelle: Players
e Scores
. Ora utilizziamo la libreria client C# per popolare la tabella Players
con i giocatori e la tabella Scores
con punteggi casuali per ogni giocatore.
Apri l'editor di Cloud Shell facendo clic sull'icona evidenziata di seguito:
Quindi, modifica il file Program.cs
nell'editor di Cloud Shell per aggiungere un comando insert
che possa essere utilizzato per inserire 100 giocatori nella tabella Players
o che possa essere utilizzato per inserire 4 punteggi casuali nella tabella Scores
per ogni giocatore nella tabella Players
.
Innanzitutto aggiungi un nuovo blocco di comando insert
in "Verbmap" nella parte superiore del programma, sotto il blocco di comandi create
esistente:
[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; }
}
Poi aggiungi i seguenti metodi Insert
, InsertPlayersAsync
e InsertScoresAsync
sotto il metodo CreateAsync
esistente:
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..."
);
}
}
});
}
}
Quindi, per rendere funzionale il comando insert
, aggiungi il seguente codice al campo "Main" del programma :
.Add((InsertOptions opts) => Insert(
opts.projectId, opts.instanceId, opts.databaseId, opts.insertType))
Puoi utilizzare il file Program.cs
nella directory dotnet-docs-samples/applications/leaderboard/step5
per vedere un esempio dell'aspetto del file Program.cs
dopo aver aggiunto il codice per abilitare il comando insert
.
Ora eseguiamo il programma per confermare che il nuovo comando insert
sia incluso nell'elenco dei possibili comandi del programma. Esegui questo comando:
dotnet run
Il comando insert
ora dovrebbe essere incluso nell'output predefinito del programma:
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.
Ora eseguiamo il comando insert
per vedere gli argomenti di input. Inserisci il seguente comando.
dotnet run insert
Dovrebbe essere restituita la seguente risposta:
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'.
Puoi notare dalla risposta che oltre all'ID progetto, all'ID istanza e all'ID database è previsto un altro argomento value pos. 3
, che è il "tipo di inserimento" per l'esecuzione. Questo argomento può avere il valore "players" o "punteggi".
Ora eseguiamo il comando insert
con gli stessi valori di argomento che abbiamo utilizzato quando abbiamo chiamato il comando create
, aggiungendo "players" come "tipo di inserto" aggiuntivo . Assicurati di sostituire PROJECT_ID
con l'ID progetto che hai creato all'inizio di questo codelab.
dotnet run insert PROJECT_ID cloudspanner-leaderboard leaderboard players
Dopo un paio di secondi, dovresti visualizzare una risposta come la seguente:
Waiting for insert players operation to complete... Done inserting player records... Operation status: RanToCompletion Inserted players into sample database leaderboard on instance cloudspanner-leaderboard
Ora utilizziamo la libreria client C# per completare la tabella Scores
con quattro punteggi casuali e i timestamp per ogni giocatore nella tabella Players
.
La colonna Timestamp
della tabella Scores
è stata definita come "timestamp di impegno" tramite la seguente istruzione SQL eseguita quando in precedenza abbiamo eseguito il comando 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
Osserva l'attributo OPTIONS(allow_commit_timestamp=true)
. Timestamp
diventa un "timestamp di commit" e consente di compilarla automaticamente con il timestamp esatto della transazione per le operazioni INSERT e UPDATE su una determinata riga della tabella.
Puoi anche inserire i tuoi valori di timestamp in un "timestamp di commit" devi inserire un timestamp con un valore nel passato, che è ciò che faremo in questo codelab.
Ora eseguiamo il comando insert
con gli stessi valori di argomento che abbiamo utilizzato quando abbiamo chiamato il comando create
aggiungendo "scores" come "tipo di inserto" aggiuntivo . Assicurati di sostituire PROJECT_ID
con l'ID progetto che hai creato all'inizio di questo codelab.
dotnet run insert PROJECT_ID cloudspanner-leaderboard leaderboard scores
Dopo un paio di secondi, dovresti visualizzare una risposta come la seguente:
Waiting for insert players operation to complete... Done inserting player records... Operation status: RanToCompletion Inserted players into sample database leaderboard on instance cloudspanner-leaderboard
insert
in esecuzione con il "tipo di inserto" specificato come scores
chiama il metodo InsertScoresAsync
, che utilizza i seguenti snippet di codice per inserire un timestamp generato in modo casuale con data e ora del passato:
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");
Per compilare automaticamente la colonna Timestamp
con il timestamp corrispondente al momento esatto in cui "Inserisci" avviene la transazione, puoi inserire la costante C# SpannerParameter.CommitTimestamp
come nel seguente snippet di codice:
cmd.Parameters["Timestamp"].Value = SpannerParameter.CommitTimestamp;
Ora che il caricamento dei dati è stato completato, verifichiamo i valori che abbiamo appena scritto nelle nuove tabelle. Seleziona prima il database leaderboard
, quindi seleziona la tabella Players
. Fai clic sulla scheda Data
. Dovresti vedere che sono presenti dati nelle colonne PlayerId
e PlayerName
della tabella.
Verifica che anche la tabella dei punteggi contenga dei dati facendo clic sulla tabella Scores
e selezionando la scheda Data
. Dovresti vedere che sono presenti dati nelle colonne PlayerId
, Timestamp
e Score
della tabella.
Ben fatto! Aggiorniamo il nostro Programma in modo da eseguire alcune query che possiamo utilizzare per creare una classifica dei giochi.
6. Eseguire query sulla classifica
Ora che abbiamo impostato il database e caricato le informazioni nelle nostre tabelle, possiamo creare un leaderboard utilizzando questi dati. Per farlo, dobbiamo rispondere alle seguenti quattro domande:
- Quali giocatori sono i "primi dieci" di tutti i tempi?
- Quali giocatori sono i "primi dieci" dell'anno?
- Quali giocatori sono i "primi dieci" del mese?
- Quali giocatori sono i "primi dieci" della settimana?
Aggiorniamo il nostro programma in modo che esegua le query SQL che rispondono a queste domande.
Aggiungeremo un comando query
che fornirà un modo per eseguire le query e rispondere alle domande che produrranno le informazioni richieste per la nostra classifica.
Modifica il file Program.cs
nell'editor di Cloud Shell per aggiornare il programma e aggiungere un comando query
.
Innanzitutto aggiungi un nuovo blocco di comando query
in "Verbmap" nella parte superiore del programma, sotto il blocco di comandi insert
esistente:
[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; }
}
Poi aggiungi i seguenti metodi Query
e QueryAsync
sotto il metodo InsertScoresAsync
esistente:
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));
}
}
}
}
Quindi, per rendere funzionale il comando query
, aggiungi il seguente codice al campo "Main" del programma :
.Add((QueryOptions opts) => Query(
opts.projectId, opts.instanceId, opts.databaseId, opts.timespan))
Puoi utilizzare il file Program.cs
nella directory dotnet-docs-samples/applications/leaderboard/step6
per vedere un esempio dell'aspetto del file Program.cs
dopo aver aggiunto il codice per abilitare il comando query
.
Ora eseguiamo il programma per confermare che il nuovo comando query
sia incluso nell'elenco dei possibili comandi del programma. Esegui questo comando:
dotnet run
Il comando query
ora dovrebbe essere incluso nell'output predefinito del programma come nuova opzione di comando:
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.
Ora eseguiamo il comando query
per vedere gli argomenti di input. Inserisci questo comando:
dotnet run query
Verrà restituita la seguente risposta:
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.
Dalla risposta puoi notare che oltre all'ID progetto, all'ID istanza e all'ID database è previsto un altro argomento value pos. 3
che ci consente di specificare un intervallo di tempo in numero di ore da utilizzare per filtrare i record in base al relativo valore nella colonna Timestamp
della tabella Scores
. Questo argomento ha un valore predefinito pari a 0, il che significa che nessun record sarà filtrato in base ai timestamp. Quindi possiamo usare il comando query
senza "timespan" per ottenere un elenco dei "primi dieci" giocatori di tutti i tempi.
Eseguiamo il comando query
senza specificare un valore "timespan", utilizzando gli stessi valori di argomento che abbiamo utilizzato quando abbiamo eseguito il comando create
. Assicurati di sostituire PROJECT_ID
con l'ID progetto che hai creato all'inizio di questo codelab.
dotnet run query PROJECT_ID cloudspanner-leaderboard leaderboard
Dovresti vedere una risposta che include i "primi dieci" giocatori di tutti i tempi, come i seguenti:
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
Ora eseguiamo il comando query
con gli argomenti necessari per eseguire una query sui "primi dieci" giocatori dell'anno specificando un "periodo" equivale al numero di ore in un anno che è 8760. Assicurati di sostituire PROJECT_ID
con l'ID progetto che hai creato all'inizio di questo codelab.
dotnet run query PROJECT_ID cloudspanner-leaderboard leaderboard 8760
Dovresti vedere una risposta che include i "primi dieci" giocatori dell'anno come i seguenti:
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
Ora eseguiamo il comando query
per interrogare i "primi dieci" giocatori del mese specificando un "intervallo di tempo" equivale al numero di ore di un mese, che è 730. Assicurati di sostituire PROJECT_ID
con l'ID progetto che hai creato all'inizio di questo codelab.
dotnet run query PROJECT_ID cloudspanner-leaderboard leaderboard 730
Dovresti vedere una risposta che include i "primi dieci" giocatori del mese, come i seguenti:
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
Ora eseguiamo il comando query
per interrogare i "primi dieci" giocatori della settimana specificando un "intervallo di tempo" equivale al numero di ore di una settimana che è 168. Assicurati di sostituire PROJECT_ID
con l'ID progetto che hai creato all'inizio di questo codelab.
dotnet run query PROJECT_ID cloudspanner-leaderboard leaderboard 168
Dovresti vedere una risposta che include i "primi dieci" giocatori della settimana come i seguenti:
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
Eccellente.
Ora che aggiungi i record, Spanner scala il tuo database in base alle tue esigenze.
Indipendentemente dalla crescita del tuo database, la classifica del tuo gioco può continuare a crescere con precisione grazie a Spanner e alla sua tecnologia Truetime.
7. Esegui la pulizia
Dopo il divertimento con Spanner dobbiamo ripulire il nostro parco giochi, risparmiando risorse e denaro preziosi. Fortunatamente, questo è un passaggio semplice: basta accedere alla console per gli sviluppatori ed eliminare l'istanza che abbiamo creato nel passaggio del codelab denominato "Configura un'istanza Cloud Spanner".
8. Complimenti!
Argomenti trattati:
- Schema delle tabelle, dei database e delle istanze di Google Cloud Spanner per una classifica
- Creare un'applicazione console .NET Core C#
- Creare un database e tabelle Spanner utilizzando la libreria client C#
- Come caricare i dati in un database Spanner utilizzando la libreria client C#
- Come eseguire una query nella sezione "Top dieci" di risultati dai tuoi dati utilizzando i timestamp di commit di Spanner e la libreria client C#
Passaggi successivi:
- Leggi il white paper su Spanner sui CAP
- Scopri di più sulle best practice per la progettazione di schemi e le query
- Scopri di più sui timestamp di commit di Cloud Spanner
Inviaci il tuo feedback
- Dedica qualche istante a completare il nostro breve sondaggio