1. Descripción general
Google Cloud Spanner es un servicio de base de datos relacional, distribuido de forma global y escalable de forma vertical completamente administrado que proporciona transacciones ACID y semántica de SQL sin renunciar al rendimiento y a la alta disponibilidad.
En este lab, aprenderás a configurar una instancia de Cloud Spanner. Completarás los pasos para crear una base de datos y un esquema que se puedan usar en una tabla de clasificación de videojuegos. Comenzarás por crear una tabla de jugadores a fin de almacenar su información y una tabla de puntuaciones para almacenar sus puntuaciones.
A continuación, propagarás las tablas con datos de muestra. Por último, finalizarás el lab mediante la ejecución de las diez consultas de muestra principales y borrarás la instancia para liberar recursos.
Qué aprenderá
- Cómo configurar una instancia de Cloud Spanner
- Cómo crear una base de datos y tablas.
- Cómo usar una columna de marca de tiempo de confirmación
- Cómo cargar datos en tu tabla de base de datos de Cloud Spanner con marcas de tiempo
- Cómo consultar tu base de datos de Cloud Spanner.
- Cómo borrar tu instancia de Cloud Spanner.
Qué necesitarás
¿Cómo usarás este instructivo?
¿Cómo calificarías tu experiencia en Google Cloud Platform?
2. Configuración y requisitos
Configuración del entorno de autoaprendizaje
Si aún no tienes una Cuenta de Google (Gmail o Google Apps), debes crear una. Accede a Google Cloud Platform Console (console.cloud.google.com) y crea un proyecto nuevo.
Si ya tienes un proyecto, haz clic en el menú desplegable de selección de proyectos en la parte superior izquierda de la Console:
y haz clic en el botón “PROYECTO NUEVO” en el diálogo resultante para crear un proyecto nuevo:
Si aún no tienes un proyecto, deberías ver un cuadro de diálogo como este para crear el primero:
El cuadro de diálogo de creación posterior del proyecto te permite ingresar los detalles de tu proyecto nuevo:
Recuerda el ID del proyecto, que es un nombre único en todos los proyectos de Google Cloud (el nombre anterior ya se encuentra en uso y no lo podrá usar). Se mencionará más adelante en este codelab como PROJECT_ID
.
A continuación, si aún no lo has hecho, deberás habilitar la facturación en Developers Console para usar los recursos de Google Cloud y habilitar la API de Cloud Spanner.
Ejecutar este codelab debería costar solo unos pocos dólares, pero su costo podría aumentar si decides usar más recursos o si los dejas en ejecución (consulta la sección “Limpiar” al final de este documento). Los precios de Google Cloud Spanner se documentan aquí.
Los usuarios nuevos de Google Cloud Platform están aptas para obtener una prueba gratuita de $300, por lo que este codelab es completamente gratuito.
Configuración de Google Cloud Shell
Si bien Google Cloud y Spanner se pueden operar de manera remota desde tu laptop, en este codelab usaremos Google Cloud Shell, un entorno de línea de comandos que se ejecuta en la nube.
Esta máquina virtual basada en Debian está cargada con todas las herramientas de desarrollo que necesitarás. Ofrece un directorio principal persistente de 5 GB y se ejecuta en Google Cloud, lo que permite mejorar considerablemente el rendimiento de la red y la autenticación. Esto significa que todo lo que necesitarás para este Codelab es un navegador (sí, funciona en una Chromebook).
- Para activar Cloud Shell desde la consola de Cloud, solo haz clic en Activar Cloud Shell (el aprovisionamiento y la conexión al entorno debería llevar solo unos minutos).
Una vez conectado a Cloud Shell, debería ver que ya se autenticó y que el proyecto ya se configuró con tu PROJECT_ID
:
gcloud auth list
Resultado del comando
Credentialed accounts: - <myaccount>@<mydomain>.com (active)
gcloud config list project
Resultado del comando
[core] project = <PROJECT_ID>
Si, por algún motivo, el proyecto no está configurado, solo emite el siguiente comando:
gcloud config set project <PROJECT_ID>
Si no conoce su PROJECT_ID
, Observa el ID que usaste en los pasos de configuración o búscalo en el panel de la consola de Cloud:
Cloud Shell también configura algunas variables de entorno de forma predeterminada, lo que puede resultar útil cuando ejecutas comandos futuros.
echo $GOOGLE_CLOUD_PROJECT
Resultado del comando
<PROJECT_ID>
- Establece la zona predeterminada y la configuración del proyecto.
gcloud config set compute/zone us-central1-f
Puedes elegir una variedad de zonas diferentes. Para obtener más información, consulta Regiones y zonas.
Resumen
En este paso, configurarás tu entorno.
A continuación
A continuación, configurarás una instancia de Cloud Spanner.
3. Configura una instancia de Cloud Spanner
En este paso, configuraremos nuestra instancia de Cloud Spanner para este codelab. Busca la entrada de Spanner en el menú de opciones superior izquierdo o ingresa “/” y escribe “Spanner”
A continuación, haz clic en y completa el formulario. Para ello, ingresa el nombre de la instancia cloudspanner-leaderboard de la instancia, elige una configuración (selecciona una instancia regional) y establece la cantidad de nodos. Para este codelab solo necesitaremos 1 nodo. Para las instancias de producción y a fin de cumplir con los requisitos del ANS de Cloud Spanner, debes ejecutar 3 o más nodos en tu instancia de Cloud Spanner.
Por último, pero no menos importante, haz clic en “Crear” y, en segundos, selecciona una instancia de Cloud Spanner a su disposición.
En el siguiente paso, usaremos la biblioteca cliente de C# para crear una base de datos y un esquema en nuestra nueva instancia.
4. Crea una base de datos y un esquema
En este paso, crearemos nuestra base de datos de muestra y un esquema.
Usemos la biblioteca cliente de C# para crear dos tablas: una tabla de jugadores para la información de los jugadores y una tabla de puntuaciones para almacenar las puntuaciones de los jugadores. Para ello, repasaremos los pasos que te permitirán crear una aplicación de consola de C# en Cloud Shell.
Primero, clona el código de muestra para este codelab desde GitHub. Para ello, escribe el siguiente comando en Cloud Shell:
git clone https://github.com/GoogleCloudPlatform/dotnet-docs-samples.git
Luego, cambia el directorio al directorio “aplicaciones” en el que crearás tu aplicación.
cd dotnet-docs-samples/applications/
Todo el código necesario para este codelab se encuentra en el directorio dotnet-docs-samples/applications/leaderboard
existente como una aplicación ejecutable C# llamada Leaderboard
que sirve como referencia a medida que avanzas a través del codelab. Crearemos un directorio nuevo y compilaremos una copia de la aplicación Tablas de clasificación en etapas.
Crea un nuevo directorio llamado “codelab” para la aplicación y usa el comando de cambio de directorio a este con el siguiente comando:
mkdir codelab && cd $_
Crea una nueva aplicación de la consola .NET C# llamada "Leaderboard" con el siguiente comando:
dotnet new console -n Leaderboard
Este comando crea una aplicación de consola simple que consta de dos archivos principales: el archivo de proyecto Leaderboard.csproj
y el archivo de programa Program.cs
.
Ejecutémosla. Cambia el directorio al directorio de la tabla de clasificación recién creado en el que reside la aplicación:
cd Leaderboard
Luego, ingresa el siguiente comando para ejecutarlo.
dotnet run
Deberías ver el resultado de la aplicación “Hello World!”.
Ahora, actualicemos nuestra app de consola editando Program.cs
para usar la biblioteca cliente de C# Spanner a fin de crear una tabla de clasificación con dos tablas: Jugadores y puntuaciones. Puedes hacerlo directamente en el editor de Cloud Shell:
Haz clic en el siguiente ícono destacado para abrir el editor de Cloud Shell:
A continuación, abre el archivo Program.cs
en el editor de Cloud Shell y reemplaza el código existente del archivo por el código requerido para crear la base de datos leaderboard
y las tablas Players
y Scores
. Para ello, pega el siguiente código de la aplicación C# en el archivo 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);
}
}
}
Para proporcionar una imagen más clara del código del Programa, a continuación se incluye un diagrama del Programa con sus componentes principales etiquetados:
Puedes usar el archivo Program.cs
en el directorio dotnet-docs-samples/applications/leaderboard/step4
si deseas ver un ejemplo de cómo debe verse tu archivo Program.cs
después de agregar el código para habilitar el comando create
.
A continuación, usa el editor de Cloud Shell para abrir y editar el archivo de proyecto del programa Leaderboard.csproj
, y actualízalo para que se vea como el siguiente código. Asegúrese de guardar todos los cambios con la opción "Archivo" de Cloud Shell Editor.
<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>
Este cambio agregó una referencia al paquete Google.Cloud.Spanner.Data
de Nuget de Spanner de C# que necesitamos para interactuar con la API de Cloud Spanner. Este cambio también agrega una referencia al proyecto CommandLineUtil
, que forma parte del repositorio dotnet-doc-samples de GitHub y proporciona un "mapa de verbo" útil extensión para el CommandLineParser
de código abierto una biblioteca práctica para manejar la entrada de la línea de comandos para aplicaciones de consola.
Puedes usar el archivo Leaderboard.csproj
en el directorio dotnet-docs-samples/applications/leaderboard/step4
si deseas ver un ejemplo de cómo debe verse tu archivo Leaderboard.csproj
después de agregar el código para habilitar el comando create
.
Ya está todo listo para ejecutar la muestra actualizada. Escribe lo siguiente para ver la respuesta predeterminada de tu aplicación actualizada:
dotnet run
Deberías ver un resultado como el siguiente:
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.
En esta respuesta, podemos ver que esta es la aplicación Leaderboard
que se puede ejecutar con uno de tres comandos posibles: create
, help
y version
.
Probemos el comando create
para crear una base de datos y tablas de Spanner. Ejecuta el comando sin argumentos para ver los argumentos esperados.
dotnet run create
Deberías ver una respuesta como la siguiente:
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.
Aquí podemos ver que los argumentos esperados del comando create
son ID del proyecto, ID de instancia y ID de base de datos.
Ahora, ejecuta el siguiente comando: Asegúrate de reemplazar el PROJECT_ID
por el ID del proyecto que creaste al comienzo de este codelab.
dotnet run create PROJECT_ID cloudspanner-leaderboard leaderboard
Después de unos segundos, deberías ver una respuesta como la siguiente:
Waiting for operation to complete... Operation status: RanToCompletion Created sample database leaderboard on instance cloudspanner-leaderboard
En la sección de Cloud Spanner de la consola de Cloud, deberías ver su nueva base de datos y tablas en el menú del lado izquierdo.
En el próximo paso, actualizaremos nuestra aplicación para cargar algunos datos en tu base de datos nueva.
5. Cargar datos
Ahora tenemos una base de datos llamada leaderboard
que contiene dos tablas, Players
y Scores
. Ahora, usemos la biblioteca cliente de C# para propagar la tabla Players
con jugadores y la tabla Scores
con puntuaciones aleatorias para cada jugador.
Haz clic en el siguiente ícono destacado para abrir el editor de Cloud Shell:
A continuación, edita el archivo Program.cs
en el editor de Cloud Shell a fin de agregar un comando insert
que se pueda usar para insertar 100 jugadores a la tabla Players
o se puede usar a fin de insertar 4 puntuaciones aleatorias en el Scores
de cada jugador en la tabla Players
.
Primero, agrega un nuevo bloque de comandos insert
en el “Verbmap”. en la parte superior del programa, debajo del bloque de comando create
existente:
[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; }
}
Luego, agrega los siguientes métodos Insert
, InsertPlayersAsync
y InsertScoresAsync
debajo del método CreateAsync
existente:
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..."
);
}
}
});
}
}
Luego, para que el comando insert
funcione, agrega el siguiente código al elemento "Main" de tu programa. método:
.Add((InsertOptions opts) => Insert(
opts.projectId, opts.instanceId, opts.databaseId, opts.insertType))
Puedes usar el archivo Program.cs
en el directorio dotnet-docs-samples/applications/leaderboard/step5
si deseas ver un ejemplo de cómo debe verse tu archivo Program.cs
después de agregar el código para habilitar el comando insert
.
Ahora ejecutemos el programa para confirmar que el nuevo comando insert
esté incluido en la lista de comandos posibles del programa. Ejecuta el siguiente comando:
dotnet run
Deberías ver el comando insert
ahora incluido en el resultado predeterminado del programa:
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.
Ahora ejecutemos el comando insert
para ver sus argumentos de entrada. Escribe el comando siguiente.
dotnet run insert
Esto debería mostrar la siguiente respuesta:
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'.
Puedes ver en la respuesta que, además del ID del proyecto, el ID de instancia y el ID de la base de datos, hay otro argumento value pos. 3
esperado, que es el "tipo de inserción" para realizar. Este argumento puede tener un valor de "jugadores" o 'puntuaciones'.
Ahora ejecutemos el comando insert
con los mismos valores de argumento que usamos cuando llamamos al comando create
, y agreguemos “jugadores” como el argumento adicional “tipo de inserción”. Asegúrate de reemplazar el PROJECT_ID
por el ID del proyecto que creaste al comienzo de este codelab.
dotnet run insert PROJECT_ID cloudspanner-leaderboard leaderboard players
Después de unos segundos, deberías ver una respuesta como la siguiente:
Waiting for insert players operation to complete... Done inserting player records... Operation status: RanToCompletion Inserted players into sample database leaderboard on instance cloudspanner-leaderboard
Ahora, usemos la biblioteca cliente de C# para propagar la tabla Scores
con cuatro puntuaciones aleatorias junto con marcas de tiempo para cada jugador de la tabla Players
.
La columna Timestamp
de la tabla Scores
se definió como una columna de “marca de tiempo de confirmación” a través de la siguiente instrucción de SQL que se ejecutaba cuando ejecutamos el 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
Observa el atributo OPTIONS(allow_commit_timestamp=true)
. Esto hace que Timestamp
sea una columna de “marca de tiempo de confirmación” y permite que se propague automáticamente con la marca de tiempo exacta de la transacción para operaciones INSERTAR y ACTUALIZAR en una fila de tabla determinada.
También puedes insertar tus propios valores de marca de tiempo en una columna “marca de tiempo de confirmación”, siempre y cuando insertes una marca de tiempo con un valor anterior, lo que haremos para este codelab.
Ahora, ejecutemos el comando insert
con los mismos valores de argumento que usamos cuando llamamos al comando create
y agregamos “puntuaciones” como el argumento adicional “tipo de inserción”. Asegúrate de reemplazar el PROJECT_ID
por el ID del proyecto que creaste al comienzo de este codelab.
dotnet run insert PROJECT_ID cloudspanner-leaderboard leaderboard scores
Después de unos segundos, deberías ver una respuesta como la siguiente:
Waiting for insert players operation to complete... Done inserting player records... Operation status: RanToCompletion Inserted players into sample database leaderboard on instance cloudspanner-leaderboard
La ejecución de insert
con el “tipo de inserción” especificado como scores
llama al método InsertScoresAsync
, que usa los siguientes fragmentos de código para insertar una marca de tiempo generada de forma aleatoria con una fecha y hora en el pasado:
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");
Para propagar automáticamente la columna Timestamp
con la marca de tiempo del momento exacto en que aparece el elemento “Insertar” cuando se realiza la transacción, puedes insertar la constante SpannerParameter.CommitTimestamp
de C# como en el siguiente fragmento de código:
cmd.Parameters["Timestamp"].Value = SpannerParameter.CommitTimestamp;
Ahora que terminamos de cargar los datos, verifiquemos los valores que acabamos de escribir en nuestras tablas nuevas. Primero, selecciona la base de datos leaderboard
y, luego, la tabla Players
. Haz clic en la pestaña Data
. Deberías ver que tienes datos en las columnas PlayerId
y PlayerName
de la tabla.
A continuación, verifiquemos la tabla de puntuaciones. Para ello, haz clic en la tabla Scores
y selecciona la pestaña Data
. Deberías ver que tiene datos en las columnas PlayerId
, Timestamp
y Score
de la tabla.
¡Bien hecho! Actualicemos nuestro programa para ejecutar algunas consultas que podamos usar para crear una tabla de clasificación de videojuegos.
6. Ejecuta consultas sobre tablas de clasificación
Ahora que configuramos nuestra base de datos y cargamos información en nuestras tablas, vamos a crear una tabla de clasificación con estos datos. Para ello, debemos responder las siguientes cuatro preguntas:
- ¿Cuáles son los diez principales jugadores de todos los tiempos?
- ¿Cuáles son los diez jugadores principales de este año?
- ¿Cuáles son los diez jugadores principales del mes?
- ¿Cuáles son los diez jugadores principales de la semana?
Actualicemos nuestro programa para ejecutar las consultas en SQL que responderán estas preguntas.
Agregaremos un comando query
que proporcionará una manera de ejecutar las consultas a fin de responder las preguntas que producirán la información requerida en nuestra tabla de clasificación.
Edita el archivo Program.cs
en el editor de Cloud Shell para actualizar el programa y agregar un comando query
.
Primero, agrega un nuevo bloque de comandos query
en el “Verbmap”. en la parte superior del programa, debajo del bloque de comando insert
existente:
[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; }
}
Luego, agrega los siguientes métodos Query
y QueryAsync
debajo del método InsertScoresAsync
existente:
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));
}
}
}
}
Luego, para que el comando query
funcione, agrega el siguiente código al elemento "Main" de tu programa. método:
.Add((QueryOptions opts) => Query(
opts.projectId, opts.instanceId, opts.databaseId, opts.timespan))
Puedes usar el archivo Program.cs
en el directorio dotnet-docs-samples/applications/leaderboard/step6
si deseas ver un ejemplo de cómo debe verse tu archivo Program.cs
después de agregar el código para habilitar el comando query
.
Ahora ejecutemos el programa para confirmar que el nuevo comando query
esté incluido en la lista de comandos posibles del programa. Ejecuta el siguiente comando:
dotnet run
Deberías ver el comando query
ahora incluido en el resultado predeterminado del programa como una nueva opción de 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.
Ahora ejecutemos el comando query
para ver sus argumentos de entrada. Ingresa el siguiente comando:
dotnet run query
Se mostrará la siguiente respuesta:
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.
Como puedes ver en la respuesta, además del ID del proyecto, el ID de instancia y el ID de la base de datos, se espera otro argumento value pos. 3
que nos permite especificar un período en la cantidad de horas que se debe usar para filtrar registros según su valor en la columna Timestamp
de la tabla Scores
. Este argumento tiene un valor predeterminado de 0, lo que significa que no se filtrará ningún registro por marcas de tiempo. Por lo tanto, podemos usar el comando query
sin un valor de “intervalo” para obtener una lista de los “diez mejores” jugadores de todos los tiempos.
Ejecutamos el comando query
sin especificar un “período” con los mismos valores de argumento que usamos cuando ejecutamos el comando create
. Asegúrate de reemplazar el PROJECT_ID
por el ID del proyecto que creaste al comienzo de este codelab.
dotnet run query PROJECT_ID cloudspanner-leaderboard leaderboard
Deberías ver una respuesta que incluya los “diez mejores” jugadores de todos los tiempos, como se indica a continuación:
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
Ahora, ejecutemos el comando query
con los argumentos necesarios a fin de consultar los “diez mejores” jugadores del año. Para ello, especifica un “intervalo” igual a la cantidad de horas en un año, que es 8,760. Asegúrate de reemplazar el PROJECT_ID
por el ID del proyecto que creaste al comienzo de este codelab.
dotnet run query PROJECT_ID cloudspanner-leaderboard leaderboard 8760
Deberías ver una respuesta que incluya a los “diez mejores” jugadores del año, como se muestra a continuación:
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
Ahora, ejecutemos el comando query
a fin de consultar a los “diez mejores” jugadores del mes. Para ello, especifica un “intervalo” igual a la cantidad de horas de un mes, que es 730. Asegúrate de reemplazar el PROJECT_ID
por el ID del proyecto que creaste al comienzo de este codelab.
dotnet run query PROJECT_ID cloudspanner-leaderboard leaderboard 730
Deberías ver una respuesta que incluya a los “diez mejores” jugadores del mes, como se muestra a continuación:
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
Ahora, ejecutemos el comando query
a fin de consultar a los “diez mejores” jugadores de la semana. Para ello, especifica un “intervalo” igual a la cantidad de horas de una semana, que es 168. Asegúrate de reemplazar el PROJECT_ID
por el ID del proyecto que creaste al comienzo de este codelab.
dotnet run query PROJECT_ID cloudspanner-leaderboard leaderboard 168
Deberías ver una respuesta que incluya a los “diez mejores” jugadores de la semana, como se muestra a continuación:
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
¡Buen trabajo!
Ahora, a medida que agregues registros, Spanner escalará tu base de datos al tamaño que necesites.
Sin importar cuánto crezca tu base de datos, la tabla de clasificación de tu juego puede seguir escalando con precisión gracias a Spanner y su tecnología de Truetime.
7. Limpieza
Después de la diversión cuando se juega con Spanner, debemos limpiar nuestro zona de prueba para ahorrar dinero y recursos valiosos. Por suerte, este es un paso sencillo. Solo tienes que ir a Play Console y borrar la instancia que creamos en el paso del codelab llamado “Configura una instancia de Cloud Spanner”.
8. ¡Felicitaciones!
Temas abordados:
- Instancias, bases de datos y esquemas de tablas de Google Cloud Spanner para una tabla de clasificación
- Cómo crear una aplicación de consola de C# con .NET Core
- Cómo crear una base de datos y tablas de Spanner con la biblioteca cliente de C#
- Cómo cargar datos en una base de datos de Spanner con la biblioteca cliente de C#
- Cómo buscar los "diez principales" resultados de tus datos con las marcas de tiempo de confirmación de Spanner y la biblioteca cliente de C#
Próximos pasos:
- Lee el Informe de CAP de Spanner.
- Obtén información sobre el diseño de esquemas y las prácticas recomendadas de consulta.
- Obtén más información sobre las marcas de tiempo de confirmación de Cloud Spanner.
Envíanos tus comentarios
- Tómate un momento para completar nuestra breve encuesta