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، وهي بيئة سطر أوامر يتم تشغيلها في السحابة الإلكترونية.
هذا الجهاز الافتراضي المستند إلى نظام دبيان محمل بكل أدوات التطوير التي ستحتاج إليها. وتوفّر هذه الشبكة دليلاً رئيسيًا دائمًا بسعة 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
ثم غير الدليل إلى "التطبيقات" الدليل الذي ستنشئ فيه التطبيق.
cd dotnet-docs-samples/applications/
تتوفّر كلّ الرموز المطلوبة لهذا الدرس التطبيقي حول الترميز في دليل dotnet-docs-samples/applications/leaderboard
الحالي كتطبيق C# قابل للتشغيل واسمه Leaderboard
، لاستخدامه كمرجع أثناء تقدّمك في الدرس التطبيقي حول الترميز. سننشئ دليلاً جديدًا وننشئ نسخة من تطبيق "ليدربورد" على مراحل.
إنشاء دليل جديد باسم "درس تطبيقي حول الترميز" للتطبيق وتغيير الدليل إليه باستخدام الأمر التالي:
mkdir codelab && cd $_
إنشاء تطبيق وحدة تحكم .NET C# جديد باسم "Leaderboard" باستخدام الأمر التالي:
dotnet new console -n Leaderboard
ينشئ هذا الأمر تطبيق وحدة تحكم بسيطًا يتكون من ملفين أساسيين، ملف المشروع Leaderboard.csproj
وملف البرنامج Program.cs
.
لنقم بإجرائها. تغيير الدليل إلى دليل "ليدربورد" الذي تم إنشاؤه حديثًا حيث يتوفّر التطبيق:
cd Leaderboard
ثم أدخل الأمر التالي لتشغيله.
dotnet run
من المفترض أن يظهر لك ناتج التطبيق "Hello World!".
يمكننا الآن تحديث تطبيق وحدة التحكم من خلال تعديل Program.cs
لاستخدام مكتبة عميل C# Spanner لإنشاء لوحة صدارة تتألف من مشغِّلي جدولي ونتائج. ويمكنك إجراء ذلك مباشرةً في "محرِّر Cloud Shell" باتّباع الخطوات التالية:
افتح محرِّر Cloud Shell، من خلال النقر على الرمز الموضح أدناه:
بعد ذلك، افتح ملف Program.cs
في محرِّر Cloud Shell واستبدِل الرمز الحالي للملف بالرمز المطلوب لإنشاء قاعدة البيانات 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. يضيف هذا التغيير أيضًا إشارة إلى مشروع CommandLineUtil
الذي هو جزء من مستودع جيت هب لعينات مستند نقطي، ويوفر طريقة مفيدة لخريطة الفعل إضافة إلى البرنامج المفتوح المصدر 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" لإضافة الأمر 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
، أضِف الرمز التالي إلى "الرئيسي" في برنامجك. :
.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
متوقعة وهي "نوع الإدراج" تنفيذها. يمكن أن تحتوي هذه الوسيطة على قيمة "players" أو "النتائج".
لنقم الآن بتشغيل الأمر 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 في صف جدول معين.
يمكنك أيضًا إدراج قيم الطوابع الزمنية الخاصة بك في "الطابع الزمني للالتزام". طالما تُدرِج طابعًا زمنيًا مع قيمة من الماضي، وهو ما سنفعله في هذا الدرس التطبيقي حول الترميز.
لنقم الآن بتشغيل الأمر 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
تلقائيًا بالطابع الزمني عند ضبط الزر "إدراج" بالضبط تحدث المعاملة، يمكنك بدلاً من ذلك إدراج عنصر C# الثابت SpannerParameter.CommitTimestamp
كما في مقتطف الرمز التالي:
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
، أضِف الرمز التالي إلى "الرئيسي" في برنامجك. :
.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
بدون "timespan" للحصول على قائمة "أهم عشرة" لاعبين في جميع الأوقات.
لنشغِّل الأمر query
بدون تحديد "timespan" باستخدام قيم الوسيطات نفسها التي استخدمناها عند تشغيل الأمر 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، علينا تنظيف ساحة اللعب وتوفير الموارد والمال الثمينة. لحسن الحظّ، هذه خطوة سهلة. ما عليك سوى الانتقال إلى Play Console وحذف المثيل الذي أنشأناه في خطوة الدرس التطبيقي حول الترميز المسماة "إعداد مثيل Cloud Spanner".
8. تهانينا!
المواضيع التي تناولناها:
- مثيلات Google Cloud Spanner وقواعد البيانات ومخطط الجدول لقائمة الصدارة
- كيفية إنشاء تطبيق وحدة تحكم .NET Core C#
- كيفية إنشاء قاعدة بيانات وجدول Spanner باستخدام مكتبة عملاء C#
- كيفية تحميل البيانات إلى قاعدة بيانات Spanner باستخدام مكتبة عميل C#
- كيفية الاستعلام عن "أعلى عشرة" نتائج من بياناتك باستخدام الطوابع الزمنية لتنفيذ Spanner ومكتبة عميل C#
الخطوات التالية:
- الاطّلاع على التقرير الموجز حول Spanner CAP
- تعرَّف على أفضل ممارسات طلب البحث وتصميم المخطط.
- مزيد من المعلومات حول الطوابع الزمنية للالتزام في Cloud Spanner
يُرجى إرسال ملاحظاتك إلينا
- يُرجى تخصيص بعض الوقت لإكمال الاستطلاع القصير جدًا.