۱. مرور کلی
گوگل کلود اسپنر یک سرویس پایگاه داده رابطهای، توزیعشده در سطح جهانی و مقیاسپذیر افقی است که تراکنشهای ACID و مفاهیم SQL را بدون از دست دادن عملکرد و دسترسیپذیری بالا ارائه میدهد.
در این آزمایش، نحوه راهاندازی یک نمونه Cloud Spanner را خواهید آموخت. مراحل ایجاد یک پایگاه داده و طرحوارهای که میتواند برای جدول امتیازات بازی استفاده شود را طی خواهید کرد. با ایجاد یک جدول Players برای ذخیره اطلاعات بازیکنان و یک جدول Scores برای ذخیره امتیازات بازیکنان شروع خواهید کرد.
در مرحله بعد، جداول را با دادههای نمونه پر خواهید کرد. سپس با اجرای چند نمونه کوئری از ده نمونه برتر، این آزمایش را به پایان خواهید رساند و در نهایت نمونه را برای آزادسازی منابع حذف خواهید کرد.
آنچه یاد خواهید گرفت
- نحوه راهاندازی یک نمونه Cloud Spanner.
- نحوه ایجاد پایگاه داده و جداول.
- نحوه استفاده از ستون مهر زمانی کامیت.
- نحوه بارگذاری دادهها در جدول پایگاه داده Cloud Spanner خود با مهرهای زمانی.
- چگونه از پایگاه داده Cloud Spanner خود پرس و جو کنید.
- چگونه نمونه Cloud Spanner خود را حذف کنید.
چه چیزهایی لازم خواهی داشت؟
چگونه از این آموزش استفاده خواهید کرد؟
تجربه خود را با پلتفرم ابری گوگل چگونه ارزیابی میکنید؟
۲. تنظیمات و الزامات
تنظیم محیط خودتنظیم
اگر از قبل حساب گوگل (جیمیل یا برنامههای گوگل) ندارید، باید یکی ایجاد کنید . وارد کنسول پلتفرم ابری گوگل ( console.cloud.google.com ) شوید و یک پروژه جدید ایجاد کنید.
اگر از قبل پروژهای دارید، روی منوی کشویی انتخاب پروژه در سمت چپ بالای کنسول کلیک کنید:

و در پنجرهی باز شده روی دکمهی «پروژهی جدید» کلیک کنید تا یک پروژهی جدید ایجاد شود:

اگر از قبل پروژهای ندارید، باید پنجرهای مانند این را برای ایجاد اولین پروژه خود ببینید:

پنجرهی بعدیِ ایجاد پروژه به شما امکان میدهد جزئیات پروژهی جدید خود را وارد کنید:

شناسه پروژه را به خاطر بسپارید، که یک نام منحصر به فرد در تمام پروژههای Google Cloud است (نام بالا قبلاً گرفته شده و برای شما کار نخواهد کرد، متاسفیم!). بعداً در این آزمایشگاه کد به آن PROJECT_ID گفته خواهد شد.
در مرحله بعد، اگر قبلاً این کار را نکردهاید، برای استفاده از منابع Google Cloud و فعال کردن Cloud Spanner API ، باید صورتحساب را در کنسول توسعهدهندگان فعال کنید .

اجرای این آزمایشگاه کد نباید بیش از چند دلار برای شما هزینه داشته باشد، اما اگر تصمیم به استفاده از منابع بیشتر بگیرید یا اگر آنها را در حال اجرا رها کنید (به بخش «پاکسازی» در انتهای این سند مراجعه کنید)، میتواند بیشتر هم بشود. قیمت Google Cloud Spanner در اینجا مستند شده است.
کاربران جدید پلتفرم ابری گوگل واجد شرایط دریافت یک دوره آزمایشی رایگان ۳۰۰ دلاری هستند که این کدلب را کاملاً رایگان میکند.
راهاندازی پوسته ابری گوگل
اگرچه میتوان از راه دور و از طریق لپتاپ، گوگل کلود و اسپنر را کنترل کرد، اما در این آزمایشگاه کد، از گوگل کلود شل ، یک محیط خط فرمان که در فضای ابری اجرا میشود، استفاده خواهیم کرد.
این ماشین مجازی مبتنی بر دبیان، تمام ابزارهای توسعه مورد نیاز شما را در خود جای داده است. این ماشین مجازی یک دایرکتوری خانگی ۵ گیگابایتی دائمی ارائه میدهد و در فضای ابری گوگل اجرا میشود که عملکرد شبکه و احراز هویت را تا حد زیادی بهبود میبخشد. این بدان معناست که تنها چیزی که برای این آزمایشگاه کد نیاز دارید یک مرورگر است (بله، روی کرومبوک هم کار میکند).
- برای فعال کردن Cloud Shell از کنسول Cloud، کافیست روی Activate Cloud Shell کلیک کنید.
(فقط چند لحظه طول میکشد تا آماده شود و به محیط متصل شود).


پس از اتصال به Cloud Shell، باید ببینید که از قبل احراز هویت شدهاید و پروژه از قبل روی PROJECT_ID شما تنظیم شده است.
gcloud auth list
خروجی دستور
Credentialed accounts: - <myaccount>@<mydomain>.com (active)
gcloud config list project
خروجی دستور
[core] project = <PROJECT_ID>
اگر به هر دلیلی پروژه تنظیم نشده باشد، کافیست دستور زیر را اجرا کنید:
gcloud config set project <PROJECT_ID>
به دنبال PROJECT_ID خود هستید؟ بررسی کنید که در مراحل راهاندازی از چه شناسهای استفاده کردهاید یا آن را در داشبورد Cloud Console جستجو کنید:

