1. Omówienie
Google Cloud Spanner to w pełni zarządzana, skalowalna w poziomie, rozproszona globalnie, relacyjna usługa baz danych, która zapewnia transakcje ACID i semantykę SQL bez utraty wydajności i wysokiej dostępności.
W tym module dowiesz się, jak skonfigurować instancję Cloud Spanner. Wykonasz kolejne kroki tworzenia bazy danych i schematu, które będą służyć do tworzenia tabeli wyników w grach. Na początek utwórz tabelę zawodników, w której będą przechowywane informacje o graczach, oraz tabelę wyników, by zapisać wyniki.
Następnie wypełnij tabele przykładowymi danymi. Następnie na koniec modułu uruchomisz kilka przykładowych zapytań o 10 najczęstszych pytań i usuniesz instancję, aby zwolnić zasoby.
Czego się nauczysz
- Jak skonfigurować instancję Cloud Spanner.
- Jak utworzyć bazę danych i tabele.
- Jak używać kolumny z sygnaturą czasową zatwierdzenia.
- Jak wczytywać dane do tabeli bazy danych Cloud Spanner z sygnaturami czasowymi.
- Jak wysyłać zapytania do bazy danych Cloud Spanner.
- Jak usunąć instancję Cloud Spanner.
Czego potrzebujesz
Jak wykorzystasz ten samouczek?
Jak oceniasz swoje doświadczenia z Google Cloud Platform?
2. Konfiguracja i wymagania
Samodzielne konfigurowanie środowiska
Jeśli nie masz jeszcze konta Google (w Gmailu lub Google Apps), musisz je utworzyć. Zaloguj się w konsoli Google Cloud Platform ( console.cloud.google.com) i utwórz nowy projekt.
Jeśli masz już projekt, kliknij menu wyboru projektu w lewym górnym rogu konsoli:
i kliknij „NOWY PROJEKT”. w wyświetlonym oknie, aby utworzyć nowy projekt:
Jeśli nie masz jeszcze projektu, zobaczysz takie okno dialogowe umożliwiające utworzenie pierwszego:
W kolejnym oknie tworzenia projektu możesz wpisać szczegóły nowego projektu:
Zapamiętaj identyfikator projektu, który jest niepowtarzalną nazwą we wszystkich projektach Google Cloud (powyższa nazwa jest już zajęta i nie będzie Ci odpowiadać). W dalszej części tego ćwiczenia w programie będzie ona określana jako PROJECT_ID
.
Następnie musisz włączyć płatności w Developers Console, aby korzystać z zasobów Google Cloud i włączyć interfejs Cloud Spanner API.
Ukończenie tego ćwiczenia w Codelabs nie powinno kosztować więcej niż kilka dolarów, ale może być droższe, jeśli zdecydujesz się użyć więcej zasobów lub nie chcesz ich uruchamiać (patrz sekcja „Czyszczenie” na końcu tego dokumentu). Cennik Google Cloud Spanner znajdziesz tutaj.
Nowi użytkownicy Google Cloud Platform mogą skorzystać z bezpłatnego okresu próbnego w wysokości 300 USD, dzięki czemu te ćwiczenia z programowania są całkowicie bezpłatne.
Konfiguracja Google Cloud Shell
Usługi Google Cloud i Spanner można obsługiwać zdalnie z poziomu laptopa, ale w ramach tego ćwiczenia w programowaniu użyjemy Google Cloud Shell – środowiska wiersza poleceń działającego w chmurze.
Ta maszyna wirtualna oparta na Debianie zawiera wszystkie potrzebne narzędzia dla programistów. Zawiera stały katalog domowy o pojemności 5 GB i działa w Google Cloud, co znacznie zwiększa wydajność sieci i uwierzytelnianie. Oznacza to, że do tego ćwiczenia z programowania wystarczy przeglądarka (tak, działa ona na Chromebooku).
- Aby aktywować Cloud Shell z poziomu konsoli Cloud, kliknij Aktywuj Cloud Shell (udostępnienie środowiska i połączenie z nim powinno zająć tylko chwilę).
Po nawiązaniu połączenia z Cloud Shell powinno pojawić się potwierdzenie, że użytkownik jest już uwierzytelniony, a projekt jest już ustawiony na PROJECT_ID
.
gcloud auth list
Dane wyjściowe polecenia
Credentialed accounts: - <myaccount>@<mydomain>.com (active)
gcloud config list project
Dane wyjściowe polecenia
[core] project = <PROJECT_ID>
Jeśli z jakiegoś powodu projekt nie jest skonfigurowany, uruchom po prostu to polecenie:
gcloud config set project <PROJECT_ID>
Szukasz urządzenia PROJECT_ID
? Sprawdź identyfikator użyty w krokach konfiguracji lub wyszukaj go w panelu Cloud Console:
Cloud Shell ustawia też domyślnie niektóre zmienne środowiskowe, które mogą być przydatne podczas uruchamiania kolejnych poleceń.
echo $GOOGLE_CLOUD_PROJECT
Dane wyjściowe polecenia
<PROJECT_ID>
- Na koniec ustaw domyślną strefę i konfigurację projektu.
gcloud config set compute/zone us-central1-f
Możesz wybrać różne strefy. Więcej informacji znajdziesz w artykule Regiony i Strefy.
Podsumowanie
W tym kroku skonfigurujesz środowisko.
Dalsze czynności
Następnie skonfigurujesz instancję Cloud Spanner.
3. Konfigurowanie instancji Cloud Spanner
W tym kroku skonfigurujemy instancję Cloud Spanner na potrzeby tego ćwiczenia w Codelabs. Wyszukaj pozycję Spannera w menu Hamburger po lewej stronie lub wyszukaj usługę Spanner, naciskając „/” i wpisz „Spanner”
Następnie kliknij i wypełnij formularz, wpisując nazwę instancji cloudspanner-leaderboard dla swojej instancji, wybierając konfigurację (wybierz instancję regionalną) i ustaw liczbę węzłów. Do tego ćwiczenia w Codelabs potrzebujemy tylko 1 węzła. Aby korzystać z instancji produkcyjnych i kwalifikować się do gwarancji jakości usług Cloud Spanner, w instancji Cloud Spanner musisz mieć co najmniej 3 węzły.
Kliknij „Utwórz”. a w ciągu kilku sekund będziesz mieć do dyspozycji instancję Cloud Spanner.
W następnym kroku użyjemy biblioteki klienta w języku Java do utworzenia bazy danych i schematu w naszej nowej instancji.
4. Tworzenie bazy danych i schematu
W tym kroku utworzymy naszą przykładową bazę danych i schemat.
Utwórzmy dwie tabele za pomocą biblioteki klienta w Javie: tabela zawodników z informacjami o graczach i tabela wyników, w której są przechowywane ich wyniki. Przeprowadzimy Cię przez proces tworzenia w Cloud Shell aplikacji konsoli Java.
Najpierw skopiuj z GitHub przykładowy kod tego ćwiczenia z programowania, wpisując w Cloud Shell to polecenie:
git clone https://github.com/GoogleCloudPlatform/java-docs-samples.git
Następnie zmień katalog na „applications”. w którym utworzysz aplikację.
cd java-docs-samples/spanner/leaderboard
Cały kod wymagany w tym ćwiczeniu w Codelabs znajduje się w istniejącym katalogu java-docs-samples/spanner/leaderboard/complete
jako uruchamiana aplikacja w języku C# o nazwie Leaderboard
, która będzie służyć jako wzorzec. Utworzymy nowy katalog i etapami utworzymy kopię aplikacji Tabela wyników.
Utwórz nowy katalog o nazwie „codelab” dla aplikacji i zmień na nią katalog za pomocą tego polecenia:
mkdir codelab && cd $_
Utwórz nową podstawową aplikację w Javie o nazwie „Tabela wyników”. za pomocą tego polecenia Maven (mvn):
mvn -B archetype:generate -DarchetypeGroupId=org.apache.maven.archetypes -DgroupId=com.google.codelabs -DartifactId=leaderboard -DarchetypeVersion=1.4
To polecenie tworzy prostą aplikację konsolową składającą się z 2 plików głównych – pliku konfiguracji aplikacji Maven pom.xml
i pliku aplikacji Java App.java
.
Następnie zmień katalog na utworzony właśnie katalog tabeli wyników i wymień jego zawartość:
cd leaderboard && ls
Plik pom.xml
i katalog src
powinny wyświetlić się na liście:
pom.xml src
Teraz zaktualizujmy aplikację konsoli, edytując obiekt App.java
, aby użyć biblioteki klienta Java Spanner do utworzenia tabeli wyników złożonej z 2 tabel. Gracze i wyniki. Możesz to zrobić w edytorze Cloud Shell:
Otwórz edytor Cloud Shell, klikając podświetloną ikonę:
Otwórz plik pom.xml w folderze tabeli wyników. Otwórz plik pom.xml, który znajduje się w folderze java-docs-samples\ spanner\leaderboard\codelab\leaderboard
.Ten plik konfiguruje system kompilacji Maven do kompilowania naszej aplikacji w pliku jar z uwzględnieniem wszystkich zależności.
Dodaj następującą 1 nową sekcję zarządzania zależnościami tuż pod istniejącymi elementami </properties> element:
<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>
Dodaj też 1 nową zależność w istniejących <zależności> , co spowoduje dodanie do aplikacji biblioteki klienta Cloud Spanner w języku Java.
<dependency>
<!-- Version auto-managed by BOM -->
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-spanner</artifactId>
</dependency>
Następnie zastąp istniejący tag <build> w pliku pom.xml
. z następującym tagiem <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>
Zapisz zmiany wprowadzone w pliku pom.xml, wybierając „Zapisz” w menu „Plik” edytora Cloud Shell lub naciskając klawisz „Ctrl” i „S” klawiszy na klawiaturze.
Następnie otwórz plik App.java
w edytorze Cloud Shell w folderze src/main/java/com/google/codelabs/
. Zastąp istniejący kod pliku kodem wymaganym do utworzenia bazy danych leaderboard
oraz tabel Players
i Scores
. W tym celu wklej w pliku App.java
ten kod 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");
}
}
Aby zapisać zmiany wprowadzone w pliku App.java
, kliknij „Zapisz” w menu „Plik” edytora Cloud Shell .
Aby zobaczyć przykładowy wygląd pliku App.java
po dodaniu kodu włączającego polecenie create
, możesz użyć pliku App.java
z katalogu java-docs-samples/spanner/leaderboard/step4/src
.
Aby skompilować aplikację, uruchom pakiet mvn z katalogu, w którym znajduje się pom.xml
:
mvn package
Po skompilowaniu pliku jar w Javie uruchom powstałą w ten sposób aplikację w Cloud Shell, wpisując to polecenie:
java -jar target/leaderboard.jar
Zostaną wyświetlone dane wyjściowe podobne do tych:
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.
Na podstawie tej odpowiedzi widzimy, że jest to aplikacja Leaderboard
, która obecnie ma 1 możliwe polecenie: create
. Jak widać, oczekiwane argumenty polecenia create
to identyfikator instancji i identyfikator bazy danych.
Teraz uruchom następujące polecenie.
java -jar target/leaderboard.jar create cloudspanner-leaderboard leaderboard
Po kilku sekundach powinna pojawić się taka odpowiedź:
Created database [projects/your-project/instances/cloudspanner-leaderboard/databases/leaderboard]
W sekcji Cloud Spanner konsoli Cloud powinna pojawić się nowa baza danych i tabele w menu po lewej stronie.
W następnym kroku zaktualizujemy naszą aplikację, aby wczytywała część danych do Twojej nowej bazy danych.
5. Wczytaj dane
Mamy teraz bazę danych o nazwie leaderboard
, która zawiera 2 tabele; Players
i Scores
. Teraz za pomocą biblioteki klienta w Javie zapełnimy tabelę Players
graczami, a tabelę Scores
losowymi wynikami każdego z nich.
Jeśli edytor Cloud Shell nie jest jeszcze otwarty, otwórz go, klikając podświetloną ikonę:
Następnie zmodyfikuj plik App.java
w edytorze Cloud Shell, dodając polecenie insert
, które umożliwia wstawienie 100 odtwarzaczy do tabeli Players
lub pozwala wstawić 4 losowe wyniki w tabeli Scores
każdego gracza w tabeli Players
.
Najpierw zaktualizuj sekcję imports
u góry pliku aplikacji, zastępując jej obecną zawartość. Gdy to zrobisz, powinna wyglądać mniej więcej tak:
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;
Następnie dodaj te metody insert, insertPlayers i insertScores pod istniejącą metodą create()
oraz nad istniejącą metodą printUsageAndExit()
:
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...");
}
}
Aby polecenie insert
działało, dodaj ten kod do sekcji „main” aplikacji metoda w instrukcji switch (command)
:
case "insert":
String insertType;
try {
insertType = args[3];
} catch (ArrayIndexOutOfBoundsException exception) {
insertType = "";
}
insert(dbClient, insertType);
break;
Gdy skończysz, instrukcja switch (command)
powinna wyglądać tak:
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();
}
Ostatni krok potrzebny do dodania funkcji „insert” do Twojej aplikacji jest dodanie tekstu pomocy dla polecenia „insert” do metody printUsageAndExit()
. Dodaj do metody printUsageAndExit()
te wiersze kodu, aby dołączyć tekst pomocy do polecenia wstawiania:
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");
Aby zapisać zmiany wprowadzone w pliku App.java
, kliknij „Zapisz” w menu „Plik” edytora Cloud Shell .
Aby zobaczyć przykładowy wygląd pliku App.java
po dodaniu kodu włączającego polecenie insert
, możesz użyć pliku App.java
z katalogu java-docs-samples/spanner/leaderboard/step5/src
.
Teraz ponownie skompiluj i uruchommy aplikację, aby sprawdzić, czy nowe polecenie insert
znajduje się na liście możliwych poleceń aplikacji.
Aby skompilować aplikację, uruchom mvn package
z katalogu, w którym znajduje się plik pom.xml:
mvn package
Po skompilowaniu pliku jar w Javie uruchom następujące polecenie:
java -jar target/leaderboard.jar
Polecenie insert
powinno pojawić się w domyślnych danych wyjściowych aplikacji:
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.
Z odpowiedzi wynika, że oprócz identyfikatora instancji i identyfikatora bazy danych istnieje jeszcze inny argument, który może mieć wartość „odtwarzacze” czyli „wyniki”.
Teraz uruchom polecenie insert
z tymi samymi wartościami argumentów, które użyliśmy przy wywołaniu polecenia create
, dodając „players” jako dodatkowy „typ wstawiania”, .
java -jar target/leaderboard.jar insert cloudspanner-leaderboard leaderboard players
Po kilku sekundach powinna pojawić się taka odpowiedź:
Done inserting player records...
Teraz za pomocą biblioteki klienta w języku Java uzupełnimy tabelę Scores
4 losowymi wynikami wraz z sygnaturami czasowymi każdego gracza w tabeli Players
.
Kolumna Timestamp
w tabeli Scores
jest zdefiniowana jako „sygnatura czasowa zatwierdzenia” za pomocą tej instrukcji SQL, która została wykonana podczas wcześniejszego uruchamiania polecenia create
:
CREATE TABLE Scores(
PlayerId INT64 NOT NULL,
Score INT64 NOT NULL,
Timestamp TIMESTAMP NOT NULL OPTIONS(allow_commit_timestamp=true)
) PRIMARY KEY(PlayerId, Timestamp),
INTERLEAVE IN PARENT Players ON DELETE NO ACTION
Zwróć uwagę na atrybut OPTIONS(allow_commit_timestamp=true)
. Powoduje to, że plik Timestamp
jest „sygnaturą czasową zatwierdzenia” i umożliwia jego automatyczne wypełnianie dokładną sygnaturą czasową transakcji w przypadku operacji INSERT i UPDATE w danym wierszu tabeli.
W polu „sygnatura czasowa zatwierdzenia” możesz też wstawić własne wartości sygnatury czasowej tak długo, jak długo wstawisz sygnaturę czasową z wartością z przeszłości, co zrobimy na potrzeby tego ćwiczenia z programowania.
Teraz uruchom polecenie insert
z tymi samymi wartościami argumentów, które użyliśmy przy wywołaniu polecenia create
, dodając „wyniki”. jako dodatkowy „typ wstawiania”, .
java -jar target/leaderboard.jar insert cloudspanner-leaderboard leaderboard scores
Po kilku sekundach powinna pojawić się taka odpowiedź:
Done inserting score records...
Uruchomiono insert
z typem wstawiania określony jako scores
wywołuje metodę insertScores
, która używa tych fragmentów kodu, aby wstawić losowo wygenerowaną sygnaturę czasową z datą i godziną przypadającą w przeszłości:
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())
Aby automatycznie wypełniać kolumnę Timestamp
sygnaturą czasową, w której znajduje się pole „Wstaw” zamiast transakcji, możesz zamiast tego wstawić stałą Java Value.COMMIT_TIMESTAMP
jak w tym fragmencie kodu:
.bind("Timestamp")
.to(Value.COMMIT_TIMESTAMP)
Po zakończeniu wczytywania danych sprawdźmy wartości, które właśnie zapisaliśmy w nowych tabelach. Najpierw wybierz bazę danych leaderboard
, a potem tabelę Players
. Kliknij kartę Data
. Powinny być widoczne dane w kolumnach PlayerId
i PlayerName
tabeli.
Następnie sprawdźmy, czy tabela wyników zawiera też dane, klikając tabelę Scores
i wybierając kartę Data
. Powinny być widoczne dane w kolumnach PlayerId
, Timestamp
i Score
tabeli.
Brawo! Zaktualizujmy aplikację, aby uruchomić kilka zapytań, które pozwolą nam utworzyć tabelę wyników w grach.
6. Uruchamianie zapytań dotyczących tabel wyników
Po skonfigurowaniu bazy danych i wczytaniu informacji do tabel utwórzmy długi baner przy użyciu tych danych. W tym celu musimy odpowiedzieć na następujące 4 pytania:
- Którzy gracze są w „największej dziesiątce” wszech czasów?
- Którzy gracze są w „największej dziesiątce” roku?
- Którzy gracze są w „największej dziesiątce” dzień miesiąca?
- Którzy gracze są w „największej dziesiątce” dnia tygodnia?
Zaktualizujmy aplikację, aby uruchomić zapytania SQL, które pozwolą odpowiedzieć na te pytania.
Dodamy polecenie query
, które umożliwi uruchamianie zapytań w celu uzyskania informacji wymaganych na potrzeby tablicy wyników.
Edytuj plik App.java
w edytorze Cloud Shell, aby zaktualizować aplikację przez dodanie polecenia query
. Polecenie query
składa się z 2 metod query
. Jedna z nich wymaga tylko argumentu DatabaseClient
, a druga używa dodatkowego argumentu timespan
, aby ułatwić filtrowanie wyników według przedziału czasu określonego w godzinach.
Dodaj dwie metody query
pod istniejącą metodą insertScores()
i nad istniejącą metodą printUsageAndExit()
:
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));
}
}
Aby polecenie query
działało, dodaj ten kod do instrukcji switch(command)
w sekcji „main” aplikacji :
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;
Ostatni krok potrzebny do dodania „zapytania” funkcji w aplikacji jest dodanie tekstu pomocy do zapytania do metody printUsageAndExit()
. Dodaj następujące wiersze kodu do metody printUsageAndExit()
, aby dodać tekst pomocy do „zapytania” polecenie:
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");
Aby zapisać zmiany wprowadzone w pliku App.java
, kliknij „Zapisz” w menu „Plik” edytora Cloud Shell .
Aby zobaczyć przykładowy wygląd pliku App.java
po dodaniu kodu włączającego polecenie query
, możesz użyć pliku App.java
z katalogu dotnet-docs-samples/applications/leaderboard/step6/src
.
Aby skompilować aplikację, uruchom mvn package
z katalogu, w którym znajduje się plik pom.xml:
mvn package
Teraz uruchom aplikację, aby sprawdzić, czy nowe polecenie query
znajduje się na liście możliwych poleceń aplikacji. Uruchom to polecenie:
java -jar target/leaderboard.jar
Polecenie query
powinno być widoczne w domyślnych danych wyjściowych aplikacji jako nowa opcja polecenia:
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.
Z odpowiedzi możesz zauważyć, że oprócz argumentów Identyfikator instancji i Identyfikator bazy danych polecenie query
pozwala nam określić opcjonalny przedział czasu w godzinach, który będzie używany do filtrowania rekordów na podstawie ich wartości w kolumnie Timestamp
tabeli Scores
. Ponieważ argument zakresu czasu w opcji opcjonalny oznacza, że jeśli ten argument nie zostanie uwzględniony, żadne rekordy nie będą filtrowane według sygnatur czasowych. Możemy więc użyć polecenia query
bez atrybutu „okres” aby otrzymać listę 10 najlepszych graczy wszechczasów.
Uruchommy polecenie query
bez określania zakresu czasu, używając tych samych wartości argumentów, które zostały użyte podczas uruchamiania polecenia create
.
java -jar target/leaderboard.jar query cloudspanner-leaderboard leaderboard
Powinna pojawić się odpowiedź zawierająca pozycję „Dziesięć najlepszych” graczy wszech czasów jak poniżej:
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
Teraz uruchom polecenie query
z niezbędnymi argumentami, aby wysłać zapytanie do listy „10 najwyższych wyników”. graczy w danym roku, określając przedział czasu równy liczbie godzin w roku, czyli 8760.
java -jar target/leaderboard.jar query cloudspanner-leaderboard leaderboard 8760
Powinna pojawić się odpowiedź zawierająca pozycję „Dziesięć najlepszych” graczy roku, na przykład:
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
Teraz uruchommy polecenie query
, aby wysłać zapytanie do listy „10 największych” graczy w miesiącu, określając „timespan” równa się liczbie godzin w miesiącu, która wynosi 730.
java -jar target/leaderboard.jar query cloudspanner-leaderboard leaderboard 730
Powinna pojawić się odpowiedź zawierająca pozycję „Dziesięć najlepszych” graczy w miesiącu na przykład tak:
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
Teraz uruchommy polecenie query
, aby wysłać zapytanie do listy „10 największych” graczy tygodnia poprzez określenie „czasu trwania”, co tydzień, czyli 168 godzin.
java -jar target/leaderboard.jar query cloudspanner-leaderboard leaderboard 168
Powinna pojawić się odpowiedź zawierająca pozycję „Dziesięć najlepszych” graczy tygodnia na przykład poniżej:
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
Doskonale!
Teraz podczas dodawania rekordów Cloud Spanner przeskaluje bazę danych do potrzebnego rozmiaru. Bez względu na to, jak bardzo Twoja baza danych się rozrasta, tablica wyników Twojej gry może być nadal skalowalna z dokładnością dzięki usłudze Cloud Spanner i technologii Truetime.
7. Czyszczenie
Po całej zabawie ze Spannerem musimy też posprzątać nasz plac zabaw, by zaoszczędzić cenne zasoby i pieniądze. Na szczęście to łatwy krok. Wystarczy przejść do sekcji Cloud Spanner w konsoli Cloud i usunąć instancję, którą utworzyliśmy w kroku w Codelabs o nazwie „Skonfiguruj instancję Cloud Spanner”.
8. Gratulacje!
Omówione zagadnienia:
- Schemat instancji, baz danych i tabel Google Cloud Spanner do tablicy wyników
- Jak utworzyć aplikację konsoli Java
- Jak utworzyć bazę danych i tabele Spannera przy użyciu biblioteki klienta w Javie
- Jak wczytać dane do bazy danych Spannera za pomocą biblioteki klienta w Javie
- Jak wysłać zapytanie dotyczące listy „Top 10” na podstawie Twoich danych za pomocą sygnatur czasowych zatwierdzenia usługi Spanner i biblioteki klienta w języku Java
Dalsze kroki:
- Przeczytaj dokument na temat platformy Sppanner CAP (w języku angielskim).
- Poznaj sprawdzone metody dotyczące projektowania schematu i zapytań
- Więcej informacji o sygnaturach czasowych zatwierdzenia w Cloud Spanner
Prześlij nam swoją opinię
- Poświęć chwilę na wypełnienie naszej bardzo krótkiej ankiety