1. نظرة عامة
Google Cloud Spanner هي خدمة قواعد بيانات ارتباطية مُدارة بالكامل وقابلة للتوسّع أفقيًا وموزّعة على مستوى العالم، وتوفّر معاملات متوافقة مع ACID ودلالات SQL بدون التنازل عن الأداء العالي ومدى التوفّر العالي.
في هذا الدرس التطبيقي، ستتعرّف على كيفية إعداد مثيل Cloud Spanner. ستتّبع خطوات إنشاء قاعدة بيانات ومخطط يمكن استخدامهما في قائمة الصدارة الخاصة بالألعاب. ستبدأ بإنشاء جدول "اللاعبون" لتخزين معلومات اللاعبين وجدول "النتائج" لتخزين نتائج اللاعبين.
بعد ذلك، ستملأ الجداول ببيانات نموذجية. بعد ذلك، ستنهي الدرس التطبيقي من خلال تنفيذ بعض نماذج طلبات البحث العشرة الأوائل، ثم حذف الجهاز الظاهري لإتاحة الموارد.
ما ستتعلمه
- كيفية إعداد مثيل Cloud Spanner
- كيفية إنشاء قاعدة بيانات وجداول
- كيفية استخدام عمود الطابع الزمني لعملية الإيداع
- كيفية تحميل البيانات إلى جدول قاعدة بيانات Cloud Spanner باستخدام الطوابع الزمنية
- كيفية طلب البحث في قاعدة بيانات Cloud Spanner
- كيفية حذف مثيل Cloud Spanner
المتطلبات
كيف ستستخدم هذا البرنامج التعليمي؟
ما هو تقييمك لتجربة استخدام Google Cloud Platform؟
2. الإعداد والمتطلبات
إعداد البيئة بالسرعة التي تناسبك
إذا لم يكن لديك حساب Google (Gmail أو Google Apps)، عليك إنشاء حساب. سجِّل الدخول إلى "وحدة تحكّم Google Cloud Platform" (console.cloud.google.com) وأنشِئ مشروعًا جديدًا.
إذا كان لديك مشروع حالي، انقر على القائمة المنسدلة لاختيار المشروع في أعلى يمين وحدة التحكّم:

وانقر على الزر "مشروع جديد" في مربّع الحوار الناتج لإنشاء مشروع جديد:

إذا لم يكن لديك مشروع، من المفترض أن يظهر لك مربّع حوار مشابه لما يلي لإنشاء مشروعك الأول:

يتيح لك مربّع حوار إنشاء المشروع اللاحق إدخال تفاصيل مشروعك الجديد:

تذكَّر رقم تعريف المشروع، وهو اسم فريد في جميع مشاريع Google Cloud (الاسم أعلاه مستخدَم حاليًا ولن يكون متاحًا لك، نأسف لذلك). سيتم الإشارة إليه لاحقًا في هذا الدرس العملي باسم PROJECT_ID.
بعد ذلك، إذا لم يسبق لك إجراء ذلك، عليك تفعيل الفوترة في Developers Console من أجل استخدام موارد Google Cloud وتفعيل Cloud Spanner API.

لن تكلفك تجربة هذا الدرس التطبيقي حول الترميز أكثر من بضعة دولارات، ولكن قد تكون التكلفة أعلى إذا قررت استخدام المزيد من الموارد أو إذا تركتها قيد التشغيل (راجِع قسم "التنظيف" في نهاية هذا المستند). يمكنك الاطّلاع على مستندات أسعار Google Cloud Spanner هنا.
يمكن لمستخدمي Google Cloud Platform الجدد الاستفادة من فترة تجريبية مجانية بقيمة 300 دولار أمريكي، ما يجعل هذا الدرس العملي مجانيًا تمامًا.
إعداد Google Cloud Shell
على الرغم من إمكانية تشغيل Google Cloud وSpanner عن بُعد من الكمبيوتر المحمول، سنستخدم في هذا الدرس التطبيقي حول الترميز Google Cloud Shell، وهي بيئة سطر أوامر تعمل في السحابة الإلكترونية.
يتم تحميل هذا الجهاز الافتراضي المستند إلى Debian بجميع أدوات التطوير التي تحتاج إليها. توفّر هذه الخدمة دليلًا رئيسيًا دائمًا بسعة 5 غيغابايت وتعمل في Google Cloud، ما يؤدي إلى تحسين أداء الشبكة والمصادقة بشكل كبير. وهذا يعني أنّ كل ما تحتاجه لهذا الدرس التطبيقي حول الترميز هو متصفّح (نعم، يمكن استخدامه على جهاز Chromebook).
- لتفعيل Cloud Shell من Cloud Console، ما عليك سوى النقر على تفعيل 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.
3- إعداد مثيل Cloud Spanner
في هذه الخطوة، سنُعدّ مثيل Cloud Spanner لهذا الدرس العملي. ابحث عن إدخال Spanner
في قائمة الهمبرغر في أعلى يمين الصفحة
أو ابحث عن Spanner بالضغط على "/" وكتابة "Spanner".

بعد ذلك، انقر على
واملأ النموذج عن طريق إدخال اسم المثيل cloudspanner-leaderboard للمثيل، واختيار إعداد (اختر مثيلاً إقليميًا)، واضبط عدد العُقد، ولن نحتاج في هذا الدرس التطبيقي حول الترميز إلا إلى عقدة واحدة. بالنسبة إلى الآلات الافتراضية المخصّصة للإنتاج وللتأهّل لاتفاقية مستوى الخدمة في Cloud Spanner، عليك تشغيل 3 عُقد أو أكثر في آلة Cloud Spanner الافتراضية.
أخيرًا، انقر على "إنشاء" وستصبح لديك في غضون ثوانٍ مثيل Cloud Spanner تحت تصرّفك.