Cloud Shell همچنین برخی از متغیرهای محیطی را به طور پیشفرض تنظیم میکند که ممکن است هنگام اجرای دستورات بعدی مفید باشند.
echo $GOOGLE_CLOUD_PROJECT
خروجی دستور
<PROJECT_ID>
- در نهایت، منطقه پیشفرض و پیکربندی پروژه را تنظیم کنید.
gcloud config set compute/zone us-central1-f
شما میتوانید مناطق مختلفی را انتخاب کنید. برای اطلاعات بیشتر، به بخش مناطق و نواحی مراجعه کنید.
خلاصه
در این مرحله، محیط خود را راهاندازی میکنید.
بعدی
در مرحله بعد، یک نمونه Cloud Spanner راهاندازی خواهید کرد.
۳. راهاندازی یک نمونهی Cloud Spanner
در این مرحله، نمونه Cloud Spanner خود را برای این codelab راهاندازی میکنیم. ورودی Spanner را جستجو کنید.
در منوی همبرگری بالا سمت چپ
یا با فشردن کلید "/" و تایپ "Spanner" عبارت Spanner را جستجو کنید.

در مرحله بعد، روی کلیک کنید
و فرم را با وارد کردن نام نمونه cloudspanner-leaderboard برای نمونه خود پر کنید، یک پیکربندی انتخاب کنید (یک نمونه منطقهای را انتخاب کنید) و تعداد گرهها را تنظیم کنید، برای این codelab ما فقط به ۱ گره نیاز داریم. برای نمونههای تولیدی و برای واجد شرایط بودن برای SLA Cloud Spanner، باید ۳ یا بیشتر گره را در نمونه Cloud Spanner خود اجرا کنید.
در آخر، اما نه کماهمیتتر، روی «ایجاد» کلیک کنید و ظرف چند ثانیه یک نمونه Cloud Spanner در اختیار شما قرار میگیرد.

در مرحله بعدی، قصد داریم از کتابخانه کلاینت Go برای ایجاد یک پایگاه داده و طرحواره در نمونه جدید خود استفاده کنیم.
۴. ایجاد پایگاه داده و طرحواره
در این مرحله، ما قصد داریم پایگاه داده و طرحواره نمونه خود را ایجاد کنیم.
بیایید از کتابخانه کلاینت Go برای ایجاد دو جدول استفاده کنیم؛ یک جدول Players برای اطلاعات بازیکنان و یک جدول Scores برای ذخیره امتیازات بازیکنان. برای انجام این کار، مراحل ایجاد یک برنامه کنسول Go در Cloud Shell را بررسی خواهیم کرد.
ابتدا با تایپ دستور زیر در Cloud Shell، کد نمونه این codelab را از Github کپی کنید:
export GO111MODULE=auto
go get -u github.com/GoogleCloudPlatform/golang-samples/spanner/...
سپس دایرکتوری را به دایرکتوری "leaderboard" تغییر دهید، جایی که برنامه خود را ایجاد خواهید کرد.
cd gopath/src/github.com/GoogleCloudPlatform/golang-samples/spanner/spanner_leaderboard
تمام کد مورد نیاز برای این آزمایشگاه کد در دایرکتوری golang-samples/spanner/spanner_leaderboard/ موجود به عنوان یک برنامه Go قابل اجرا با نام leaderboard قرار دارد تا در حین پیشرفت در آزمایشگاه کد به عنوان مرجع عمل کند. ما یک دایرکتوری جدید ایجاد خواهیم کرد و یک کپی از برنامه Leaderboard را به صورت مرحلهای ایجاد خواهیم کرد.
یک دایرکتوری جدید با نام "codelab" برای برنامه ایجاد کنید و با دستور زیر دایرکتوری را به آن تغییر دهید:
mkdir codelab && cd $_
حالا بیایید یک برنامهی پایهی Go با نام "Leaderboard" ایجاد کنیم که از کتابخانهی کلاینت Spanner برای ایجاد یک جدول امتیازات متشکل از دو جدول بازیکنان و امتیازات استفاده میکند. میتوانید این کار را مستقیماً در ویرایشگر Cloud Shell انجام دهید:
با کلیک روی آیکون «باز کردن ویرایشگر» که در زیر مشخص شده است، ویرایشگر Cloud Shell را باز کنید:

فایلی با نام "leaderboard.go" در پوشه ~/gopath/src/github.com/GoogleCloudPlatform/golang-samples/spanner/codelab ایجاد کنید.
- ابتدا مطمئن شوید که پوشهی «codelab» را در فهرست پوشههای ویرایشگر Cloud Shell انتخاب کردهاید.
- سپس از منوی «فایل» ویرایشگر Cloud Shell، گزینه «فایل جدید» را انتخاب کنید.
- نام فایل جدید را «leaderboard.go» قرار دهید.
این فایل اصلی برنامه است که شامل کد برنامه و ارجاعات مربوط به هرگونه وابستگی خواهد بود.
برای ایجاد پایگاه داده جدول leaderboard و جداول Players و Scores ، کد Go زیر را کپی (Ctrl + P) و در فایل leaderboard.go پیست (Ctrl + V) کنید:
package main
import (
"context"
"flag"
"fmt"
"io"
"log"
"os"
"regexp"
"time"
"cloud.google.com/go/spanner"
database "cloud.google.com/go/spanner/admin/database/apiv1"
adminpb "google.golang.org/genproto/googleapis/spanner/admin/database/v1"
)
type adminCommand func(ctx context.Context, w io.Writer, adminClient *database.DatabaseAdminClient, database string) error
func createDatabase(ctx context.Context, w io.Writer, adminClient *database.DatabaseAdminClient, db string) error {
matches := regexp.MustCompile("^(.*)/databases/(.*)$").FindStringSubmatch(db)
if matches == nil || len(matches) != 3 {
return fmt.Errorf("Invalid database id %s", db)
}
op, err := adminClient.CreateDatabase(ctx, &adminpb.CreateDatabaseRequest{
Parent: matches[1],
CreateStatement: "CREATE DATABASE `" + matches[2] + "`",
ExtraStatements: []string{
`CREATE TABLE Players(
PlayerId INT64 NOT NULL,
PlayerName STRING(2048) NOT NULL
) PRIMARY KEY(PlayerId)`,
`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`,
},
})
if err != nil {
return err
}
if _, err := op.Wait(ctx); err != nil {
return err
}
fmt.Fprintf(w, "Created database [%s]\n", db)
return nil
}
func createClients(ctx context.Context, db string) (*database.DatabaseAdminClient, *spanner.Client) {
adminClient, err := database.NewDatabaseAdminClient(ctx)
if err != nil {
log.Fatal(err)
}
dataClient, err := spanner.NewClient(ctx, db)
if err != nil {
log.Fatal(err)
}
return adminClient, dataClient
}
func run(ctx context.Context, adminClient *database.DatabaseAdminClient, dataClient *spanner.Client, w io.Writer,
cmd string, db string, timespan int) error {
// createdatabase command
if cmd == "createdatabase" {
err := createDatabase(ctx, w, adminClient, db)
if err != nil {
fmt.Fprintf(w, "%s failed with %v", cmd, err)
}
return err
}
return nil
}
func main() {
flag.Usage = func() {
fmt.Fprintf(os.Stderr, `Usage: leaderboard <command> <database_name> [command_option]
Command can be one of: createdatabase
Examples:
leaderboard createdatabase projects/my-project/instances/my-instance/databases/example-db
- Create a sample Cloud Spanner database along with sample tables in your project.
`)
}
flag.Parse()
flagCount := len(flag.Args())
if flagCount != 2 {
flag.Usage()
os.Exit(2)
}
cmd, db := flag.Arg(0), flag.Arg(1)
// Set timespan to zero, as it's not currently being used
var timespan int = 0
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute)
defer cancel()
adminClient, dataClient := createClients(ctx, db)
if err := run(ctx, adminClient, dataClient, os.Stdout, cmd, db, timespan); err != nil {
os.Exit(1)
}
}
با انتخاب «ذخیره» از منوی «فایل» ویرایشگر Cloud Shell، تغییراتی را که در فایل leaderboard.go ایجاد کردهاید، ذخیره کنید.
شما میتوانید از فایل leaderboard.go در دایرکتوری golang-samples/spanner/spanner_leaderboard استفاده کنید تا نمونهای از نحوهی عملکرد فایل leaderboard.go خود را پس از افزودن کد فعالسازی دستور createdatabase مشاهده کنید.
برای ساخت برنامه خود در Cloud Shell، دستور "go build" را از دایرکتوری codelab که فایل leaderboard.go شما در آن قرار دارد، اجرا کنید:
go build leaderboard.go
پس از ساخت موفقیتآمیز برنامه، با وارد کردن دستور زیر، برنامه حاصل را در Cloud Shell اجرا کنید:
./leaderboard
شما باید خروجی مانند زیر را ببینید:
Usage: leaderboard <command> <database_name> [command_option]
Command can be one of: createdatabase
Examples:
leaderboard createdatabase projects/my-project/instances/my-instance/databases/example-db
- Create a sample Cloud Spanner database along with sample tables in your project.
از این پاسخ میتوانیم ببینیم که این برنامهی Leaderboard است که در حال حاضر یک دستور ممکن دارد: createdatabase . میتوانیم ببینیم که آرگومان مورد انتظار دستور createdatabase رشتهای است که شامل یک شناسهی نمونه (Instance ID) و شناسهی پایگاه داده (Database ID) خاص است.
حالا دستور زیر را اجرا کنید. مطمئن شوید که my-project با شناسه پروژهای که در ابتدای این codelab ایجاد کردهاید، جایگزین میکنید.
./leaderboard createdatabase projects/my-project/instances/cloudspanner-leaderboard/databases/leaderboard
بعد از چند ثانیه باید پاسخی مانند زیر ببینید:
Created database [projects/my-project/instances/cloudspanner-leaderboard/databases/leaderboard]
در بخش نمای کلی پایگاههای داده Cloud Spanner در کنسول ابری، باید پایگاه داده و جداول جدید خود را که در منوی سمت چپ ظاهر میشوند، ببینید.

در مرحله بعدی، برنامه خود را بهروزرسانی خواهیم کرد تا برخی دادهها را در پایگاه داده جدید شما بارگذاری کند.
۵. بارگذاری دادهها
اکنون یک پایگاه داده به نام leaderboard داریم که شامل دو جدول است: Players و Scores . حال بیایید از کتابخانه کلاینت Go برای پر کردن جدول Players با بازیکنان و جدول Scores با نمرات تصادفی برای هر بازیکن استفاده کنیم.
اگر هنوز باز نشده است، ویرایشگر Cloud Shell را با کلیک روی آیکون مشخص شده در زیر باز کنید:

در مرحله بعد، فایل leaderboard.go را در ویرایشگر Cloud Shell ویرایش کنید تا دستور insertplayers را اضافه کنید که میتواند برای درج ۱۰۰ بازیکن در جدول Players استفاده شود. همچنین یک دستور insertscores اضافه خواهیم کرد که میتواند برای درج ۴ امتیاز تصادفی در جدول Scores برای هر بازیکن در جدول Players استفاده شود.
ابتدا بخش imports را در بالای فایل leaderboard.go بهروزرسانی کنید و موارد فعلی را جایگزین کنید تا پس از اتمام کار، به شکل زیر درآید:
import (
"context"
"flag"
"fmt"
"io"
"log"
"math/rand"
"os"
"regexp"
"time"
"cloud.google.com/go/spanner"
database "cloud.google.com/go/spanner/admin/database/apiv1"
"google.golang.org/api/iterator"
adminpb "google.golang.org/genproto/googleapis/spanner/admin/database/v1"
)
سپس یک نوع فرمان جدید به همراه لیستی از فرمانها در بالای فایل، درست زیر خطی که با "type adminCommand ..." شروع میشود، اضافه کنید تا پس از اتمام کار، به شکل زیر درآید:
type adminCommand func(ctx context.Context, w io.Writer, adminClient *database.DatabaseAdminClient, database string) error
type command func(ctx context.Context, w io.Writer, client *spanner.Client) error
var (
commands = map[string]command{
"insertplayers": insertPlayers,
"insertscores": insertScores,
}
)
سپس توابع insertPlayers و insertScores زیر را زیر تابع createdatabase() موجود اضافه کنید:
func insertPlayers(ctx context.Context, w io.Writer, client *spanner.Client) error {
// Get number of players to use as an incrementing value for each PlayerName to be inserted
stmt := spanner.Statement{
SQL: `SELECT Count(PlayerId) as PlayerCount FROM Players`,
}
iter := client.Single().Query(ctx, stmt)
defer iter.Stop()
row, err := iter.Next()
if err != nil {
return err
}
var numberOfPlayers int64 = 0
if err := row.Columns(&numberOfPlayers); err != nil {
return err
}
// Initialize values for random PlayerId
rand.Seed(time.Now().UnixNano())
min := 1000000000
max := 9000000000
// Insert 100 player records into the Players table
_, err = client.ReadWriteTransaction(ctx, func(ctx context.Context, txn *spanner.ReadWriteTransaction) error {
stmts := []spanner.Statement{}
for i := 1; i <= 100; i++ {
numberOfPlayers++
playerID := rand.Intn(max-min) + min
playerName := fmt.Sprintf("Player %d", numberOfPlayers)
stmts = append(stmts, spanner.Statement{
SQL: `INSERT INTO Players
(PlayerId, PlayerName)
VALUES (@playerID, @playerName)`,
Params: map[string]interface{}{
"playerID": playerID,
"playerName": playerName,
},
})
}
_, err := txn.BatchUpdate(ctx, stmts)
if err != nil {
return err
}
return nil
})
fmt.Fprintf(w, "Inserted players \n")
return nil
}
func insertScores(ctx context.Context, w io.Writer, client *spanner.Client) error {
playerRecordsFound := false
// Create slice for insert statements
stmts := []spanner.Statement{}
// Select all player records
stmt := spanner.Statement{SQL: `SELECT PlayerId FROM Players`}
iter := client.Single().Query(ctx, stmt)
defer iter.Stop()
// Insert 4 score records into the Scores table for each player in the Players table
for {
row, err := iter.Next()
if err == iterator.Done {
break
}
if err != nil {
return err
}
playerRecordsFound = true
var playerID int64
if err := row.ColumnByName("PlayerId", &playerID); err != nil {
return err
}
// Initialize values for random score and date
rand.Seed(time.Now().UnixNano())
min := 1000
max := 1000000
for i := 0; i < 4; i++ {
// Generate random score between 1,000 and 1,000,000
score := rand.Intn(max-min) + min
// Generate random day within the past two years
now := time.Now()
endDate := now.Unix()
past := now.AddDate(0, -24, 0)
startDate := past.Unix()
randomDateInSeconds := rand.Int63n(endDate-startDate) + startDate
randomDate := time.Unix(randomDateInSeconds, 0)
// Add insert statement to stmts slice
stmts = append(stmts, spanner.Statement{
SQL: `INSERT INTO Scores
(PlayerId, Score, Timestamp)
VALUES (@playerID, @score, @timestamp)`,
Params: map[string]interface{}{
"playerID": playerID,
"score": score,
"timestamp": randomDate,
},
})
}
}
if !playerRecordsFound {
fmt.Fprintln(w, "No player records currently exist. First insert players then insert scores.")
} else {
_, err := client.ReadWriteTransaction(ctx, func(ctx context.Context, txn *spanner.ReadWriteTransaction) error {
// Commit insert statements for all scores to be inserted as a single transaction
_, err := txn.BatchUpdate(ctx, stmts)
return err
})
if err != nil {
return err
}
fmt.Fprintln(w, "Inserted scores")
}
return nil
}
سپس، برای اینکه دستور insert قابل اجرا باشد، کد زیر را به تابع "run" برنامه خود، زیر دستور مدیریت createdatabase ، اضافه کنید و آن را جایگزین دستور return nil کنید:
// insert and query commands
cmdFn := commands[cmd]
if cmdFn == nil {
flag.Usage()
os.Exit(2)
}
err := cmdFn(ctx, w, dataClient)
if err != nil {
fmt.Fprintf(w, "%s failed with %v", cmd, err)
}
return err
وقتی کارتان تمام شد، تابع run باید به شکل زیر باشد:
func run(ctx context.Context, adminClient *database.DatabaseAdminClient, dataClient *spanner.Client, w io.Writer,
cmd string, db string, timespan int) error {
// createdatabase command
if cmd == "createdatabase" {
err := createDatabase(ctx, w, adminClient, db)
if err != nil {
fmt.Fprintf(w, "%s failed with %v", cmd, err)
}
return err
}
// insert and query commands
cmdFn := commands[cmd]
if cmdFn == nil {
flag.Usage()
os.Exit(2)
}
err := cmdFn(ctx, w, dataClient)
if err != nil {
fmt.Fprintf(w, "%s failed with %v", cmd, err)
}
return err
}
مرحله آخر برای تکمیل افزودن قابلیت "insert" به برنامه شما، اضافه کردن متن راهنما برای دستورات "insertplayers" و "insertscores" به تابع flag.Usage() است. متن راهنمای زیر را به تابع flag.Usage() اضافه کنید تا متن راهنما برای دستورات درج نیز اضافه شود:
دو دستور زیر را به لیست دستورات ممکن اضافه کنید:
Command can be one of: createdatabase, insertplayers, insertscores
و این متن کمکی اضافی را زیر متن کمکی دستور createdatabase اضافه کنید.
leaderboard insertplayers projects/my-project/instances/my-instance/databases/example-db
- Insert 100 sample Player records into the database.
leaderboard insertscores projects/my-project/instances/my-instance/databases/example-db
- Insert sample score data into Scores sample Cloud Spanner database table.
با انتخاب «ذخیره» از منوی «فایل» ویرایشگر Cloud Shell، تغییراتی را که در فایل leaderboard.go ایجاد کردهاید، ذخیره کنید.
شما میتوانید از فایل leaderboard.go در دایرکتوری golang-samples/spanner/spanner_leaderboard استفاده کنید تا نمونهای از نحوهی عملکرد فایل leaderboard.go خود را پس از افزودن کد فعالسازی دستورات insertplayers و insertscores مشاهده کنید.
حالا بیایید برنامه را بسازیم و اجرا کنیم تا مطمئن شویم که دستورات جدید insertplayers و insertscores در لیست دستورات ممکن برنامه قرار دارند. دستور زیر را برای ساخت برنامه اجرا کنید:
go build leaderboard.go
با وارد کردن دستور زیر، برنامهی حاصل را در پوستهی ابری اجرا کنید:
./leaderboard
اکنون باید دستورات insertplayers و insertscores را در خروجی پیشفرض برنامه مشاهده کنید:
Usage: leaderboard <command> <database_name> [command_option]
Command can be one of: createdatabase, insertplayers, insertscores
Examples:
leaderboard createdatabase projects/my-project/instances/my-instance/databases/example-db
- Create a sample Cloud Spanner database along with sample tables in your project.
leaderboard insertplayers projects/my-project/instances/my-instance/databases/example-db
- Insert 100 sample Player records into the database.
leaderboard insertscores projects/my-project/instances/my-instance/databases/example-db
- Insert sample score data into Scores sample Cloud Spanner database table.
حالا بیایید دستور insertplayers را با همان مقادیر آرگومانی که هنگام فراخوانی دستور createdatabase استفاده کردیم، اجرا کنیم. مطمئن شوید که my-project با شناسه پروژهای که در ابتدای این codelab ایجاد کردید، جایگزین میکنید.
./leaderboard insertplayers projects/my-project/instances/cloudspanner-leaderboard/databases/leaderboard
بعد از چند ثانیه باید پاسخی مانند زیر ببینید:
Inserted players
حالا بیایید از کتابخانه کلاینت Go برای پر کردن جدول Scores خود با چهار امتیاز تصادفی به همراه مهرهای زمانی برای هر بازیکن در جدول Players استفاده کنیم.
ستون Timestamp جدول Scores از طریق دستور SQL زیر که هنگام اجرای دستور create اجرا شد، به عنوان یک ستون "commit timestamp" تعریف شد:
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
به ویژگی OPTIONS(allow_commit_timestamp=true) توجه کنید. این ویژگی Timestamp به یک ستون "commit timestamp" تبدیل میکند و امکان پر کردن خودکار آن با مهر زمانی دقیق تراکنش برای عملیات INSERT و UPDATE در یک ردیف جدول مشخص را فراهم میکند.
شما همچنین میتوانید مقادیر timestamp خودتان را در ستون "commit timestamp" وارد کنید، البته تا زمانی که یک timestamp با مقداری که در گذشته است وارد کنید، که ما در این کدلب همین کار را انجام خواهیم داد.
حالا بیایید دستور insertscores را با همان مقادیر آرگومانی که هنگام فراخوانی دستور insertplayers استفاده کردیم، اجرا کنیم. مطمئن شوید که my-project با شناسه پروژهای که در ابتدای این codelab ایجاد کردید، جایگزین میکنید.
./leaderboard insertscores projects/my-project/instances/cloudspanner-leaderboard/databases/leaderboard
بعد از چند ثانیه باید پاسخی مانند زیر ببینید:
Inserted scores
اجرای تابع insertScores از قطعه کد زیر برای درج یک مهر زمانی تصادفی با تاریخ-زمانی که در گذشته رخ میدهد، استفاده میکند:
now := time.Now()
endDate := now.Unix()
past := now.AddDate(0, -24, 0)
startDate := past.Unix()
randomDateInSeconds := rand.Int63n(endDate-startDate) + startDate
randomDate := time.Unix(randomDateInSeconds, 0)
stmts = append(stmts, spanner.Statement{
SQL: `INSERT INTO Scores
(PlayerId, Score, Timestamp)
VALUES (@playerID, @score, @timestamp)`,
Params: map[string]interface{}{
"playerID": playerID,
"score": score,
"timestamp": randomDate,
},
})
برای پر کردن خودکار ستون Timestamp با مهر زمانی که دقیقاً همان زمانی است که تراکنش "Insert" انجام میشود، میتوانید ثابت spanner.CommitTimestamp را در Go مانند قطعه کد زیر وارد کنید:
...
stmts = append(stmts, spanner.Statement{
SQL: `INSERT INTO Scores
(PlayerId, Score, Timestamp)
VALUES (@playerID, @score, @timestamp)`,
Params: map[string]interface{}{
"playerID": playerID,
"score": score,
"timestamp": spanner.CommitTimestamp,
},
})
حالا که بارگذاری دادهها را تکمیل کردیم، بیایید مقادیری را که در جداول جدیدمان در بخش Cloud Spanner از Cloud Console نوشتیم، تأیید کنیم. ابتدا پایگاه داده جدول leaderboard و سپس جدول Players را انتخاب کنید. روی تب Data کلیک کنید. باید ببینید که در ستونهای PlayerId و PlayerName جدول، دادهها را دارید.

