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 con Google Cloud Platform?
Configuración del entorno a su propio ritmo
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 Cloud Console, 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 Cloud Console:
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.
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 Java para crear una base de datos y un esquema en la instancia nueva.
En este paso, crearemos nuestra base de datos de muestra y un esquema.
Usemos la biblioteca cliente de Java para crear dos tablas, una tabla de jugadores con información sobre ellos y una tabla de puntuaciones a fin de almacenar las puntuaciones Para ello, analizaremos los pasos a fin de crear una aplicación de consola de Java 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/java-docs-samples.git
Luego, cambia el directorio al directorio “aplicaciones” en el que crearás tu aplicación.
cd java-docs-samples/spanner/leaderboard
Todo el código necesario para este codelab se encuentra en el directorio java-docs-samples/spanner/leaderboard/complete
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 básica de Java llamada “Tabla de clasificación” con el siguiente comando de Maven (mvn):
mvn -B archetype:generate -DarchetypeGroupId=org.apache.maven.archetypes -DgroupId=com.google.codelabs -DartifactId=leaderboard -DarchetypeVersion=1.4
Este comando crea una aplicación simple de consola que consta de dos archivos principales: el archivo de configuración de la aplicación Maven pom.xml
y el archivo de aplicación de Java App.java
.
A continuación, usa el comando de cambio de directorio al directorio de la tabla de clasificación que acabas de crear y haz una lista de su contenido:
cd leaderboard && ls
Deberías ver los archivos pom.xml
y src
en la lista:
pom.xml src
Ahora, actualicemos esta app de Console mediante la edición de App.java
para usar la biblioteca cliente de Java Spanner a fin de crear una tabla de clasificación que contenga dos tabla, jugadores y puntuación. Puedes hacerlo directamente en el editor de Cloud Shell:
Haz clic en el siguiente ícono destacado para abrir el editor de Cloud Shell:
Abre pom.xml en la carpeta de la tabla de clasificación. Abre el archivo pom.xml ubicado en la carpeta java-docs-samples\ spanner\leaderboard\codelab\leaderboard
. En este archivo, se configura el sistema de compilación de Maven para compilar nuestra aplicación en un archivo jar, incluidas todas nuestras dependencias.
Agrega la siguiente 1 sección de administración de dependencias nueva debajo del elemento </properties> existente:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-bom</artifactId>
<version>0.83.0-alpha</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
Además, agrega 1 dependencia nueva en la sección <dependencies> existente, que agregará la biblioteca cliente de Java de Cloud Spanner a la aplicación.
<dependency>
<!-- Version auto-managed by BOM -->
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-spanner</artifactId>
</dependency>
Luego, reemplaza la sección <build> existente del archivo pom.xml
por la siguiente sección <build>:
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.5.5</version>
<configuration>
<finalName>leaderboard</finalName>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<mainClass>com.google.codelabs.App</mainClass>
</manifest>
</archive>
<appendAssemblyId>false</appendAssemblyId>
<attach>false</attach>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>3.0.0-M3</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M3</version>
<configuration>
<useSystemClassLoader>false</useSystemClassLoader>
</configuration>
</plugin>
</plugins>
</build>
Para guardar los cambios realizados en el archivo pom.xml, selecciona “Guardar” en el menú “Archivo” del editor de Cloud Shell o presiona las teclas “Ctrl” y “S” juntas.
A continuación, abre el archivo App.java
en el editor de Cloud Shell ubicado en la carpeta src/main/java/com/google/codelabs/
. Para reemplazar el código existente del archivo con el código necesario a fin de crear la base de datos leaderboard
y las tablas Players
y Scores
, pega el siguiente código Java en el archivo App.java
:
package com.google.codelabs;
import com.google.api.gax.longrunning.OperationFuture;
import com.google.cloud.spanner.Database;
import com.google.cloud.spanner.DatabaseAdminClient;
import com.google.cloud.spanner.DatabaseClient;
import com.google.cloud.spanner.DatabaseId;
import com.google.cloud.spanner.Spanner;
import com.google.cloud.spanner.SpannerException;
import com.google.cloud.spanner.SpannerExceptionFactory;
import com.google.cloud.spanner.SpannerOptions;
import com.google.spanner.admin.database.v1.CreateDatabaseMetadata;
import java.util.Arrays;
import java.util.concurrent.ExecutionException;
/**
* Example code for using the Cloud Spanner API with the Google Cloud Java client library
* to create a simple leaderboard.
*
* This example demonstrates:
*
* <p>
*
* <ul>
* <li>Creating a Cloud Spanner database.
* </ul>
*/
public class App {
static void create(DatabaseAdminClient dbAdminClient, DatabaseId db) {
OperationFuture<Database, CreateDatabaseMetadata> op =
dbAdminClient.createDatabase(
db.getInstanceId().getInstance(),
db.getDatabase(),
Arrays.asList(
"CREATE TABLE Players(\n"
+ " PlayerId INT64 NOT NULL,\n"
+ " PlayerName STRING(2048) NOT NULL\n"
+ ") PRIMARY KEY(PlayerId)",
"CREATE TABLE Scores(\n"
+ " PlayerId INT64 NOT NULL,\n"
+ " Score INT64 NOT NULL,\n"
+ " Timestamp TIMESTAMP NOT NULL\n"
+ " OPTIONS(allow_commit_timestamp=true)\n"
+ ") PRIMARY KEY(PlayerId, Timestamp),\n"
+ "INTERLEAVE IN PARENT Players ON DELETE NO ACTION"));
try {
// Initiate the request which returns an OperationFuture.
Database dbOperation = op.get();
System.out.println("Created database [" + dbOperation.getId() + "]");
} catch (ExecutionException e) {
// If the operation failed during execution, expose the cause.
throw (SpannerException) e.getCause();
} catch (InterruptedException e) {
// Throw when a thread is waiting, sleeping, or otherwise occupied,
// and the thread is interrupted, either before or during the activity.
throw SpannerExceptionFactory.propagateInterrupt(e);
}
}
static void printUsageAndExit() {
System.out.println("Leaderboard 1.0.0");
System.out.println("Usage:");
System.out.println(" java -jar leaderboard.jar "
+ "<command> <instance_id> <database_id> [command_option]");
System.out.println("");
System.out.println("Examples:");
System.out.println(" java -jar leaderboard.jar create my-instance example-db");
System.out.println(" - Create a sample Cloud Spanner database along with "
+ "sample tables in your project.\n");
System.exit(1);
}
public static void main(String[] args) throws Exception {
if (!(args.length == 3 || args.length == 4)) {
printUsageAndExit();
}
SpannerOptions options = SpannerOptions.newBuilder().build();
Spanner spanner = options.getService();
try {
String command = args[0];
DatabaseId db = DatabaseId.of(options.getProjectId(), args[1], args[2]);
DatabaseClient dbClient = spanner.getDatabaseClient(db);
DatabaseAdminClient dbAdminClient = spanner.getDatabaseAdminClient();
switch (command) {
case "create":
create(dbAdminClient, db);
break;
default:
printUsageAndExit();
}
} finally {
spanner.close();
}
System.out.println("Closed client");
}
}
Para guardar los cambios realizados en el archivo App.java
, selecciona “Guardar” en el menú “Archivo” de Cloud Shell.
Puedes usar el archivo App.java
en el directorio java-docs-samples/spanner/leaderboard/step4/src
si deseas ver un ejemplo de cómo debe verse tu archivo App.java
después de agregar el código para habilitar el comando create
.
Para compilar tu app, ejecuta el paquete mvn desde el directorio en el que se encuentra el pom.xml
:
mvn package
Una vez que su archivo jar de Java se haya compilado correctamente, ejecuta el siguiente comando para ejecutar la aplicación resultante en Cloud Shell:
java -jar target/leaderboard.jar
Deberías ver un resultado como el siguiente:
Leaderboard 1.0.0 Usage: java -jar leaderboard.jar <command> <instance_id> <database_id> [command_option] Examples: java -jar leaderboard.jar create my-instance example-db - Create a sample Cloud Spanner database along with sample tables in your project.
En esta respuesta, podemos ver que esta es la aplicación Leaderboard
, que en la actualidad tiene un comando posible: create
. Podemos ver que los argumentos esperados del comando create
son el ID de instancia y el ID de la base de datos.
Ahora, ejecuta el siguiente comando:
java -jar target/leaderboard.jar create cloudspanner-leaderboard leaderboard
Después de unos segundos, deberías ver una respuesta como la siguiente:
Created database [projects/your-project/instances/cloudspanner-leaderboard/databases/leaderboard]
En la sección de Cloud Spanner de Cloud Console, 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.
Ahora tenemos una base de datos llamada leaderboard
que contiene dos tablas, Players
y Scores
. Ahora usemos la biblioteca cliente de Java a fin de propagar la tabla Players
con los jugadores y nuestra tabla Scores
con puntuaciones aleatorias para cada jugador.
Si aún no se abrió, haz clic en el ícono que se destaca a continuación para abrir el editor de Cloud Shell:
A continuación, edita el archivo App.java
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, actualiza la sección imports
en la parte superior del archivo de la app y reemplaza el contenido actual de modo que, una vez que termines, se vea de la siguiente manera:
package com.google.codelabs;
import static com.google.cloud.spanner.TransactionRunner.TransactionCallable;
import com.google.api.gax.longrunning.OperationFuture;
import com.google.cloud.spanner.Database;
import com.google.cloud.spanner.DatabaseAdminClient;
import com.google.cloud.spanner.DatabaseClient;
import com.google.cloud.spanner.DatabaseId;
import com.google.cloud.spanner.Mutation;
import com.google.cloud.spanner.ResultSet;
import com.google.cloud.spanner.Spanner;
import com.google.cloud.spanner.SpannerException;
import com.google.cloud.spanner.SpannerExceptionFactory;
import com.google.cloud.spanner.SpannerOptions;
import com.google.cloud.spanner.Statement;
import com.google.cloud.spanner.TransactionContext;
import com.google.spanner.admin.database.v1.CreateDatabaseMetadata;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadLocalRandom;
A continuación, agrega la siguiente inserción, métodos insertPlayers e insertScores debajo del método create()
existente y encima del método printUsageAndExit()
existente:
static void insert(DatabaseClient dbClient, String insertType) {
try {
insertType = insertType.toLowerCase();
} catch (Exception e) {
// Invalid input received, set insertType to empty string.
insertType = "";
}
if (insertType.equals("players")) {
// Insert players.
insertPlayers(dbClient);
} else if (insertType.equals("scores")) {
// Insert scores.
insertScores(dbClient);
} else {
// Invalid input.
System.out.println("Invalid value for 'type of insert'. "
+ "Specify a valid value: 'players' or 'scores'.");
System.exit(1);
}
}
static void insertPlayers(DatabaseClient dbClient) {
dbClient
.readWriteTransaction()
.run(
new TransactionCallable<Void>() {
@Override
public Void run(TransactionContext transaction) throws Exception {
// Get the number of players.
String sql = "SELECT Count(PlayerId) as PlayerCount FROM Players";
ResultSet resultSet = transaction.executeQuery(Statement.of(sql));
long numberOfPlayers = 0;
if (resultSet.next()) {
numberOfPlayers = resultSet.getLong("PlayerCount");
}
// Insert 100 player records into the Players table.
List<Statement> stmts = new ArrayList<Statement>();
long randomId;
for (int x = 1; x <= 100; x++) {
numberOfPlayers++;
randomId = (long) Math.floor(Math.random() * 9_000_000_000L) + 1_000_000_000L;
Statement statement =
Statement
.newBuilder(
"INSERT INTO Players (PlayerId, PlayerName) "
+ "VALUES (@PlayerId, @PlayerName) ")
.bind("PlayerId")
.to(randomId)
.bind("PlayerName")
.to("Player " + numberOfPlayers)
.build();
stmts.add(statement);
}
transaction.batchUpdate(stmts);
return null;
}
});
System.out.println("Done inserting player records...");
}
static void insertScores(DatabaseClient dbClient) {
boolean playerRecordsFound = false;
ResultSet resultSet =
dbClient
.singleUse()
.executeQuery(Statement.of("SELECT * FROM Players"));
while (resultSet.next()) {
playerRecordsFound = true;
final long playerId = resultSet.getLong("PlayerId");
dbClient
.readWriteTransaction()
.run(
new TransactionCallable<Void>() {
@Override
public Void run(TransactionContext transaction) throws Exception {
// Initialize objects for random Score and random Timestamp.
LocalDate endDate = LocalDate.now();
long end = endDate.toEpochDay();
int startYear = endDate.getYear() - 2;
int startMonth = endDate.getMonthValue();
int startDay = endDate.getDayOfMonth();
LocalDate startDate = LocalDate.of(startYear, startMonth, startDay);
long start = startDate.toEpochDay();
Random r = new Random();
List<Statement> stmts = new ArrayList<Statement>();
// Insert 4 score records into the Scores table
// for each player in the Players table.
for (int x = 1; x <= 4; x++) {
// Generate random score between 1,000,000 and 1,000
long randomScore = r.nextInt(1000000 - 1000) + 1000;
// Get random day within the past two years.
long randomDay = ThreadLocalRandom.current().nextLong(start, end);
LocalDate randomDayDate = LocalDate.ofEpochDay(randomDay);
LocalTime randomTime = LocalTime.of(
r.nextInt(23), r.nextInt(59), r.nextInt(59), r.nextInt(9999));
LocalDateTime randomDate = LocalDateTime.of(randomDayDate, randomTime);
Instant randomInstant = randomDate.toInstant(ZoneOffset.UTC);
Statement statement =
Statement
.newBuilder(
"INSERT INTO Scores (PlayerId, Score, Timestamp) "
+ "VALUES (@PlayerId, @Score, @Timestamp) ")
.bind("PlayerId")
.to(playerId)
.bind("Score")
.to(randomScore)
.bind("Timestamp")
.to(randomInstant.toString())
.build();
stmts.add(statement);
}
transaction.batchUpdate(stmts);
return null;
}
});
}
if (!playerRecordsFound) {
System.out.println("Parameter 'scores' is invalid since "
+ "no player records currently exist. First insert players "
+ "then insert scores.");
System.exit(1);
} else {
System.out.println("Done inserting score records...");
}
}
Luego, para que el comando insert
funcione, agrega el siguiente código al método “principal” de tu app dentro de la declaración switch (command)
:
case "insert":
String insertType;
try {
insertType = args[3];
} catch (ArrayIndexOutOfBoundsException exception) {
insertType = "";
}
insert(dbClient, insertType);
break;
Cuando termines la declaración switch (command)
, el código se verá de la siguiente manera:
switch (command) {
case "create":
create(dbAdminClient, db);
break;
case "insert":
String insertType;
try {
insertType = args[3];
} catch (ArrayIndexOutOfBoundsException exception) {
insertType = "";
}
insert(dbClient, insertType);
break;
default:
printUsageAndExit();
}
El último paso a fin de que termines de agregar la funcionalidad “insert” a tu app es agregar texto de ayuda para el comando “insert” al método printUsageAndExit()
. Agrega las siguientes líneas de código al método printUsageAndExit()
a fin de incluir texto de ayuda para el comando de inserción:
System.out.println(" java -jar leaderboard.jar insert my-instance example-db players");
System.out.println(" - Insert 100 sample Player records into the database.\n");
System.out.println(" java -jar leaderboard.jar insert my-instance example-db scores");
System.out.println(" - Insert sample score data into Scores sample Cloud Spanner "
+ "database table.\n");
Para guardar los cambios realizados en el archivo App.java
, selecciona “Guardar” en el menú “Archivo” de Cloud Shell.
Puedes usar el archivo App.java
en el directorio java-docs-samples/spanner/leaderboard/step5/src
si deseas ver un ejemplo de cómo debe verse tu archivo App.java
después de agregar el código para habilitar el comando insert
.
Ahora volveremos a compilar y ejecutar la app para confirmar que el nuevo comando insert
se incluye en la lista de comandos de la app.
Para compilar tu app, ejecuta mvn package
desde el directorio en el que se encuentra el archivo pom.xml:
mvn package
Una vez que tu archivo jar de Java se haya compilado correctamente, ejecuta el siguiente comando:
java -jar target/leaderboard.jar
Deberías ver el comando insert
ahora incluido en el resultado predeterminado de la aplicación:
Leaderboard 1.0.0 Usage: java -jar leaderboard.jar <command> <instance_id> <database_id> [command_option] Examples: java -jar leaderboard.jar create my-instance example-db - Create a sample Cloud Spanner database along with sample tables in your project. java -jar leaderboard.jar insert my-instance example-db players - Insert 100 sample Player records into the database. java -jar leaderboard.jar insert my-instance example-db scores - Insert sample score data into Scores sample Cloud Spanner database table.
Se puede ver de la respuesta que, además del ID de instancia y el ID de la base de datos, hay otro argumento que puede tener un valor “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”.
java -jar target/leaderboard.jar insert cloudspanner-leaderboard leaderboard players
Después de unos segundos, deberías ver una respuesta como la siguiente:
Done inserting player records...
Ahora usemos la biblioteca cliente de Java a fin de propagar nuestra tabla Scores
con cuatro puntuaciones aleatorias junto con marcas de tiempo para cada jugador en 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”.
java -jar target/leaderboard.jar insert cloudspanner-leaderboard leaderboard scores
Después de unos segundos, deberías ver una respuesta como la siguiente:
Done inserting score records...
La ejecución de insert
con el “tipo de inserción” especificado como scores
llama al método insertScores
, 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:
LocalDate endDate = LocalDate.now();
long end = endDate.toEpochDay();
int startYear = endDate.getYear() - 2;
int startMonth = endDate.getMonthValue();
int startDay = endDate.getDayOfMonth();
LocalDate startDate = LocalDate.of(startYear, startMonth, startDay);
long start = startDate.toEpochDay();
...
long randomDay = ThreadLocalRandom.current().nextLong(start, end);
LocalDate randomDayDate = LocalDate.ofEpochDay(randomDay);
LocalTime randomTime = LocalTime.of(
r.nextInt(23), r.nextInt(59), r.nextInt(59), r.nextInt(9999));
LocalDateTime randomDate = LocalDateTime.of(randomDayDate, randomTime);
Instant randomInstant = randomDate.toInstant(ZoneOffset.UTC);
...
.bind("Timestamp")
.to(randomInstant.toString())
Para propagar automáticamente la columna Timestamp
con la marca de tiempo exacta cuando la transacción “Insertar” se instale, puedes insertar la constante Value.COMMIT_TIMESTAMP
de Java, como en el siguiente fragmento de código:
.bind("Timestamp")
.to(Value.COMMIT_TIMESTAMP)
Ahora que se completó la carga de datos, verificaremos 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ía ver que tiene 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 nuestra app para ejecutar algunas consultas que podemos usar a fin de crear una tabla de clasificación de videojuegos.
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 nuestra aplicación para ejecutar las consultas de 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 App.java
en el editor de Cloud Shell a fin de actualizar la app para agregar un comando query
. El comando query
consta de dos métodos query
, uno que solo requiere un argumento DatabaseClient
y otro que requiere un argumento timespan
adicional para facilitar el filtrado de resultados por un período especificado en horas.
Agrega los dos métodos query
siguientes debajo del método insertScores()
existente y encima del método printUsageAndExit()
existente:
static void query(DatabaseClient dbClient) {
String scoreDate;
String score;
ResultSet resultSet =
dbClient
.singleUse()
.executeQuery(
Statement.of(
"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"));
while (resultSet.next()) {
scoreDate = String.valueOf(resultSet.getTimestamp("Timestamp"));
score = String.format("%,d", resultSet.getLong("Score"));
System.out.printf(
"PlayerId: %d PlayerName: %s Score: %s Timestamp: %s\n",
resultSet.getLong("PlayerId"), resultSet.getString("PlayerName"), score,
scoreDate.substring(0,10));
}
}
static void query(DatabaseClient dbClient, int timespan) {
String scoreDate;
String score;
Statement statement =
Statement
.newBuilder(
"SELECT p.PlayerId, p.PlayerName, s.Score, s.Timestamp "
+ "FROM Players p "
+ "JOIN Scores s ON p.PlayerId = s.PlayerId "
+ "WHERE s.Timestamp > "
+ "TIMESTAMP_SUB(CURRENT_TIMESTAMP(), "
+ " INTERVAL @Timespan HOUR) "
+ "ORDER BY s.Score DESC LIMIT 10")
.bind("Timespan")
.to(timespan)
.build();
ResultSet resultSet =
dbClient
.singleUse()
.executeQuery(statement);
while (resultSet.next()) {
scoreDate = String.valueOf(resultSet.getTimestamp("Timestamp"));
score = String.format("%,d", resultSet.getLong("Score"));
System.out.printf(
"PlayerId: %d PlayerName: %s Score: %s Timestamp: %s\n",
resultSet.getLong("PlayerId"), resultSet.getString("PlayerName"), score,
scoreDate.substring(0,10));
}
}
Luego, para que el comando query
funcione, agrega el siguiente código a la declaración switch(command)
en el método “principal” de tu app:
case "query":
if (args.length == 4) {
int timespan = 0;
try {
timespan = Integer.parseInt(args[3]);
} catch (NumberFormatException e) {
System.err.println("query command's 'timespan' parameter must be a valid integer.");
System.exit(1);
}
query(dbClient, timespan);
} else {
query(dbClient);
}
break;
El último paso a fin de que termines de agregar la funcionalidad “consulta” a tu app es agregar texto de ayuda para el comando “query” al método printUsageAndExit()
. Agrega las siguientes líneas de código al método printUsageAndExit()
a fin de incluir el texto de ayuda para el comando “query”:
System.out.println(" java -jar leaderboard.jar query my-instance example-db");
System.out.println(" - Query players with top ten scores of all time.\n");
System.out.println(" java -jar leaderboard.jar query my-instance example-db 168");
System.out.println(" - Query players with top ten scores within a timespan "
+ "specified in hours.\n");
Para guardar los cambios realizados en el archivo App.java
, selecciona “Guardar” en el menú “Archivo” de Cloud Shell.
Puedes usar el archivo App.java
en el directorio dotnet-docs-samples/applications/leaderboard/step6/src
si deseas ver un ejemplo de cómo debe verse tu archivo App.java
después de agregar el código para habilitar el comando query
.
Para compilar tu app, ejecuta mvn package
desde el directorio en el que se encuentra el archivo pom.xml:
mvn package
Ahora ejecutemos la app para confirmar que el nuevo comando query
se incluye en la lista de comandos posibles de la app. Ejecuta el siguiente comando:
java -jar target/leaderboard.jar
Ahora, deberías ver el comando query
incluido en el resultado predeterminado de la aplicación como una nueva opción de comando:
Leaderboard 1.0.0 Usage: java -jar leaderboard.jar <command> <instance_id> <database_id> [command_option] Examples: java -jar leaderboard.jar create my-instance example-db - Create a sample Cloud Spanner database along with sample tables in your project. java -jar leaderboard.jar insert my-instance example-db players - Insert 100 sample Player records into the database. java -jar leaderboard.jar insert my-instance example-db scores - Insert sample score data into Scores sample Cloud Spanner database table. java -jar leaderboard.jar query my-instance example-db - Query players with top ten scores of all time. java -jar leaderboard.jar query my-instance example-db 168 - Query players with top ten scores within a timespan specified in hours.
Como puedes ver en la respuesta, además del ID de instancia y los argumentos de ID de la base de datos, el comando query
nos permite especificar un período opcional en cantidad de horas para usar en el filtrado de registros según su valor en la columna Scores
de la tabla Timestamp
. Dado que el argumento de intervalo es opcional, significa que no se incluye un argumento de intervalo, por lo que las marcas de tiempo no se filtran los registros. 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
.
java -jar target/leaderboard.jar query 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: 4018687297 PlayerName: Player 83 Score: 999,618 Timestamp: 2017-07-01
PlayerId: 4018687297 PlayerName: Player 83 Score: 998,956 Timestamp: 2017-09-02
PlayerId: 4285713246 PlayerName: Player 51 Score: 998,648 Timestamp: 2017-12-01
PlayerId: 5267931774 PlayerName: Player 49 Score: 997,733 Timestamp: 2017-11-09
PlayerId: 1981654448 PlayerName: Player 35 Score: 997,480 Timestamp: 2018-12-06
PlayerId: 4953940705 PlayerName: Player 87 Score: 995,184 Timestamp: 2018-09-14
PlayerId: 2456736905 PlayerName: Player 84 Score: 992,881 Timestamp: 2017-04-14
PlayerId: 8234617611 PlayerName: Player 19 Score: 992,399 Timestamp: 2017-12-27
PlayerId: 1788051688 PlayerName: Player 76 Score: 992,265 Timestamp: 2018-11-22
PlayerId: 7127686505 PlayerName: Player 97 Score: 992,038 Timestamp: 2017-12-02
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.
java -jar target/leaderboard.jar query 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: 1981654448 PlayerName: Player 35 Score: 997,480 Timestamp: 2018-12-06
PlayerId: 4953940705 PlayerName: Player 87 Score: 995,184 Timestamp: 2018-09-14
PlayerId: 1788051688 PlayerName: Player 76 Score: 992,265 Timestamp: 2018-11-22
PlayerId: 6862349579 PlayerName: Player 30 Score: 990,877 Timestamp: 2018-09-14
PlayerId: 5529627211 PlayerName: Player 16 Score: 989,142 Timestamp: 2018-03-30
PlayerId: 9743904155 PlayerName: Player 1 Score: 988,765 Timestamp: 2018-05-30
PlayerId: 6809119884 PlayerName: Player 7 Score: 986,673 Timestamp: 2018-05-16
PlayerId: 2132710638 PlayerName: Player 54 Score: 983,108 Timestamp: 2018-09-11
PlayerId: 2320093590 PlayerName: Player 79 Score: 981,373 Timestamp: 2018-05-07
PlayerId: 9554181430 PlayerName: Player 80 Score: 981,087 Timestamp: 2018-06-21
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.
java -jar target/leaderboard.jar query 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: 3869829195 PlayerName: Player 69 Score: 949,686 Timestamp: 2019-02-19
PlayerId: 7448359883 PlayerName: Player 20 Score: 938,998 Timestamp: 2019-02-07
PlayerId: 1981654448 PlayerName: Player 35 Score: 929,003 Timestamp: 2019-02-22
PlayerId: 9336678658 PlayerName: Player 44 Score: 914,106 Timestamp: 2019-01-27
PlayerId: 6968576389 PlayerName: Player 40 Score: 898,041 Timestamp: 2019-02-21
PlayerId: 5529627211 PlayerName: Player 16 Score: 896,433 Timestamp: 2019-01-29
PlayerId: 9395039625 PlayerName: Player 59 Score: 879,495 Timestamp: 2019-02-09
PlayerId: 2094604854 PlayerName: Player 39 Score: 860,434 Timestamp: 2019-02-01
PlayerId: 9395039625 PlayerName: Player 59 Score: 849,955 Timestamp: 2019-02-21
PlayerId: 4285713246 PlayerName: Player 51 Score: 805,654 Timestamp: 2019-02-02
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.
java -jar target/leaderboard.jar query 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: 3869829195 PlayerName: Player 69 Score: 949,686 Timestamp: 2019-02-19
PlayerId: 1981654448 PlayerName: Player 35 Score: 929,003 Timestamp: 2019-02-22
PlayerId: 6968576389 PlayerName: Player 40 Score: 898,041 Timestamp: 2019-02-21
PlayerId: 9395039625 PlayerName: Player 59 Score: 849,955 Timestamp: 2019-02-21
PlayerId: 5954045812 PlayerName: Player 8 Score: 795,639 Timestamp: 2019-02-22
PlayerId: 3889939638 PlayerName: Player 71 Score: 775,252 Timestamp: 2019-02-21
PlayerId: 5529627211 PlayerName: Player 16 Score: 604,695 Timestamp: 2019-02-19
PlayerId: 9006728426 PlayerName: Player 3 Score: 457,208 Timestamp: 2019-02-22
PlayerId: 8289497066 PlayerName: Player 58 Score: 227,697 Timestamp: 2019-02-20
PlayerId: 8065482904 PlayerName: Player 99 Score: 198,429 Timestamp: 2019-02-24
¡Excelente trabajo!
Ahora a medida que agregues los registros, Cloud Spanner escalará tu base de datos del tamaño que necesites que sea. No importa cuánto crezca tu base de datos, la tabla de clasificación de tu juego puede escalar con exactitud con Cloud Spanner y su tecnología Truetime.
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 paso es sencillo. Ve a la sección de Cloud Spanner de Cloud Console y borra la instancia que creamos en el paso de codelab denominado “Configurar una instancia de Cloud Spanner”.
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 Java
- Cómo crear una base de datos y tablas de Spanner con la biblioteca cliente de Java
- Cómo cargar datos en una base de datos de Spanner con la biblioteca cliente de Java
- Cómo consultar los resultados de los “Diez mejores” de tus datos mediante las marcas de tiempo de confirmación de Spanner y la biblioteca cliente de Java
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