في الخطوة التالية، سنستخدم مكتبة برامج C# لإنشاء قاعدة بيانات ومخطط في مثيلنا الجديد.
4. إنشاء قاعدة بيانات ومخطط
في هذه الخطوة، سننشئ قاعدة البيانات والنموذج الخاصين بنا.
لنستخدِم مكتبة برامج C# لإنشاء جدولَين: جدول "اللاعبون" الذي يتضمّن معلومات اللاعبين وجدول "النتائج" الذي يخزّن نتائج اللاعبين. لإجراء ذلك، سنوضّح خطوات إنشاء تطبيق وحدة تحكّم C# في Cloud Shell.
أولاً، استنسِخ الرمز النموذجي لهذا الدرس التطبيقي حول الترميز من GitHub عن طريق كتابة الأمر التالي في Cloud Shell:
git clone https://github.com/GoogleCloudPlatform/dotnet-docs-samples.git
بعد ذلك، غيِّر الدليل إلى دليل "التطبيقات" (applications) الذي ستنشئ فيه تطبيقك.
cd dotnet-docs-samples/applications/
يقع كل الرمز البرمجي المطلوب لهذا الدرس التطبيقي حول الترميز في الدليل dotnet-docs-samples/applications/leaderboard الحالي كتطبيق C# قابل للتنفيذ باسم Leaderboard ليتم استخدامه كمرجع أثناء تقدّمك في الدرس التطبيقي حول الترميز. سننشئ دليلاً جديدًا وننشئ نسخة من تطبيق "قائمة الصدارة" على مراحل.
أنشئ دليلاً جديدًا باسم "codelab" للتطبيق وانتقِل إلى الدليل باستخدام الأمر التالي:
mkdir codelab && cd $_
أنشئ تطبيق وحدة تحكّم جديدًا بلغة C# في .NET باسم "Leaderboard" باستخدام الأمر التالي:
dotnet new console -n Leaderboard
ينشئ هذا الأمر تطبيقًا بسيطًا على وحدة التحكّم يتألف من ملفَين أساسيَين، وهما ملف المشروع Leaderboard.csproj وملف البرنامج Program.cs.
لننفّذها. غيِّر الدليل إلى دليل "قائمة الصدارة" الذي تم إنشاؤه حديثًا والذي يتضمّن التطبيق:
cd Leaderboard
بعد ذلك، أدخِل الأمر التالي لتشغيله.
dotnet run
من المفترض أن يظهر لك ناتج التطبيق "Hello World!".
لنعدّل الآن تطبيق وحدة التحكّم من خلال تعديل Program.cs لاستخدام مكتبة برامج C# Spanner لإنشاء قائمة صدارة تتألف من جدولَين هما Players وScores. يمكنك إجراء ذلك مباشرةً في "محرِّر Cloud Shell" باتّباع الخطوات التالية:
افتح "محرّر Cloud Shell" من خلال النقر على الرمز المميّز أدناه:

بعد ذلك، افتح ملف Program.cs في Cloud Shell Editor واستبدِل الرمز الحالي في الملف بالرمز البرمجي للتطبيق المطلوب لإنشاء قاعدة بيانات leaderboard وجدولَي Players وScores من خلال لصق الرمز البرمجي لتطبيق C# التالي في ملف Program.cs:
using System;
using System.Threading.Tasks;
using Google.Cloud.Spanner.Data;
using CommandLine;
namespace GoogleCloudSamples.Leaderboard
{
[Verb("create", HelpText = "Create a sample Cloud Spanner database "
+ "along with sample 'Players' and 'Scores' tables in your project.")]
class CreateOptions
{
[Value(0, HelpText = "The project ID of the project to use "
+ "when creating Cloud Spanner resources.", Required = true)]
public string projectId { get; set; }
[Value(1, HelpText = "The ID of the instance where the sample database "
+ "will be created.", Required = true)]
public string instanceId { get; set; }
[Value(2, HelpText = "The ID of the sample database to create.",
Required = true)]
public string databaseId { get; set; }
}
public class Program
{
enum ExitCode : int
{
Success = 0,
InvalidParameter = 1,
}
public static object Create(string projectId,
string instanceId, string databaseId)
{
var response =
CreateAsync(projectId, instanceId, databaseId);
Console.WriteLine("Waiting for operation to complete...");
response.Wait();
Console.WriteLine($"Operation status: {response.Status}");
Console.WriteLine($"Created sample database {databaseId} on "
+ $"instance {instanceId}");
return ExitCode.Success;
}
public static async Task CreateAsync(
string projectId, string instanceId, string databaseId)
{
// Initialize request connection string for database creation.
string connectionString =
$"Data Source=projects/{projectId}/instances/{instanceId}";
using (var connection = new SpannerConnection(connectionString))
{
string createStatement = $"CREATE DATABASE `{databaseId}`";
string[] createTableStatements = new string[] {
// Define create table statement for Players table.
@"CREATE TABLE Players(
PlayerId INT64 NOT NULL,
PlayerName STRING(2048) NOT NULL
) PRIMARY KEY(PlayerId)",
// Define create table statement for Scores table.
@"CREATE TABLE Scores(
PlayerId INT64 NOT NULL,
Score INT64 NOT NULL,
Timestamp TIMESTAMP NOT NULL OPTIONS(allow_commit_timestamp=true)
) PRIMARY KEY(PlayerId, Timestamp),
INTERLEAVE IN PARENT Players ON DELETE NO ACTION" };
// Make the request.
var cmd = connection.CreateDdlCommand(
createStatement, createTableStatements);
try
{
await cmd.ExecuteNonQueryAsync();
}
catch (SpannerException e) when
(e.ErrorCode == ErrorCode.AlreadyExists)
{
// OK.
}
}
}
public static int Main(string[] args)
{
var verbMap = new VerbMap<object>();
verbMap
.Add((CreateOptions opts) => Create(
opts.projectId, opts.instanceId, opts.databaseId))
.NotParsedFunc = (err) => 1;
return (int)verbMap.Run(args);
}
}
}
لتقديم صورة أوضح عن رمز البرنامج، إليك مخططًا للبرنامج مع تصنيف مكوناته الرئيسية:

يمكنك استخدام الملف Program.cs في الدليل dotnet-docs-samples/applications/leaderboard/step4 للاطّلاع على مثال حول الشكل الذي يجب أن يبدو عليه الملف Program.cs بعد إضافة الرمز لتفعيل الأمر create.
بعد ذلك، استخدِم "محرِّر Cloud Shell" لفتح ملف مشروع البرنامج Leaderboard.csproj وتعديله ليصبح مشابهاً للرمز التالي. تأكَّد من حفظ جميع التغييرات باستخدام قائمة "ملف" في "أداة تعديل Cloud Shell".
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Google.Cloud.Spanner.Data" Version="3.3.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\commandlineutil\Lib\CommandLineUtil.csproj" />
</ItemGroup>
</Project>
أضاف هذا التغيير مرجعًا إلى حزمة C# Spanner Nuget Google.Cloud.Spanner.Data التي نحتاج إليها للتفاعل مع واجهة برمجة التطبيقات Cloud Spanner API. يضيف هذا التغيير أيضًا مرجعًا إلى مشروع CommandLineUtil الذي يشكّل جزءًا من مستودع dotnet-doc-samples على Github ويوفر إضافة "verbmap" مفيدة إلى CommandLineParser المفتوحة المصدر، وهي مكتبة مفيدة للتعامل مع إدخال سطر الأوامر لتطبيقات وحدة التحكّم.
يمكنك استخدام الملف Leaderboard.csproj في الدليل dotnet-docs-samples/applications/leaderboard/step4 للاطّلاع على مثال حول الشكل الذي يجب أن يبدو عليه الملف Leaderboard.csproj بعد إضافة الرمز لتفعيل الأمر create.
أنت الآن جاهز لتشغيل النموذج المعدَّل. اكتب ما يلي للاطّلاع على الردّ التلقائي لتطبيقك المعدَّل:
dotnet run
من المفترض أن تظهر لك نتيجة مثل ما يلي:
Leaderboard 1.0.0 Copyright (C) 2018 Leaderboard ERROR(S): No verb selected. create Create a sample Cloud Spanner database along with sample 'Players' and 'Scores' tables in your project. help Display more information on a specific command. version Display version information.
من هذه الاستجابة، يمكننا أن نرى أنّ هذا هو تطبيق Leaderboard الذي يمكن تشغيله باستخدام أحد الأوامر الثلاثة المحتملة: create وhelp وversion.
لنستخدم الأمر create لإنشاء قاعدة بيانات وجداول Spanner. نفِّذ الأمر بدون وسيطات للاطّلاع على الوسيطات المتوقّعة للأمر.
dotnet run create
من المفترض أن يظهر لك ردّ على النحو التالي:
Leaderboard 1.0.0 Copyright (C) 2018 Leaderboard ERROR(S): A required value not bound to option name is missing. --help Display this help screen. --version Display version information. value pos. 0 Required. The project ID of the project to use when creating Cloud Spanner resources. value pos. 1 Required. The ID of the instance where the sample database will be created. value pos. 2 Required. The ID of the sample database to create.
يمكننا هنا أن نرى أنّ الوسيطات المتوقّعة للأمر create هي رقم تعريف المشروع ومعرّف المثيل ومعرّف قاعدة البيانات.
الآن، شغِّل الأمر التالي. تأكَّد من استبدال PROJECT_ID برقم تعريف المشروع الذي أنشأته في بداية هذا الدرس العملي.
dotnet run create PROJECT_ID cloudspanner-leaderboard leaderboard
بعد بضع ثوانٍ، من المفترض أن يظهر لك ردّ على النحو التالي:
Waiting for operation to complete... Operation status: RanToCompletion Created sample database leaderboard on instance cloudspanner-leaderboard
في قسم Cloud Spanner في Cloud Console، من المفترض أن تظهر قاعدة البيانات والجداول الجديدة في القائمة على الجانب الأيمن.

في الخطوة التالية، سنعدّل تطبيقنا لتحميل بعض البيانات إلى قاعدة البيانات الجديدة.
5- تحميل البيانات
لدينا الآن قاعدة بيانات باسم leaderboard تحتوي على جدولَين هما Players وScores. لنستخدِم الآن مكتبة برامج C# لملء الجدول Players باللاعبين والجدول Scores بالنتائج العشوائية لكل لاعب.
افتح "محرّر Cloud Shell" من خلال النقر على الرمز المميّز أدناه:

بعد ذلك، عدِّل ملف Program.cs في Cloud Shell Editor لإضافة أمر insert يمكن استخدامه لإدراج 100 لاعب في جدول Players أو لإدراج 4 نتائج عشوائية في جدول Scores لكل لاعب في جدول Players.
أولاً، أضِف كتلة أمر insert جديدة في "Verbmap" في أعلى "البرنامج" أسفل كتلة الأمر create الحالية:
[Verb("insert", HelpText = "Insert sample 'players' records or 'scores' records "
+ "into the database.")]
class InsertOptions
{
[Value(0, HelpText = "The project ID of the project to use "
+ "when managing Cloud Spanner resources.", Required = true)]
public string projectId { get; set; }
[Value(1, HelpText = "The ID of the instance where the sample database resides.",
Required = true)]
public string instanceId { get; set; }
[Value(2, HelpText = "The ID of the database where the sample database resides.",
Required = true)]
public string databaseId { get; set; }
[Value(3, HelpText = "The type of insert to perform, 'players' or 'scores'.",
Required = true)]
public string insertType { get; set; }
}
بعد ذلك، أضِف طرق Insert وInsertPlayersAsync وInsertScoresAsync التالية أسفل طريقة CreateAsync الحالية:
public static object Insert(string projectId,
string instanceId, string databaseId, string insertType)
{
if (insertType.ToLower() == "players")
{
var responseTask =
InsertPlayersAsync(projectId, instanceId, databaseId);
Console.WriteLine("Waiting for insert players operation to complete...");
responseTask.Wait();
Console.WriteLine($"Operation status: {responseTask.Status}");
}
else if (insertType.ToLower() == "scores")
{
var responseTask =
InsertScoresAsync(projectId, instanceId, databaseId);
Console.WriteLine("Waiting for insert scores operation to complete...");
responseTask.Wait();
Console.WriteLine($"Operation status: {responseTask.Status}");
}
else
{
Console.WriteLine("Invalid value for 'type of insert'. "
+ "Specify 'players' or 'scores'.");
return ExitCode.InvalidParameter;
}
Console.WriteLine($"Inserted {insertType} into sample database "
+ $"{databaseId} on instance {instanceId}");
return ExitCode.Success;
}
public static async Task InsertPlayersAsync(string projectId,
string instanceId, string databaseId)
{
string connectionString =
$"Data Source=projects/{projectId}/instances/{instanceId}"
+ $"/databases/{databaseId}";
long numberOfPlayers = 0;
using (var connection = new SpannerConnection(connectionString))
{
await connection.OpenAsync();
await connection.RunWithRetriableTransactionAsync(async (transaction) =>
{
// Execute a SQL statement to get current number of records
// in the Players table to use as an incrementing value
// for each PlayerName to be inserted.
var cmd = connection.CreateSelectCommand(
@"SELECT Count(PlayerId) as PlayerCount FROM Players");
numberOfPlayers = await cmd.ExecuteScalarAsync<long>();
// Insert 100 player records into the Players table.
SpannerBatchCommand cmdBatch = connection.CreateBatchDmlCommand();
for (int i = 0; i < 100; i++)
{
numberOfPlayers++;
SpannerCommand cmdInsert = connection.CreateDmlCommand(
"INSERT INTO Players "
+ "(PlayerId, PlayerName) "
+ "VALUES (@PlayerId, @PlayerName)",
new SpannerParameterCollection {
{"PlayerId", SpannerDbType.Int64},
{"PlayerName", SpannerDbType.String}});
cmdInsert.Parameters["PlayerId"].Value =
Math.Abs(Guid.NewGuid().GetHashCode());
cmdInsert.Parameters["PlayerName"].Value =
$"Player {numberOfPlayers}";
cmdBatch.Add(cmdInsert);
}
await cmdBatch.ExecuteNonQueryAsync();
});
}
Console.WriteLine("Done inserting player records...");
}
public static async Task InsertScoresAsync(
string projectId, string instanceId, string databaseId)
{
string connectionString =
$"Data Source=projects/{projectId}/instances/{instanceId}"
+ $"/databases/{databaseId}";
// Insert 4 score records into the Scores table for each player
// in the Players table.
using (var connection = new SpannerConnection(connectionString))
{
await connection.OpenAsync();
await connection.RunWithRetriableTransactionAsync(async (transaction) =>
{
Random r = new Random();
bool playerRecordsFound = false;
SpannerBatchCommand cmdBatch =
connection.CreateBatchDmlCommand();
var cmdLookup =
connection.CreateSelectCommand("SELECT * FROM Players");
using (var reader = await cmdLookup.ExecuteReaderAsync())
{
while (await reader.ReadAsync())
{
playerRecordsFound = true;
for (int i = 0; i < 4; i++)
{
DateTime randomTimestamp = DateTime.Now
.AddYears(r.Next(-2, 1))
.AddMonths(r.Next(-12, 1))
.AddDays(r.Next(-28, 0))
.AddHours(r.Next(-24, 0))
.AddSeconds(r.Next(-60, 0))
.AddMilliseconds(r.Next(-100000, 0));
SpannerCommand cmdInsert =
connection.CreateDmlCommand(
"INSERT INTO Scores "
+ "(PlayerId, Score, Timestamp) "
+ "VALUES (@PlayerId, @Score, @Timestamp)",
new SpannerParameterCollection {
{"PlayerId", SpannerDbType.Int64},
{"Score", SpannerDbType.Int64},
{"Timestamp",
SpannerDbType.Timestamp}});
cmdInsert.Parameters["PlayerId"].Value =
reader.GetFieldValue<int>("PlayerId");
cmdInsert.Parameters["Score"].Value =
r.Next(1000, 1000001);
cmdInsert.Parameters["Timestamp"].Value =
randomTimestamp.ToString("o");
cmdBatch.Add(cmdInsert);
}
}
if (!playerRecordsFound)
{
Console.WriteLine("Parameter 'scores' is invalid "
+ "since no player records currently exist. First "
+ "insert players then insert scores.");
Environment.Exit((int)ExitCode.InvalidParameter);
}
else
{
await cmdBatch.ExecuteNonQueryAsync();
Console.WriteLine(
"Done inserting score records..."
);
}
}
});
}
}
بعد ذلك، لجعل الأمر insert يعمل، أضِف الرمز التالي إلى طريقة "Main" في برنامجك:
.Add((InsertOptions opts) => Insert(
opts.projectId, opts.instanceId, opts.databaseId, opts.insertType))
يمكنك استخدام الملف Program.cs في الدليل dotnet-docs-samples/applications/leaderboard/step5 للاطّلاع على مثال حول الشكل الذي يجب أن يبدو عليه الملف Program.cs بعد إضافة الرمز لتفعيل الأمر insert.
لننفّذ البرنامج الآن للتأكّد من أنّ الأمر الجديد insert مضمّن في قائمة الأوامر المحتملة للبرنامج. نفِّذ الأمر التالي:
dotnet run
من المفترض أن يظهر الأمر insert الآن مضمّنًا في الناتج التلقائي للبرنامج:
Leaderboard 1.0.0 Copyright (C) 2018 Leaderboard ERROR(S): No verb selected. create Create a sample Cloud Spanner database along with sample 'Players' and 'Scores' tables in your project. insert Insert sample 'players' records or 'scores' records into the database. help Display more information on a specific command. version Display version information.
لننفّذ الآن الأمر insert للاطّلاع على مَعلمات الإدخال. أدخِل الأمر التالي.
dotnet run insert
من المفترض أن يعرض هذا الردّ التالي:
Leaderboard 1.0.0 Copyright (C) 2018 Leaderboard ERROR(S): A required value not bound to option name is missing. --help Display this help screen. --version Display version information. value pos. 0 Required. The project ID of the project to use when managing Cloud Spanner resources. value pos. 1 Required. The ID of the instance where the sample database resides. value pos. 2 Required. The ID of the database where the sample database resides. value pos. 3 Required. The type of insert to perform, 'players' or 'scores'.
يمكنك أن ترى من الردّ أنّه بالإضافة إلى رقم تعريف المشروع ومعرّف المثيل ومعرّف قاعدة البيانات، هناك وسيطة أخرى value pos. 3 متوقّعة وهي "نوع الإدراج" المطلوب تنفيذه. يمكن أن تتضمّن هذه الوسيطة القيمة "اللاعبون" أو "النتائج".
لننفّذ الآن الأمر insert باستخدام قيم الوسيطات نفسها التي استخدمناها عند تنفيذ الأمر create، مع إضافة "players" كنوع إضافي من وسيطات "نوع الإدراج". تأكَّد من استبدال PROJECT_ID برقم تعريف المشروع الذي أنشأته في بداية هذا الدرس العملي.
dotnet run insert PROJECT_ID cloudspanner-leaderboard leaderboard players
بعد بضع ثوانٍ، من المفترض أن يظهر لك ردّ على النحو التالي:
Waiting for insert players operation to complete... Done inserting player records... Operation status: RanToCompletion Inserted players into sample database leaderboard on instance cloudspanner-leaderboard
لنستخدِم الآن مكتبة برامج C# لملء جدول Scores بأربع نتائج عشوائية مع الطوابع الزمنية لكل لاعب في جدول Players.
تم تعريف عمود Timestamp في الجدول Scores كعمود "الطابع الزمني للتثبيت" من خلال عبارة SQL التالية التي تم تنفيذها عندما نفّذنا الأمر 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
لاحظ السمة OPTIONS(allow_commit_timestamp=true). يؤدي ذلك إلى تحويل Timestamp إلى عمود "الطابع الزمني للتثبيت" ويتيح ملؤه تلقائيًا بالطابع الزمني الدقيق للمعاملة لعمليات INSERT وUPDATE في صف جدول معيّن.
يمكنك أيضًا إدراج قيم الطابع الزمني الخاصة بك في عمود "الطابع الزمني للتثبيت" (commit timestamp) طالما أنّك تُدرج طابعًا زمنيًا بقيمة في الماضي، وهو ما سنفعله لأغراض هذا الدرس العملي.
لننفّذ الآن الأمر insert باستخدام قيم الوسيطات نفسها التي استخدمناها عند تنفيذ الأمر create، مع إضافة "scores" كنوع إضافي من وسيطات "نوع الإدراج". تأكَّد من استبدال PROJECT_ID برقم تعريف المشروع الذي أنشأته في بداية هذا الدرس العملي.
dotnet run insert PROJECT_ID cloudspanner-leaderboard leaderboard scores
بعد بضع ثوانٍ، من المفترض أن يظهر لك ردّ على النحو التالي:
Waiting for insert players operation to complete... Done inserting player records... Operation status: RanToCompletion Inserted players into sample database leaderboard on instance cloudspanner-leaderboard
يؤدي تنفيذ insert مع تحديد "نوع الإدراج" على أنّه scores إلى استدعاء الطريقة InsertScoresAsync التي تستخدم مقتطفات الرمز البرمجي التالية لإدراج طابع زمني تم إنشاؤه عشوائيًا مع تاريخ ووقت حدثا في الماضي:
DateTime randomTimestamp = DateTime.Now
.AddYears(r.Next(-2, 1))
.AddMonths(r.Next(-12, 1))
.AddDays(r.Next(-28, 0))
.AddHours(r.Next(-24, 0))
.AddSeconds(r.Next(-60, 0))
.AddMilliseconds(r.Next(-100000, 0));
...
cmdInsert.Parameters["Timestamp"].Value = randomTimestamp.ToString("o");
لتعبئة عمود Timestamp تلقائيًا بالطابع الزمني لوقت إجراء معاملة "الإدراج" بالضبط، يمكنك بدلاً من ذلك إدراج الثابت SpannerParameter.CommitTimestamp في لغة C# كما في مقتطف الرمز التالي:
cmd.Parameters["Timestamp"].Value = SpannerParameter.CommitTimestamp;
بعد إكمال عملية تحميل البيانات، لنتحقّق من القيم التي كتبناها للتو في جداولنا الجديدة. اختَر أولاً قاعدة بيانات leaderboard ثم اختَر جدول Players. انقر على علامة التبويب Data. يجب أن تظهر لك بيانات في العمودَين PlayerId وPlayerName في الجدول.