در مرحله بعد، بیایید با کلیک روی جدول Scores و انتخاب تب Data ، بررسی کنیم که آیا جدول Scores نیز داده دارد یا خیر. باید ببینید که در ستونهای PlayerId ، Timestamp و Score جدول، داده دارید.

آفرین! بیایید برنامهمان را بهروزرسانی کنیم تا چند کوئری اجرا کنیم که میتوانیم از آنها برای ایجاد جدول امتیازات بازی استفاده کنیم.
۶. جستجوهای جدول امتیازات را اجرا کنید
حالا که پایگاه داده خود را راهاندازی و اطلاعات را در جداول بارگذاری کردهایم، بیایید با استفاده از این دادهها یک جدول امتیازات ایجاد کنیم. برای انجام این کار باید به چهار سوال زیر پاسخ دهیم:
- کدام بازیکنان جزو "ده بازیکن برتر" تمام دوران هستند؟
- کدام بازیکنان جزو «ده بازیکن برتر» سال هستند؟
- کدام بازیکنان جزو «ده بازیکن برتر» ماه هستند؟
- کدام بازیکنان جزو «ده بازیکن برتر» هفته هستند؟
بیایید برنامه خود را بهروزرسانی کنیم تا کوئریهای SQL را که به این سؤالات پاسخ میدهند، اجرا کند.
ما یک دستور query و یک دستور queryWithTimespan اضافه خواهیم کرد که روشی برای اجرای queryها جهت پاسخ به سوالاتی که اطلاعات مورد نیاز برای جدول امتیازات ما را تولید میکنند، فراهم میکند.
فایل leaderboard.go را در ویرایشگر Cloud Shell ویرایش کنید تا برنامه را برای افزودن یک دستور query و یک دستور queryWithTimespan بهروزرسانی کنید. همچنین یک تابع کمکی formatWithCommas برای قالببندی امتیازات خود با کاما اضافه خواهیم کرد.
ابتدا بخش imports را در بالای فایل leaderboard.go بهروزرسانی کنید و موارد فعلی را جایگزین کنید تا پس از اتمام کار، به شکل زیر درآید:
import (
"bytes"
"context"
"flag"
"fmt"
"io"
"log"
"math/rand"
"os"
"regexp"
"strconv"
"time"
"cloud.google.com/go/spanner"
database "cloud.google.com/go/spanner/admin/database/apiv1"
"google.golang.org/api/iterator"
adminpb "google.golang.org/genproto/googleapis/spanner/admin/database/v1"
)
سپس دو تابع زیر و تابع کمکی زیر متد insertScores موجود را اضافه کنید:
func query(ctx context.Context, w io.Writer, client *spanner.Client) error {
stmt := spanner.Statement{
SQL: `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`}
iter := client.Single().Query(ctx, stmt)
defer iter.Stop()
for {
row, err := iter.Next()
if err == iterator.Done {
return nil
}
if err != nil {
return err
}
var playerID, score int64
var playerName string
var timestamp time.Time
if err := row.Columns(&playerID, &playerName, &score, ×tamp); err != nil {
return err
}
fmt.Fprintf(w, "PlayerId: %d PlayerName: %s Score: %s Timestamp: %s\n",
playerID, playerName, formatWithCommas(score), timestamp.String()[0:10])
}
}
func queryWithTimespan(ctx context.Context, w io.Writer, client *spanner.Client, timespan int) error {
stmt := spanner.Statement{
SQL: `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`,
Params: map[string]interface{}{"Timespan": timespan},
}
iter := client.Single().Query(ctx, stmt)
defer iter.Stop()
for {
row, err := iter.Next()
if err == iterator.Done {
return nil
}
if err != nil {
return err
}
var playerID, score int64
var playerName string
var timestamp time.Time
if err := row.Columns(&playerID, &playerName, &score, ×tamp); err != nil {
return err
}
fmt.Fprintf(w, "PlayerId: %d PlayerName: %s Score: %s Timestamp: %s\n",
playerID, playerName, formatWithCommas(score), timestamp.String()[0:10])
}
}
func formatWithCommas(n int64) string {
numberAsString := strconv.FormatInt(n, 10)
numberLength := len(numberAsString)
if numberLength < 4 {
return numberAsString
}
var buffer bytes.Buffer
comma := []rune(",")
bufferPosition := numberLength % 3
if (bufferPosition) > 0 {
bufferPosition = 3 - bufferPosition
}
for i := 0; i < numberLength; i++ {
if bufferPosition == 3 {
buffer.WriteRune(comma[0])
bufferPosition = 0
}
bufferPosition++
buffer.WriteByte(numberAsString[i])
}
return buffer.String()
}
سپس در بالای فایل leaderboard.go گزینه "query" را به عنوان یکی از گزینههای دستور در متغیر commands ، درست زیر گزینه " insertscores": insertScores اضافه کنید تا متغیر commands به این شکل درآید:
var (
commands = map[string]command{
"insertplayers": insertPlayers,
"insertscores": insertScores,
"query": query,
}
)
سپس "queryWithTimespan" را به عنوان یک گزینه دستوری در تابع run ، زیر بخش دستور "createdatabase" و بالای بخش مدیریت دستورات "insert and query" اضافه کنید:
// querywithtimespan command
if cmd == "querywithtimespan" {
err := queryWithTimespan(ctx, w, dataClient, timespan)
if err != nil {
fmt.Fprintf(w, "%s failed with %v", cmd, err)
}
return err
}
وقتی کارتان تمام شد، تابع run باید به شکل زیر باشد:
func run(ctx context.Context, adminClient *database.DatabaseAdminClient, dataClient *spanner.Client, w io.Writer,
cmd string, db string, timespan int) error {
// createdatabase command
if cmd == "createdatabase" {
err := createDatabase(ctx, w, adminClient, db)
if err != nil {
fmt.Fprintf(w, "%s failed with %v", cmd, err)
}
return err
}
// querywithtimespan command
if cmd == "querywithtimespan" {
if timespan == 0 {
flag.Usage()
os.Exit(2)
}
err := queryWithTimespan(ctx, w, dataClient, timespan)
if err != nil {
fmt.Fprintf(w, "%s failed with %v", cmd, err)
}
return err
}
// insert and query commands
cmdFn := commands[cmd]
if cmdFn == nil {
flag.Usage()
os.Exit(2)
}
err := cmdFn(ctx, w, dataClient)
if err != nil {
fmt.Fprintf(w, "%s failed with %v", cmd, err)
}
return err
}
سپس، برای اینکه دستور queryWithTimespan قابل اجرا باشد، بلوک کد flag.Parse() را در متد "main" برنامه خود بهروزرسانی کنید تا به شکل زیر درآید:
flag.Parse()
flagCount := len(flag.Args())
if flagCount < 2 || flagCount > 3 {
flag.Usage()
os.Exit(2)
}
cmd, db := flag.Arg(0), flag.Arg(1)
// If query timespan flag is specified, parse to int
var timespan int = 0
if flagCount == 3 {
parsedTimespan, err := strconv.Atoi(flag.Arg(2))
if err != nil {
fmt.Println(err)
os.Exit(2)
}
timespan = parsedTimespan
}
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute)
defer cancel()
adminClient, dataClient := createClients(ctx, db)
if err := run(ctx, adminClient, dataClient, os.Stdout, cmd, db, timespan); err != nil {
os.Exit(1)
}
مرحله آخر برای تکمیل افزودن قابلیت "query" به برنامه شما، اضافه کردن متن راهنما برای دستورات "query" و "querywithtimespan" به تابع flag.Usage() است. خطوط کد زیر را به تابع flag.Usage() اضافه کنید تا متن راهنما برای دستورات query اضافه شود:
دو دستور "query" را به لیست دستورات ممکن اضافه کنید:
Command can be one of: createdatabase, insertplayers, insertscores, query, querywithtimespan
و این متن کمکی اضافی را زیر متن کمکی دستور insertscores اضافه کنید.
leaderboard query projects/my-project/instances/my-instance/databases/example-db
- Query players with top ten scores of all time.
leaderboard querywithtimespan projects/my-project/instances/my-instance/databases/example-db 168
- Query players with top ten scores within a timespan specified in hours.
با انتخاب «ذخیره» از منوی «فایل» ویرایشگر Cloud Shell، تغییراتی را که در فایل leaderboard.go ایجاد کردهاید، ذخیره کنید.
شما میتوانید از فایل leaderboard.go در دایرکتوری golang-samples/spanner/spanner_leaderboard استفاده کنید تا نمونهای از نحوهی عملکرد فایل leaderboard.go خود را پس از افزودن کد فعالسازی دستورات query و querywithtimespan مشاهده کنید.
حالا بیایید برنامه را بسازیم و اجرا کنیم تا تأیید کنیم که دستورات query جدید و querywithtimespan در لیست دستورات ممکن برنامه قرار دارند.
برای ساخت برنامه، دستور زیر را در Cloud Shell اجرا کنید:
go build leaderboard.go
با وارد کردن دستور زیر، برنامهی حاصل را در پوستهی ابری اجرا کنید:
./leaderboard
اکنون باید دستورات query و querywithtimespan را به عنوان یک گزینه دستور جدید در خروجی پیشفرض برنامه مشاهده کنید:
Usage: leaderboard <command> <database_name> [command_option]
Command can be one of: createdatabase, insertplayers, insertscores, query, querywithtimespan
Examples:
leaderboard createdatabase projects/my-project/instances/my-instance/databases/example-db
- Create a sample Cloud Spanner database along with sample tables in your project.
leaderboard insertplayers projects/my-project/instances/my-instance/databases/example-db
- Insert 100 sample Player records into the database.
leaderboard insertscores projects/my-project/instances/my-instance/databases/example-db
- Insert sample score data into Scores sample Cloud Spanner database table.
leaderboard query projects/my-project/instances/my-instance/databases/example-db
- Query players with top ten scores of all time.
leaderboard querywithtimespan projects/my-project/instances/my-instance/databases/example-db 168
- Query players with top ten scores within a timespan specified in hours.
از پاسخ میتوانید ببینید که میتوانیم از دستور query برای دریافت لیستی از ده بازیکن برتر تمام دوران استفاده کنیم. همچنین میتوانیم ببینیم که دستور querywithtimespan به ما امکان میدهد یک بازه زمانی را بر حسب ساعت مشخص کنیم تا برای فیلتر کردن رکوردها بر اساس مقدار آنها در ستون Timestamp جدول Scores استفاده کنیم.
بیایید دستور query را با استفاده از همان مقادیر آرگومانی که هنگام اجرای دستور create استفاده کردیم، اجرا کنیم. مطمئن شوید که my-project با شناسه پروژهای که در ابتدای این codelab ایجاد کردید، جایگزین میکنید.
./leaderboard query projects/my-project/instances/cloudspanner-leaderboard/databases/leaderboard
شما باید پاسخی را ببینید که شامل «ده بازیکن برتر» تمام دوران مانند زیر است:
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
حالا بیایید دستور querywithtimespan را با آرگومانهای لازم اجرا کنیم تا با مشخص کردن یک "timespan" برابر با تعداد ساعات در یک سال که ۸۷۶۰ است، "ده بازیکن برتر" سال را جستجو کنیم . مطمئن شوید که my-project با شناسه پروژهای که در ابتدای این codelab ایجاد کردهاید، جایگزین میکنید.
./leaderboard querywithtimespan projects/my-project/instances/cloudspanner-leaderboard/databases/leaderboard 8760
شما باید پاسخی را ببینید که شامل «ده بازیکن برتر» سال مانند زیر باشد:
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
حالا بیایید دستور querywithtimespan را اجرا کنیم تا با مشخص کردن یک "timespan" برابر با تعداد ساعات در یک ماه که ۷۳۰ است، "ده بازیکن برتر" ماه را جستجو کنیم. مطمئن شوید که my-project با شناسه پروژهای که در ابتدای این codelab ایجاد کردهاید، جایگزین میکنید.
./leaderboard querywithtimespan projects/my-project/instances/cloudspanner-leaderboard/databases/leaderboard 730
شما باید پاسخی را ببینید که شامل «ده بازیکن برتر» ماه مانند زیر است:
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
حالا بیایید دستور querywithtimespan را اجرا کنیم تا با مشخص کردن یک "timespan" برابر با تعداد ساعات در یک هفته که ۱۶۸ است، "ده بازیکن برتر" هفته را جستجو کنیم. مطمئن شوید که my-project با شناسه پروژهای که در ابتدای این codelab ایجاد کردهاید، جایگزین میکنید.
./leaderboard querywithtimespan projects/my-project/instances/cloudspanner-leaderboard/databases/leaderboard 168
شما باید پاسخی را ببینید که شامل «ده بازیکن برتر» هفته مانند زیر است:
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
کار عالی!
حالا که رکورد اضافه میکنید، کلود اسپنر پایگاه داده شما را تا هر اندازهای که نیاز دارید، بزرگ میکند. مهم نیست پایگاه داده شما چقدر بزرگ شود، جدول امتیازات بازی شما میتواند با کلود اسپنر و فناوری Truetime آن، با دقت بیشتری بزرگ شود.
۷. پاکسازی
بعد از کلی بازی سرگرمکننده با Spanner، باید زمین بازیمان را تمیز کنیم و در منابع و پول ارزشمندمان صرفهجویی کنیم. خوشبختانه این مرحله آسانی است، فقط کافی است به بخش Cloud Spanner در Cloud Console بروید و نمونهای را که در مرحله codelab با عنوان «راهاندازی یک نمونه Cloud Spanner» ایجاد کردیم، حذف کنید.
۸. تبریک میگویم!
آنچه ما پوشش دادهایم:
- نمونههای گوگل کلود اسپنر، پایگاههای داده و طرح جدول برای کسب رتبه برتر
- نحوه ایجاد برنامه کنسول Go
- نحوه ایجاد پایگاه داده و جداول Spanner با استفاده از کتابخانه کلاینت Go
- نحوه بارگذاری دادهها در پایگاه داده Spanner با استفاده از کتابخانه کلاینت Go
- چگونه با استفاده از مهرهای زمانی کامیت Spanner و کتابخانه کلاینت Go، نتایج «ده مورد برتر» را از دادههای خود پرسوجو کنیم؟
مراحل بعدی:
- گزارش رسمی Spanner CAP را بخوانید.
- آشنایی با طراحی Schema و بهترین شیوههای پرسوجو
- درباره مهرهای زمانی کامیت Cloud Spanner بیشتر بدانید
نظرات خود را با ما در میان بگذارید
- لطفا کمی وقت بگذارید و نظرسنجی بسیار کوتاه ما را تکمیل کنید.