بعد ذلك، لننتحقق من أنّ جدول "النتائج" يتضمّن أيضًا بيانات من خلال النقر على الجدول Scores واختيار علامة التبويب Data. من المفترض أن تظهر لك بيانات في أعمدة PlayerId وTimestamp وScore في الجدول.

أحسنت! لنعدّل برنامجنا لتشغيل بعض طلبات البحث التي يمكننا استخدامها لإنشاء قائمة صدارة للألعاب.
6. تنفيذ طلبات البحث في قائمة الصدارة
بعد أن أعددنا قاعدة البيانات وحمّلنا المعلومات في جداولنا، لننشئ الآن قائمة صدارة باستخدام هذه البيانات. ولإجراء ذلك، علينا الإجابة عن الأسئلة الأربعة التالية:
- ما هي قائمة "أفضل عشرة لاعبين" على الإطلاق؟
- ما هي قائمة "أفضل عشرة لاعبين" لهذا العام؟
- ما هي "أفضل عشرة" لاعبين في الشهر؟
- ما هي "أفضل عشرة" لاعبين في الأسبوع؟
لنعدّل برنامجنا لتنفيذ طلبات بحث SQL التي ستجيب عن هذه الأسئلة.
سنضيف الأمر query الذي سيتيح تنفيذ طلبات البحث للإجابة عن الأسئلة التي ستنتج المعلومات المطلوبة لقائمة الصدارة.
عدِّل الملف Program.cs في "محرّر Cloud Shell" لتعديل البرنامج من أجل إضافة الأمر query.
أولاً، أضِف كتلة أمر query جديدة في "Verbmap" في أعلى "البرنامج" أسفل كتلة الأمر insert الحالية:
[Verb("query", HelpText = "Query players with 'Top Ten' scores within a specific timespan "
+ "from sample Cloud Spanner database table.")]
class QueryOptions
{
[Value(0, HelpText = "The project ID of the project to use "
+ "when managing Cloud Spanner resources.", Required = true)]
public string projectId { get; set; }
[Value(1, HelpText = "The ID of the instance where the sample data resides.",
Required = true)]
public string instanceId { get; set; }
[Value(2, HelpText = "The ID of the database where the sample data resides.",
Required = true)]
public string databaseId { get; set; }
[Value(3, Default = 0, HelpText = "The timespan in hours that will be used to filter the "
+ "results based on a record's timestamp. The default will return the "
+ "'Top Ten' scores of all time.")]
public int timespan { get; set; }
}
بعد ذلك، أضِف طريقتَي Query وQueryAsync التاليتَين أسفل طريقة InsertScoresAsync الحالية:
public static object Query(string projectId,
string instanceId, string databaseId, int timespan)
{
var response = QueryAsync(
projectId, instanceId, databaseId, timespan);
response.Wait();
return ExitCode.Success;
}
public static async Task QueryAsync(
string projectId, string instanceId, string databaseId, int timespan)
{
string connectionString =
$"Data Source=projects/{projectId}/instances/"
+ $"{instanceId}/databases/{databaseId}";
// Create connection to Cloud Spanner.
using (var connection = new SpannerConnection(connectionString))
{
string sqlCommand;
if (timespan == 0)
{
// No timespan specified. Query Top Ten scores of all time.
sqlCommand =
@"SELECT p.PlayerId, p.PlayerName, s.Score, s.Timestamp
FROM Players p
JOIN Scores s ON p.PlayerId = s.PlayerId
ORDER BY s.Score DESC LIMIT 10";
}
else
{
// Query Top Ten scores filtered by the timepan specified.
sqlCommand =
$@"SELECT p.PlayerId, p.PlayerName, s.Score, s.Timestamp
FROM Players p
JOIN Scores s ON p.PlayerId = s.PlayerId
WHERE s.Timestamp >
TIMESTAMP_SUB(CURRENT_TIMESTAMP(),
INTERVAL {timespan.ToString()} HOUR)
ORDER BY s.Score DESC LIMIT 10";
}
var cmd = connection.CreateSelectCommand(sqlCommand);
using (var reader = await cmd.ExecuteReaderAsync())
{
while (await reader.ReadAsync())
{
Console.WriteLine("PlayerId : "
+ reader.GetFieldValue<string>("PlayerId")
+ " PlayerName : "
+ reader.GetFieldValue<string>("PlayerName")
+ " Score : "
+ string.Format("{0:n0}",
Int64.Parse(reader.GetFieldValue<string>("Score")))
+ " Timestamp : "
+ reader.GetFieldValue<string>("Timestamp").Substring(0, 10));
}
}
}
}
بعد ذلك، لجعل الأمر query يعمل، أضِف الرمز التالي إلى طريقة "Main" في برنامجك:
.Add((QueryOptions opts) => Query(
opts.projectId, opts.instanceId, opts.databaseId, opts.timespan))
يمكنك استخدام الملف Program.cs في الدليل dotnet-docs-samples/applications/leaderboard/step6 للاطّلاع على مثال حول الشكل الذي يجب أن يبدو عليه الملف Program.cs بعد إضافة الرمز لتفعيل الأمر query.
لننفّذ البرنامج الآن للتأكّد من أنّ الأمر الجديد query مضمّن في قائمة الأوامر المحتملة للبرنامج. نفِّذ الأمر التالي:
dotnet run
من المفترض أن يظهر الأمر query الآن مضمّنًا في الناتج التلقائي للبرنامج كخيار أمر جديد:
Leaderboard 1.0.0 Copyright (C) 2018 Leaderboard ERROR(S): No verb selected. create Create a sample Cloud Spanner database along with sample 'Players' and 'Scores' tables in your project. insert Insert sample 'players' records or 'scores' records into the database. query Query players with 'Top Ten' scores within a specific timespan from sample Cloud Spanner database table. help Display more information on a specific command. version Display version information.
لننفّذ الآن الأمر query للاطّلاع على مَعلمات الإدخال. أدخِل الأمر التالي:
dotnet run query
سيؤدي ذلك إلى عرض الرد التالي:
Leaderboard 1.0.0 Copyright (C) 2018 Leaderboard ERROR(S): A required value not bound to option name is missing. --help Display this help screen. --version Display version information. value pos. 0 Required. The project ID of the project to use when managing Cloud Spanner resources. value pos. 1 Required. The ID of the instance where the sample data resides. value pos. 2 Required. The ID of the database where the sample data resides. value pos. 3 (Default: 0) The timespan in hours that will be used to filter the results based on a record's timestamp. The default will return the 'Top Ten' scores of all time.
يمكنك أن ترى من الردّ أنّه بالإضافة إلى رقم تعريف المشروع ومعرّف المثيل وقاعدة البيانات، هناك وسيطة أخرى value pos. 3 متوقّعة تتيح لنا تحديد فترة زمنية بعدد الساعات لاستخدامها في فلترة السجلات استنادًا إلى قيمتها في عمود Timestamp بجدول Scores. تتضمّن هذه الوسيطة القيمة التلقائية 0، ما يعني أنّه لن يتم فلترة أي سجلّات حسب الطوابع الزمنية. وبالتالي، يمكننا استخدام الأمر query بدون قيمة "فترة زمنية" للحصول على قائمة بأفضل عشرة لاعبين على الإطلاق.
لننفّذ الأمر query بدون تحديد "الفترة الزمنية"، وذلك باستخدام قيم الوسيطة نفسها التي استخدمناها عند تنفيذ الأمر create. تأكَّد من استبدال PROJECT_ID برقم تعريف المشروع الذي أنشأته في بداية هذا الدرس العملي.
dotnet run query PROJECT_ID cloudspanner-leaderboard leaderboard
ستظهر لك استجابة تتضمّن أفضل عشرة لاعبين على الإطلاق، مثل ما يلي:
PlayerId : 1843159180 PlayerName : Player 87 Score : 998,955 Timestamp : 2016-03-23
PlayerId : 61891198 PlayerName : Player 19 Score : 998,720 Timestamp : 2016-03-26
PlayerId : 340906298 PlayerName : Player 48 Score : 993,302 Timestamp : 2015-08-27
PlayerId : 541473117 PlayerName : Player 22 Score : 991,368 Timestamp : 2018-04-30
PlayerId : 857460496 PlayerName : Player 68 Score : 988,010 Timestamp : 2015-05-25
PlayerId : 1826646419 PlayerName : Player 91 Score : 984,022 Timestamp : 2016-11-26
PlayerId : 1002199735 PlayerName : Player 35 Score : 982,933 Timestamp : 2015-09-26
PlayerId : 2002563755 PlayerName : Player 23 Score : 979,041 Timestamp : 2016-10-25
PlayerId : 1377548191 PlayerName : Player 2 Score : 978,632 Timestamp : 2016-05-02
PlayerId : 1358098565 PlayerName : Player 65 Score : 973,257 Timestamp : 2016-10-30
لننفّذ الآن الأمر query مع الوسيطات اللازمة للاستعلام عن أفضل عشرة لاعبين في العام من خلال تحديد "الفترة الزمنية" التي تساوي عدد الساعات في السنة، أي 8760 ساعة. تأكَّد من استبدال PROJECT_ID برقم تعريف المشروع الذي أنشأته في بداية هذا الدرس العملي.
dotnet run query PROJECT_ID cloudspanner-leaderboard leaderboard 8760
من المفترض أن يظهر لك ردّ يتضمّن أفضل عشرة لاعبين في العام، مثل ما يلي:
PlayerId : 541473117 PlayerName : Player 22 Score : 991,368 Timestamp : 2018-04-30
PlayerId : 228469898 PlayerName : Player 82 Score : 967,177 Timestamp : 2018-01-26
PlayerId : 1131343000 PlayerName : Player 26 Score : 944,725 Timestamp : 2017-05-26
PlayerId : 396780730 PlayerName : Player 41 Score : 929,455 Timestamp : 2017-09-26
PlayerId : 61891198 PlayerName : Player 19 Score : 921,251 Timestamp : 2018-05-01
PlayerId : 634269851 PlayerName : Player 54 Score : 909,379 Timestamp : 2017-07-24
PlayerId : 821111159 PlayerName : Player 55 Score : 908,402 Timestamp : 2017-05-25
PlayerId : 228469898 PlayerName : Player 82 Score : 889,040 Timestamp : 2017-12-26
PlayerId : 1408782275 PlayerName : Player 27 Score : 874,124 Timestamp : 2017-09-24
PlayerId : 1002199735 PlayerName : Player 35 Score : 864,758 Timestamp : 2018-04-24
لننفّذ الآن الأمر query للاستعلام عن أفضل عشرة لاعبين في الشهر من خلال تحديد "الفترة الزمنية" التي تساوي عدد الساعات في الشهر، أي 730 ساعة. تأكَّد من استبدال PROJECT_ID برقم تعريف المشروع الذي أنشأته في بداية هذا الدرس العملي.
dotnet run query PROJECT_ID cloudspanner-leaderboard leaderboard 730
من المفترض أن يظهر لك رد يتضمّن أفضل عشرة لاعبين في الشهر، مثل ما يلي:
PlayerId : 541473117 PlayerName : Player 22 Score : 991,368 Timestamp : 2018-04-30
PlayerId : 61891198 PlayerName : Player 19 Score : 921,251 Timestamp : 2018-05-01
PlayerId : 1002199735 PlayerName : Player 35 Score : 864,758 Timestamp : 2018-04-24
PlayerId : 1228490432 PlayerName : Player 11 Score : 682,033 Timestamp : 2018-04-26
PlayerId : 648239230 PlayerName : Player 92 Score : 653,895 Timestamp : 2018-05-02
PlayerId : 70762849 PlayerName : Player 77 Score : 598,074 Timestamp : 2018-04-22
PlayerId : 1671215342 PlayerName : Player 62 Score : 506,770 Timestamp : 2018-04-28
PlayerId : 1208850523 PlayerName : Player 21 Score : 216,008 Timestamp : 2018-04-30
PlayerId : 1587692674 PlayerName : Player 63 Score : 188,157 Timestamp : 2018-04-25
PlayerId : 992391797 PlayerName : Player 37 Score : 167,175 Timestamp : 2018-04-30
لننفّذ الآن الأمر query للاستعلام عن أفضل عشرة لاعبين في الأسبوع من خلال تحديد "فترة زمنية" تساوي عدد الساعات في الأسبوع، أي 168 ساعة. تأكَّد من استبدال PROJECT_ID برقم تعريف المشروع الذي أنشأته في بداية هذا الدرس العملي.
dotnet run query PROJECT_ID cloudspanner-leaderboard leaderboard 168
من المفترض أن يظهر لك ردّ يتضمّن أفضل عشرة لاعبين في الأسبوع، مثل ما يلي:
PlayerId : 541473117 PlayerName : Player 22 Score : 991,368 Timestamp : 2018-04-30
PlayerId : 61891198 PlayerName : Player 19 Score : 921,251 Timestamp : 2018-05-01
PlayerId : 228469898 PlayerName : Player 82 Score : 853,602 Timestamp : 2018-04-28
PlayerId : 1131343000 PlayerName : Player 26 Score : 695,318 Timestamp : 2018-04-30
PlayerId : 1228490432 PlayerName : Player 11 Score : 682,033 Timestamp : 2018-04-26
PlayerId : 1408782275 PlayerName : Player 27 Score : 671,827 Timestamp : 2018-04-27
PlayerId : 648239230 PlayerName : Player 92 Score : 653,895 Timestamp : 2018-05-02
PlayerId : 816861444 PlayerName : Player 83 Score : 622,277 Timestamp : 2018-04-27
PlayerId : 162043954 PlayerName : Player 75 Score : 572,634 Timestamp : 2018-05-02
PlayerId : 1671215342 PlayerName : Player 62 Score : 506,770 Timestamp : 2018-04-28
ممتاز!
عند إضافة سجلات، سيوسّع Spanner قاعدة البيانات إلى الحجم الذي تحتاجه.
بغض النظر عن حجم قاعدة البيانات، يمكن أن يستمر ترتيب اللاعبين في التوسّع بدقة باستخدام Spanner وتكنولوجيا Truetime.
7. تنظيف
بعد كل المتعة التي حظينا بها أثناء اللعب باستخدام Spanner، علينا تنظيف ساحة اللعب، ما يوفّر لنا موارد وأموالاً ثمينة. لحسن الحظ، هذه خطوة سهلة، ما عليك سوى الانتقال إلى "وحدة تحكّم المطوّرين" وحذف المثيل الذي أنشأناه في خطوة الدرس العملي التي تحمل الاسم "إعداد مثيل Cloud Spanner".
8. تهانينا!
المواضيع التي تناولناها:
- مثيلات وقواعد بيانات ومخطط جداول Google Cloud Spanner للوحات الصدارة
- كيفية إنشاء تطبيق وحدة تحكّم بلغة C# في .NET Core
- كيفية إنشاء قاعدة بيانات وجداول Spanner باستخدام مكتبة برامج C#
- كيفية تحميل البيانات إلى قاعدة بيانات Spanner باستخدام مكتبة برامج C#
- كيفية الاستعلام عن نتائج "أفضل عشرة" من بياناتك باستخدام الطوابع الزمنية لعمليات الإكمال في Spanner ومكتبة برامج C#
الخطوات التالية:
- الاطّلاع على التقرير الموجز حول إمكانات Spanner
- مزيد من المعلومات عن تصميم المخطط وأفضل الممارسات المتعلّقة بالطلبات
- مزيد من المعلومات عن الطوابع الزمنية لعمليات الإيداع في Cloud Spanner
تقديم ملاحظاتك
- يُرجى تخصيص بعض الوقت لإكمال الاستطلاع القصير جدًا.