كيفية استخدام الحزم لتحسين Azure SQL Database، وأداء تطبيقAzure SQL Managed Instance

ينطبق على: قاعدة بيانات Azure SQL مثيل Azure SQL المُدار

تعمل عمليات التجميع في قاعدة بيانات Azure SQL والمثيل المدارAzure SQL على تحسين أداء التطبيقات، وقابلية تطويرها بشكل كبير. لفهم الفوائد، يغطي الجزء الأول من هذه المقالة بعض نتائج اختبار العينة التي تقارن بين الطلبات المتسلسلة والطلبات المحددة إلى قاعدة بيانات في قاعدة بيانات Azure SQL، أو المثيل المدار لـ Azure SQL. تظهر بقية المقالة التقنيات والسيناريوهات والاعتبارات التي تساعدك على استخدام الحزم بنجاح في تطبيقات Azure الخاصة بك.

لماذا يعد التجميع مهمًا لقاعدة بيانات Azure SQL، ومثيل Azure SQL المدار؟

يعتبر تجميع المكالمات إلى الخدمة عن بعد إستراتيجية معروفة لزيادة الأداء، وقابلية التطوير. توجد تكاليف معالجة ثابتة لأي تفاعلات مع خدمة عن بعد؛ مثل: تحويل البيانات إلى ملف، ونقل الشبكة، وإلغاء التسلسل. إن تجميع العديد من المعاملات المنفصلة في حزمة واحدة من شأنه أن يقلل من هذه التكاليف إلى الحد الأدنى.

في هذه المقالة، نريد أن ندرس مختلف إستراتيجيات الحزم والسيناريوهات. على الرغم من أن هذه الإستراتيجيات مهمة أيضًا للتطبيقات المحلية التي تستخدم SQL Server، إلا أن هناك عدة أسباب لإبراز استخدام الحزم لقاعدة بيانات Azure SQL، والمثيل المدار لـ Azure SQL:

  • قد يكون هناك زمن وصول أكبر للشبكة في الوصول إلى قاعدة بيانات Azure SQL، والمثيل المدار Azure SQL، خاصةً إذا كنت تحاول الوصول إلى قاعدة بيانات Azure SQL، أو المثيل المدار Azure SQL من خارج نفس مركز بيانات Microsoft Azure.
  • تعني الخصائص متعددة المستأجرين لقاعدة بيانات Azure SQL، والمثيل المدار لـ Azure SQL أن كفاءة طبقة الوصول إلى البيانات ترتبط بقابلية تطوير قاعدة البيانات ككل. استجابة لاستخدام يزيد عن الحصص النسبية المحددة مسبقًا، يمكن لقاعدة بيانات Azure SQL، والمثيل المدار Azure SQL تقليل الإنتاجية، أو الاستجابة باستثناءات خانقة. تمكنك الكفاءات؛ مثل: الحزم من القيام بالمزيد من العمل قبل الوصول إلى هذه الحدود.
  • الدفعات فعالة أيضًا للهندسة المعمارية التي تستخدم قواعد بيانات متعددة (القطع). لا تزال كفاءة تفاعلك مع كل وحدة قاعدة بيانات عاملاً رئيسيًا في قابليتك الإجمالية للتوسع.

من مزايا استخدام قاعدة بيانات Azure SQL، أو المثيل المدار Azure SQL عدم الحاجة إلى إدارة الخوادم التي تستضيف قاعدة البيانات. ومع ذلك، فإن هذه البنية الأساسية المدارة تعني أيضًا أنه يتعين عليك التفكير بشكل مختلف حول عمليات تحسين قاعدة البيانات. لم يعد بإمكانك البحث عن تحسين أجهزة قاعدة البيانات، أو البنية الأساسية للشبكة. يتحكم Microsoft Azure في هذه البيئات. المنطقة الرئيسية التي يمكنك التحكم بها هي كيفية تفاعل التطبيق مع قاعدة بيانات azure SQL، وAzure SQL المثيل المدار. الدفعات هي واحدة من هذه التحسينات.

يتناول الجزء الأول من هذه المقالة تقنيات الدفعات المختلفة لتطبيقات .NET التي تستخدم المثيل المدار Azure SQL، أو Azure SQL. يغطي القسمان الأخيران إرشادات وسيناريوهات الدفع.

إستراتيجيات الدفع

ملاحظة حول نتائج التوقيت في هذه المقالة

ملاحظة

النتائج ليست معايير؛ ولكن تهدف إلى إظهار ⁧⁩الأداء النسبي.⁧⁩ وتستند المواعيد إلى ما متوسطه 10 عمليات اختبار على الأقل. يتم إدراج العمليات في جدول فارغ. تم قياس هذه الاختبارات قبل V12، وأنها لا تتوافق بالضرورة مع الإنتاجية التي قد تواجهها في قاعدة بيانات V12 باستخدام ⁧⁩مستويات خدمة DTU⁧⁩ الجديدة، أو ⁧⁩مستويات خدمة vCore⁧⁩. يجب أن تكون الفائدة النسبية لتقنية الدفع متشابهة.

المعاملات

يبدو غريبًا أن تبدأ مراجعة الدفعات من خلال مناقشة المعاملات. ولكن استخدام المعاملات من جانب العميل له تأثير قوي على أداء الخادم. ويمكن إضافة المعاملات ببضعة أسطر فقط من التعليمات البرمجية؛ لذلك فإنها توفر طريقة سريعة لتحسين أداء العمليات التسلسلية.

خذ بعين الاعتبار التعليمات البرمجية C# التالية التي تحتوي على تسلسل عمليات الإدراج، والتحديث على جدول بسيط.

List<string> dbOperations = new List<string>();
dbOperations.Add("update MyTable set mytext = 'updated text' where id = 1");
dbOperations.Add("update MyTable set mytext = 'updated text' where id = 2");
dbOperations.Add("update MyTable set mytext = 'updated text' where id = 3");
dbOperations.Add("insert MyTable values ('new value',1)");
dbOperations.Add("insert MyTable values ('new value',2)");
dbOperations.Add("insert MyTable values ('new value',3)");

تقوم التعليمات البرمجية ADO.NET التالية بتنفيذ هذه العمليات بشكل تسلسلي.

using (SqlConnection connection = new SqlConnection(CloudConfigurationManager.GetSetting("Sql.ConnectionString")))
{
    conn.Open();

    foreach(string commandString in dbOperations)
    {
        SqlCommand cmd = new SqlCommand(commandString, conn);
        cmd.ExecuteNonQuery();
    }
}

أفضل طريقة لتحسين هذا الرمز هي تنفيذ شكل ما من دفعة من جانب العميل لهذه المكالمات. ولكن هناك وسيلة بسيطة لزيادة أداء هذه التعليمة البرمجية بمجرد التفاف تسلسل المكالمات في المعاملة. هنا هو نفس التعليمات البرمجية التي تستخدم معاملة.

using (SqlConnection connection = new SqlConnection(CloudConfigurationManager.GetSetting("Sql.ConnectionString")))
{
    conn.Open();
    SqlTransaction transaction = conn.BeginTransaction();

    foreach (string commandString in dbOperations)
    {
        SqlCommand cmd = new SqlCommand(commandString, conn, transaction);
        cmd.ExecuteNonQuery();
    }

    transaction.Commit();
}

يتم استخدام المعاملات فعليًا في كلا المثالين. في المثال الأول، كل استدعاء فردي هو معاملة ضمنية. وفي المثال الثاني، تغطي معاملة صريحة كافة المكالمات. في الوثائق الخاصة ⁧⁩بسجل المعاملات قبل الكتابة⁧⁩، يتم مسح سجلات السجل إلى القرص عند تنفيذ المعاملة. لذا، من خلال تضمين المزيد من المكالمات في إحدى المعاملات، يمكن أن تتأخر الكتابة إلى سجل المعاملة حتى يتم تنفيذ المعاملة. في الواقع، أنت تقوم بتمكين الدفعة لعمليات الكتابة إلى سجل معاملات الخادم.

يعرض الجدول التالي بعض نتائج الاختبار المخصصة. أجرت الاختبارات نفس عمليات الإدراج المتسلسلة مع المعاملات وبدونها. من منظور أوضح، أجريت المجموعة الأولى من الاختبارات عن بعد من كمبيوتر محمول إلى قاعدة البيانات في Microsoft Azure. تم تشغيل المجموعة الثانية من الاختبارات من خدمة سحابة وقاعدة بيانات، كلاهما موجودان داخل نفس مركز بيانات Microsoft Azure (غرب الولايات المتحدة). يعرض الجدول التالي المدة بالمللي ثانية من إدراج متسلسل مع وبدون معاملات.

⁩في أماكن العمل إلى Azure⁧⁩:

العمليات لا توجد معاملة (مللي ثانية) المعاملة (مللي ثانية)
1 130 402
10 1208 1226
100 12662 10395
1000 128852 102917

⁩Azure إلى Azure (نفس مركز البيانات):⁧

العمليات لا توجد معاملة (مللي ثانية) المعاملة (مللي ثانية)
1 21 26
10 220 56
100 2145 341
1000 21479 2756

ملاحظة

النتائج ليست معايير. راجع ⁧⁩الملاحظة حول نتائج التوقيت في هذه المقالة⁧⁩.

استنادًا إلى نتائج الاختبار السابقة، يؤدي التفاف عملية واحدة في معاملة إلى تقليل من الأداء. ولكن كلما قمت بزيادة عدد العمليات داخل معاملة واحدة، يصبح تحسين الأداء أكثر وضوحًا. يكون فرق الأداء أيضًا أكثر وضوحًا عند حدوث كافة العمليات داخل مركز بيانات Microsoft Azure. يطغى زمن الوصول المتزايد لاستخدام قاعدة بيانات Azure SQL، أو المثيل المدار Azure SQL من خارج مركز بيانات Microsoft Azure على مكاسب الأداء الناتجة عن استخدام المعاملات.

على الرغم من أن استخدام المعاملات يمكن أن يزيد من الأداء، استمر إلى ⁧⁩ مراقبة أفضل الممارسات للمعاملات والاتصالات.⁧⁩ أبق الحركة قصيرة قدر الإمكان، وأغلق اتصال قاعدة البيانات بعد اكتمال العمل. تضمن جملة الاستخدام في المثال السابق إغلاق الاتصال عند اكتمال كتلة التعليمات البرمجية التالية.

يوضح المثال السابق أنه يمكنك إضافة حركة محلية إلى أي كود ADO.NET بسطرين. توفر الحركات طريقة سريعة لتحسين أداء التعليمات البرمجية التي تقوم بعمليات الإدراج، والتحديث، والحذف المتسلسلة. ومع ذلك، للحصول على الأداء الأسرع، خذ بعين الاعتبار تغيير التعليمات البرمجية؛ للاستفادة من دفعات العميل؛ مثل: معلمات قيمة الجدول.

لمزيد من المعلومات حول المعاملات في ADO.NET، راجع ⁧⁩المعاملات المحلية في ADO.NET⁧⁩.

المعلمات ذات القيمة الجدولية

تدعم المعلمات ذات القيمة الجدولية أنواع الجداول المعرفة من قبل المستخدم كمعلمات في جمل Transact-SQL، والإجراءات المخزنة والوظائف. تسمح لك تقنية الدفعات من جانب العميل بإرسال صفوف متعددة من البيانات داخل المعلمة ذات القيمة الجدولية. لاستخدام معلمات ذات قيمة جدول، قم أولاً بتعريف نوع جدول. تقوم العبارة SQL Transact التالية بإنشاء نوع جدول يسمى ⁧⁩MyTableType⁧⁩.

    CREATE TYPE MyTableType AS TABLE
    ( mytext TEXT,
      num INT );

في التعليمات البرمجية، يمكنك إنشاء ⁧⁩DataTable⁧⁩ بنفس الأسماء والأنواع من نوع الجدول. قم بتمرير ⁧⁩DataTable⁧⁩ هذا في معلمة في استعلام نص، أو استدعاء إجراء مخزن. المثال التالي يوضح هذه الطريقة:

using (SqlConnection connection = new SqlConnection(CloudConfigurationManager.GetSetting("Sql.ConnectionString")))
{
    connection.Open();

    DataTable table = new DataTable();
    // Add columns and rows. The following is a simple example.
    table.Columns.Add("mytext", typeof(string));
    table.Columns.Add("num", typeof(int));
    for (var i = 0; i < 10; i++)
    {
        table.Rows.Add(DateTime.Now.ToString(), DateTime.Now.Millisecond);
    }

    SqlCommand cmd = new SqlCommand(
        "INSERT INTO MyTable(mytext, num) SELECT mytext, num FROM @TestTvp",
        connection);

    cmd.Parameters.Add(
        new SqlParameter()
        {
            ParameterName = "@TestTvp",
            SqlDbType = SqlDbType.Structured,
            TypeName = "MyTableType",
            Value = table,
        });

    cmd.ExecuteNonQuery();
}

في المثال السابق، يُدرج الكائن SqlCommand صفوفاً من معلمة بقيمة الجدول، TestTvp. تم تعيين كائن ⁧⁩DataTable⁧⁩ الذي تم إنشاؤه مسبقًا إلى هذه المعلمة باستخدام الأسلوب ⁧⁩SqlCommand.Parameters.Add.⁧⁩ يؤدي إرسال المدرجات في دفعات في مكالمة واحدة إلى زيادة الأداء بشكل ملحوظ عن الإدراجات التسلسلية.

لتحسين المثال السابق بشكل أكبر، استخدم إجراء مخزن بدلاً من أمر يستند إلى نص. يقوم أمر Transact-SQL التالي بإنشاء إجراء مخزن يأخذ المعلمة⁧⁩ SimpleTestTableType⁧⁩ ذات القيمة الجدولية.

CREATE PROCEDURE [dbo].[sp_InsertRows]
@TestTvp as MyTableType READONLY
AS
BEGIN
INSERT INTO MyTable(mytext, num)
SELECT mytext, num FROM @TestTvp
END
GO

ثم قم بتغيير تعريف كائن ⁧⁩SqlCommand⁧⁩ في مثال التعليمات البرمجية السابقة إلى ما يلي.

SqlCommand cmd = new SqlCommand("sp_InsertRows", connection);
cmd.CommandType = CommandType.StoredProcedure;

في معظم الحالات، المعلمات ذات قيمة الجدول يكون أداؤها مكافئًا، أو أفضل من تقنيات الدفع الأخرى. غالبًا ما تكون المعلمات ذات القيمة الجدولية أفضل؛ لأنها أكثر مرونة من الخيارات الأخرى. على سبيل المثال: تسمح تقنيات أخرى؛ مثل: نسخة SQL المجمع، بإدخال صفوف جديدة فقط. ولكن مع المحددات ذات القيمة الجدولية، يمكنك استخدام المنطق في الإجراء المخزن؛ لتحديد أي الصفوف هي تحديثات، وأيها يتم إدراجها. يمكن أيضًا تعديل نوع الجدول لاحتواء عمود "عملية" يشير إلى ما إذا كان يجب إدراج الصف المحدد، أو تحديثه، أو حذفه.

يظهر الجدول التالي نتائج اختبار الأقران لاستخدام المعلمات ذات القيمة للجدول بالمللي ثانية.

العمليات محلي إلى Azure (ms) Azure نفس مركز البيانات (مللي ثانية)
1 124 32
10 131 25
100 338 51
1000 2615 382
10000 23830 3586

ملاحظة

النتائج ليست معايير. راجع ⁧⁩الملاحظة حول نتائج التوقيت في هذه المقالة⁧⁩.

إن مكاسب الأداء الناتجة عن الضرب واضحة على الفور. في الاختبار التسلسلي السابق، استغرقت 1000 عملية 129 ثانية خارج مركز البيانات، و21 ثانية من داخل مركز البيانات. ولكن مع المعلمات ذات القيمة الجدولية، تستغرق 1000 عملية 2.6 ثانية فقط خارج مركز البيانات و 0.4 ثانية داخل مركز البيانات.

لمزيد من المعلومات حول المعلمات ذات القيمة الجدولية، راجع ⁧⁩معلمات قيمة الجدول⁧⁩.

SQL نسخة مجمعة

SQL النسخة المجمعة طريقة أخرى لإدراج كميات كبيرة من البيانات في قاعدة بيانات الهدف. يمكن استخدام تطبيقات .NET ⁧⁩الفئة SqlBulkCopy⁧⁩؛ لتنفيذ عمليات إدراج مجمع. تعتبر⁧⁩SqlBulkCopy⁧⁩ مشابهة في وظيفة لأداة سطر الأوامر، ⁧⁩Bcp.exe، ⁧⁩أو عبارة SQL، ⁧⁩BULK INSERT⁧⁩. يوضح مثال التعليمات البرمجية التالي كيفية نسخ الصفوف في جدول ⁧⁩البيانات⁧⁩المصدر، الجدول، إلى جدول الوجهة MyTable.

using (SqlConnection connection = new SqlConnection(CloudConfigurationManager.GetSetting("Sql.ConnectionString")))
{
    connection.Open();

    using (SqlBulkCopy bulkCopy = new SqlBulkCopy(connection))
    {
        bulkCopy.DestinationTableName = "MyTable";
        bulkCopy.ColumnMappings.Add("mytext", "mytext");
        bulkCopy.ColumnMappings.Add("num", "num");
        bulkCopy.WriteToServer(table);
    }
}

هناك بعض الحالات التي يفضل فيها النسخ المجمع على المعلمات ذات القيمة الجدولية. راجع جدول المقارنة من معلمات Table-Valued مقابل عمليات إدراج BULK في المقالة ⁧⁩معلمات قيمة الجدول⁧⁩.

تظهر نتائج الاختبار المخصصة التالية أداء الدفعات مع ⁧⁩SqlBulkCopy⁧⁩ بالمللي ثانية.

العمليات محلي إلى Azure (ms) Azure نفس مركز البيانات (مللي ثانية)
1 433 57
10 441 32
100 636 53
1000 2535 341
10000 21605 2737

ملاحظة

النتائج ليست معايير. راجع ⁧⁩الملاحظة حول نتائج التوقيت في هذه المقالة⁧⁩.

في أحجام دفعات أصغر، تفوقت المعلمات ذات القيمة الجدولية على فئة ⁧⁩SqlBulkCopy.⁧⁩ ومع ذلك، كان أداء ⁧⁩SqlBulkCopy⁧⁩ أسرع بنسبة 12-31٪ من المعلمات ذات القيمة للجدول للاختبارات التي تتكون من 1000 و10000 صف. مثل المعلمات ذات القيمة الجدولية، يعد ⁧⁩SqlBulkCopy⁧⁩ خيارًا جيدًا للإدخالات المجمعة، خاصة عند مقارنته بأداء العمليات غير الدفعية.

لمزيد من المعلومات حول النسخة المجمعة في ADO.NET، راجع ⁧⁩عمليات النسخ المجمع⁧⁩.

جمل INSERT ذات معلمات متعددة الصفوف

أحد البدائل للدفعات الصغيرة هو إنشاء جملة INSERT كبيرة ذات معلمات، والتي تدخل صفوف متعددة. يوضح مثال التعليمات البرمجية التالي هذه التقنية.

using (SqlConnection connection = new SqlConnection(CloudConfigurationManager.GetSetting("Sql.ConnectionString")))
{
    connection.Open();

    string insertCommand = "INSERT INTO [MyTable] ( mytext, num ) " +
        "VALUES (@p1, @p2), (@p3, @p4), (@p5, @p6), (@p7, @p8), (@p9, @p10)";

    SqlCommand cmd = new SqlCommand(insertCommand, connection);

    for (int i = 1; i <= 10; i += 2)
    {
        cmd.Parameters.Add(new SqlParameter("@p" + i.ToString(), "test"));
        cmd.Parameters.Add(new SqlParameter("@p" + (i+1).ToString(), i));
    }

    cmd.ExecuteNonQuery();
}

يهدف هذا المثال إلى إظهار المفهوم الأساسي. قد يتخلل السيناريو الأكثر واقعية الوحدات المطلوبة لإنشاء سلسلة الاستعلام، ومعلمات الأوامر في وقت واحد. أنت محدود بمجموع 2100 معلمات الاستعلام؛ بحيث يحد هذا العدد الإجمالي للصفوف التي يمكن معالجتها بهذه الطريقة.

تظهر نتائج اختبار الأقران التالية أداء هذا النوع من جملة الإدراج بالمللي ثانية.

العمليات المعلمات ذات القيمة الجدولية (مللي ثانية) إدراج عبارة واحدة (مللي ثانية)
1 32 20
10 30 25
100 33 51

ملاحظة

النتائج ليست معايير. راجع ⁧⁩الملاحظة حول نتائج التوقيت في هذه المقالة⁧⁩.

يمكن أن يكون هذا الأسلوب أسرع قليلاً للدفعات التي هي أقل من 100 صف. على الرغم من أن التحسين بسيط، إلا أن هذه التقنية تعد خيارًا آخر يمكن أن يعمل بشكل جيد في سيناريو طلبك المحدد.

DataAdapter

فئة ⁧⁩DataAdapter⁧⁩ تسمح لك بتعديل كائن ⁧⁩DataSet⁧⁩ ثم إرسال التغييرات؛ مثل: إدراج UPDATE وعمليات DELETE. إذا كنت تستخدم ⁧⁩DataAdapter⁧⁩ بهذه الطريقة، فمن المهم ملاحظة إجراء مكالمات منفصلة لكل عملية مميزة. لتحسين الأداء، استخدم خاصية ⁧⁩UpdateBatchSize⁧⁩ إلى عدد العمليات التي يجب أن يتم دفعها في كل مرة. لمزيد من المعلومات، راجع ⁧⁩تنفيذ عمليات الدفعات باستخدام DataAdapters⁧⁩.

Entity Framework

⁩يدعم كيان Framework Core⁧⁩ الدفعات.

XML

للاكتمال، نشعر أنه من المهم الحديث عن XML كإستراتيجية للدفعة. غير أن استخدام نظام XML لا ينطوي على مزايا على الطرق الأخرى وعلى العديد من العيوب. النهج مماثل للمعلمات التي تم تقييمها للجدول؛ ولكن يتم تمرير ملف، أو سلسلة XML إلى إجراء مخزن بدلاً من جدول معرف من قبل المستخدم. الإجراء المخزن يوزع الأوامر في الإجراء المخزن.

وهناك العديد من المساوئ التي قد تترتب على هذا النهج:

  • يمكن أن يكون العمل مع XML مرهقًا، وعرضة للخطأ.
  • يمكن أن يعتمد تحليل XML على وحدة المعالجة المركزية (CPU) مكثفة.
  • في معظم الحالات، يكون هذا الأسلوب أبطأ من المعلمات ذات القيمة الجدولية.

لهذه الأسباب، لا ينصح باستخدام XML لاستعلامات الدفعة.

اعتبارات الدفع

توفر المقاطع التالية إرشادات أكثر لاستخدام الدفعات في قاعدة بيانات azure SQL، وتطبيقات Azure SQL المثيل المدار.

Tradeoffs

وفقًا للبنية المعمارية الخاصة بك، يمكن أن تشتمل عملية الحزم على المفاضلة بين الأداء والمرونة. على سبيل المثال: ضع في اعتبارك سيناريو هبوط دورك بشكل غير متوقع. إذا فقدت صف واحد من البيانات، يكون التأثير أصغر من تأثير فقدان مجموعة كبيرة من الصفوف غير المرسلة. هناك مخاطرة أكبر عند قيامك بتخزين الصفوف مؤقتًا قبل إرسالها إلى قاعدة البيانات في إطار زمني محدد.

بسبب هذه المقايضة، قم بتقييم نوع العمليات التي قمت بدفعها. حزمة أكثر قوة (حزم أكبر، وأطر زمنية أطول) مع بيانات أقل أهمية.

حجم الدفعة

في اختباراتنا، لم يكن هناك عادة أي فائدة لكسر الدفعات الكبيرة إلى قطع أصغر. وفي الواقع، كثيرًا ما أدى هذا التقسيم الفرعي إلى أداء أبطأ من تقديم دفعة كبيرة واحدة. على سبيل المثال: ضع في اعتبارك سيناريو حيث تريد إدراج 1000 صف. يوضح الجدول التالي المدة التي يستغرقها استخدام المعلمات التي لها قيمة الجدول لإدراج 1000 صف عند تقسيمها إلى دفعات أصغر.

حجم الدفعة التكرارات المعلمات ذات القيمة الجدولية (مللي ثانية)
1000 1 347
500 2 355
100 10 465
50 20 630

ملاحظة

النتائج ليست معايير. راجع ⁧⁩الملاحظة حول نتائج التوقيت في هذه المقالة⁧⁩.

يمكنك أن ترى أن أفضل أداء لصفوف 1000 هو تقديم كل منهم في وقت واحد. في اختبارات أخرى (لم يتم عرضها هنا)، كان هناك زيادة بسيطة في الأداء لتجزئة دفعة مكونة من 10000 صف إلى دفعتين مقدارهما 5000. ولكن مخطط الجدول لهذه الاختبارات بسيط نسبيًا؛ لذا يجب عليك إجراء اختبارات على البيانات المحددة، وأحجام الدفعات؛ للتحقق من هذه النتائج.

وهناك عامل آخر يجب وضعه في الاعتبار، وهو أنه إذا أصبحت الدفعة الإجمالية كبيرة جدًا، فقد تقوم قاعدة بيانات Azure SQL، أو Azure SQL Managed Instance بتقييد تنفيذ الدفعة، ورفض تنفيذها. للحصول على أفضل النتائج، اختبر السيناريو الخاص بك؛ لتحديد ما إذا كان هناك حجم دفعي مثالي. قم بجعل حجم الدفعة قابل للتكوين في وقت التشغيل؛ لتمكين عمليات الضبط السريعة استنادًا إلى الأداء أو الأخطاء.

وأخيرًا، قم بموازنة حجم الدفعة مع المخاطر المرتبطة بالدفعات. في حالة وجود أخطاء مؤقتة أو فشل الدور، ضع في الاعتبار عواقب إعادة محاولة العملية، أو فقد البيانات في الدفعة.

المعالجة المُتوازية

ماذا لو اتبعت طريقة تقليل حجم الدفعة؛ ولكنك استخدمت مؤشرات ترابط متعددة لتنفيذ العمل؟ ومرة أخرى، أظهرت الاختبارات التي أجريناها أن العديد من الدفعات الأصغر متعددة مؤشرات الترابط عادة ما يكون أداؤها أسوأ من دفعة واحدة أكبر. يحاول الاختبار التالي إدراج 1000 صف في دفعة متوازية واحدة أو أكثر. يوضح هذا الاختبار كيفية انخفاض أداء الدفعات المتزامنة بشكل فعلي.

حجم الدفعة [التكرارات] مؤشري ترابط (مللي ثانية) أربعة مؤشرات ترابط (مللي ثانية) ستة مؤشرات ترابط (مللي ثانية)
1000 [1] 277 315 266
500 [2] 548 278 256
250 [4] 405 329 265
100 [10] 488 439 391

ملاحظة

النتائج ليست معايير. راجع ⁧⁩الملاحظة حول نتائج التوقيت في هذه المقالة⁧⁩.

هناك عدة أسباب محتملة لتدهور الأداء بسبب التوازي:

  • توجد العديد من مكالمات الشبكة المتزامنة بدلاً من مكالمة واحدة.
  • يمكن أن تؤدي عمليات متعددة مقابل جدول واحد إلى تزاحم وحظر.
  • توجد نفقات زائدة مرتبطة بتعدد العمليات.
  • تتجاوز تكلفة فتح اتصالات متعددة فوائد المعالجة المتوازية.

إذا كنت تستهدف جداول أو قواعد بيانات مختلفة، فمن الممكن رؤية بعض مكاسب الأداء في هذه الإستراتيجية. وسيكون تقسيم قاعدة البيانات أو اتحاداتها سيناريو لهذا النهج. يستخدم Sharding قواعد بيانات متعددة، ويقوم بتوجيه بيانات مختلفة إلى كل قاعدة بيانات. إذا كانت كل دفعة صغيرة في طريقها إلى قاعدة بيانات مختلفة، فإن تنفيذ العمليات بالتوازي يمكن أن يكون أكثر فعالية. ومع ذلك، فإن زيادة الأداء ليست كبيرة بما يكفي لاستخدامها كأساس لاتخاذ قرار لاستخدام تقسيم قواعد البيانات في الحل الخاص بك.

وفي بعض التصميمات، يمكن أن يؤدي التنفيذ المتوازي لدفعات أصغر إلى تحسين معالجة الطلبات في نظام قيد التحميل. في هذه الحالة، على الرغم من أنه من الأسرع معالجة دفعة واحدة أكبر، فإن معالجة دفعات متعددة بالتوازي قد تكون أكثر فعالية.

إذا كنت تستخدم التنفيذ المتوازي، فعليك بالتحكم في الحد الأقصى لعدد مؤشرات الترابط العاملة. قد ينتج عن استخدام عدد أصغر محتوى أقل، ووقت تنفيذ أسرع. خذ بعين الاعتبار أيضًا التحميل الإضافي الذي يضعه هذا على قاعدة البيانات الهدف في كل من الاتصالات والمعاملات.

كما أن الإرشادات النموذجية حول أداء قاعدة البيانات تؤثر على التجميع في دفعات. على سبيل المثال: يتم تقليل أداء الإدراج للجداول التي تحتوي على مفتاح أساسي كبير، أو العديد من الفهارس غير المجمعة.

إذا كانت المعلمات ذات القيمة الجدولية تستخدم إجراءً مخزنًا، يمكنك استخدام الأمر ⁧⁩SET NOCOUNT ON⁧⁩ في بداية الإجراء. يمنع هذا البيان إرجاع عدد الصفوف المتأثرة في الإجراء. ومع ذلك، في اختباراتنا، لم يكن استخدام ⁧⁩SET NOCOUNT ON⁧⁩ له تأثير، أو أداء منخفض. كان إجراء الاختبار المخزن بسيطًا مع أمر ⁧⁩INSERT⁧⁩ مفرد من المعلمة ذات قيم الجدول. ومن الممكن أن تستفيد الإجراءات المخزنة الأكثر تعقيدًا من هذا البيان. ولكن لا تفترض أن إضافة ⁧⁩SET NOCOUNT ON⁧⁩ إلى الإجراء المخزن الخاص بك يحسن الأداء تلقائيًا. لفهم التأثير، اختبر الإجراء المخزن مع وبدون عبارة ⁧⁩SET NOCOUNT ON.⁧

سيناريوهات الدفع

توضح الأقسام التالية كيفية استخدام معلمات ذات قيم جدول في ثلاثة سيناريوهات تطبيق. يظهر السيناريو الأول كيف يمكن للتخزين المؤقت والدفعات العمل معًا. يحسن السيناريو الثاني من الأداء من خلال تنفيذ عمليات التفاصيل الرئيسية في استدعاء إجراء مخزن واحد. يعرض السيناريو النهائي كيفية استخدام معلمات قيم الجدول في عملية "UPSERT".

التخزين المؤقت

على الرغم من وجود بعض السيناريوهات التي قد تكون مرشحة بشكل واضح للدفعات، إلا أن هناك العديد من السيناريوهات التي قد تستفيد من الدفعات نتيجة تأخر المعالجة. غير أن المعالجة المؤجلة تنطوي أيضًا على خطر أكبر يتمثل في فقدان البيانات في حالة حدوث فشل غير متوقع. من المهم فهم هذا الخطر والنظر في العواقب.

على سبيل المثال: خذ بعين الاعتبار تطبيق ويب الذي يتتبع محفوظات التنقل لكل مستخدم. في كل طلب من طلبات الصفحات، يمكن للتطبيق إجراء استدعاء لقاعدة البيانات لتسجيل طريقة عرض صفحة المستخدم. ولكن يمكن تحقيق الأداء الفائق والقابلية للتطوير من خلال التخزين المؤقت لأنشطة التنقل الخاصة بالمستخدمين، ثم إرسال هذه البيانات إلى قاعدة البيانات على دفعات. يمكنك تشغيل تحديث قاعدة البيانات حسب الوقت المنقضي و/ أو حجم المخزن المؤقت. على سبيل المثال: يمكن أن تحدد قاعدة أنه يجب معالجة الدفعة بعد 20 ثانية، أو عندما يصل المخزن المؤقت إلى 1000 عنصر.

يستخدم المثال التعليمات البرمجية التالي ⁧⁩Reactive Extensions - Rx⁧⁩ لمعالجة الأحداث المخزنة مؤقتًا التي تم رفعها بواسطة فئة مراقبة. عندما يتم ملء المخزن المؤقت، أو انتهاء المهلة، يتم إرسال دفعة بيانات المستخدم إلى قاعدة البيانات باستخدام معلمة ذات قيمة جدول.

تعمل فئة NavHistoryData التالية على نماذج تفاصيل تنقل المستخدم. فهو يحتوي على معلومات أساسية؛ مثل: معرف المستخدم، وURL الذي تم الوصول إليه، ووقت الوصول.

public class NavHistoryData
{
    public NavHistoryData(int userId, string url, DateTime accessTime)
    { UserId = userId; URL = url; AccessTime = accessTime; }
    public int UserId { get; set; }
    public string URL { get; set; }
    public DateTime AccessTime { get; set; }
}

الفئة NavHistoryDataMonitor مسؤولة عن تخزين بيانات تنقل المستخدم إلى قاعدة البيانات. يحتوي على أسلوب RecordUserNavigationEntry الذي يستجيب عن طريق رفع حدث ⁧⁩OnAdded.⁧⁩ يظهر الرمز التالي منطق المنشئ الذي يستخدم Rx لإنشاء مجموعة قابلة للملاحظة استنادًا إلى الحدث. ثم تقوم بالاشتراك في هذه المجموعة القابلة للملاحظة باستخدام أسلوب المخزن المؤقت. تحدد زيادة التحميل وجوب إرسال المخزن المؤقت كل 20 ثانية أو 1000 إدخال.

public NavHistoryDataMonitor()
{
    var observableData =
        Observable.FromEventPattern<NavHistoryDataEventArgs>(this, "OnAdded");

    observableData.Buffer(TimeSpan.FromSeconds(20), 1000).Subscribe(Handler);
}

يقوم المعالج بتحويل كافة العناصر المخزنة مؤقتًا إلى نوع ذي قيمة جدول، ثم يقوم بتمرير هذا النوع إلى إجراء مخزن يعالج الدفعة. تظهر التعليمة البرمجية التالية التعريف الكامل لكل من فئتي NavHistoryDataEventArgs و NavHistoryDataMonitor.

public class NavHistoryDataEventArgs : System.EventArgs
{
    public NavHistoryDataEventArgs(NavHistoryData data) { Data = data; }
    public NavHistoryData Data { get; set; }
}

public class NavHistoryDataMonitor
{
    public event EventHandler<NavHistoryDataEventArgs> OnAdded;

    public NavHistoryDataMonitor()
    {
        var observableData =
            Observable.FromEventPattern<NavHistoryDataEventArgs>(this, "OnAdded");

        observableData.Buffer(TimeSpan.FromSeconds(20), 1000).Subscribe(Handler);
    }

يقوم المعالج بتحويل كافة العناصر المخزنة مؤقتًا إلى نوع ذي قيمة جدول، ثم يقوم بتمرير هذا النوع إلى إجراء مخزن يعالج الدفعة. تظهر التعليمة البرمجية التالية التعريف الكامل لكل من فئتي NavHistoryDataEventArgs و NavHistoryDataMonitor.

    public class NavHistoryDataEventArgs : System.EventArgs
    {
        if (OnAdded != null)
            OnAdded(this, new NavHistoryDataEventArgs(data));
    }

    protected void Handler(IList<EventPattern<NavHistoryDataEventArgs>> items)
    {
        DataTable navHistoryBatch = new DataTable("NavigationHistoryBatch");
        navHistoryBatch.Columns.Add("UserId", typeof(int));
        navHistoryBatch.Columns.Add("URL", typeof(string));
        navHistoryBatch.Columns.Add("AccessTime", typeof(DateTime));
        foreach (EventPattern<NavHistoryDataEventArgs> item in items)
        {
            NavHistoryData data = item.EventArgs.Data;
            navHistoryBatch.Rows.Add(data.UserId, data.URL, data.AccessTime);
        }

        using (SqlConnection connection = new SqlConnection(CloudConfigurationManager.GetSetting("Sql.ConnectionString")))
        {
            connection.Open();

            SqlCommand cmd = new SqlCommand("sp_RecordUserNavigation", connection);
            cmd.CommandType = CommandType.StoredProcedure;

            cmd.Parameters.Add(
                new SqlParameter()
                {
                    ParameterName = "@NavHistoryBatch",
                    SqlDbType = SqlDbType.Structured,
                    TypeName = "NavigationHistoryTableType",
                    Value = navHistoryBatch,
                });

            cmd.ExecuteNonQuery();
        }
    }
}

لاستخدام فئة التخزين المؤقت هذه، يقوم التطبيق بإنشاء كائن NavHistoryDataMonitor ثابت. في كل مرة يصل مستخدم إلى صفحة، يستدعي التطبيق أسلوب NavHistoryDataMonitor.RecordUserNavigationEntry. يستمر منطق التخزين المؤقت في مراعاة إرسال هذه الإدخالات إلى قاعدة البيانات على دفعات.

التفاصيل الرئيسية

معلمات قيمة الجدول مفيدة لسيناريوهات INSERT بسيطة. على أي حال، قد يكون من الصعب أكثر للمدرجات المجمعة التي تتضمن أكثر من جدول واحد. ويعد سيناريو "رئيسي/ تفصيلي" مثالاً جيدًا. يحدد الجدول الرئيسي الكيان الأساسي. يخزن جدول تفاصيل واحد أو أكثر مزيدًا من البيانات حول الكيان. في هذا السيناريو، تفرض علاقات المفتاح الخارجي علاقة التفاصيل بكيان رئيسي فريد. خذ بعين الاعتبار إصدارًا مبسطًا من جدول PurchaseOrder، وجدول OrderDetail المقترن به. يقوم Transact-SQL التالي بإنشاء جدول PurchaseOrder الذي يحتوي على أربعة أعمدة: OrderID، وOrderDate، وCustomerID، وStatus.

CREATE TABLE [dbo].[PurchaseOrder](
[OrderID] [int] IDENTITY(1,1) NOT NULL,
[OrderDate] [datetime] NOT NULL,
[CustomerID] [int] NOT NULL,
[Status] [nvarchar](50) NOT NULL,
CONSTRAINT [PrimaryKey_PurchaseOrder]
PRIMARY KEY CLUSTERED ( [OrderID] ASC ))

يحتوي كل طلب على شراء منتج واحد أو أكثر. يتم التقاط هذه المعلومات في جدول PurchaseOrderDetail. ينشئ SQL للعمليات التالية جدول PurchaseOrderDetail مع خمسة أعمدة: OrderID OrderDetailID، وProductID، وUnitPrice، وOrderQty.

CREATE TABLE [dbo].[PurchaseOrderDetail](
[OrderID] [int] NOT NULL,
[OrderDetailID] [int] IDENTITY(1,1) NOT NULL,
[ProductID] [int] NOT NULL,
[UnitPrice] [money] NULL,
[OrderQty] [smallint] NULL,
CONSTRAINT [PrimaryKey_PurchaseOrderDetail] PRIMARY KEY CLUSTERED
( [OrderID] ASC, [OrderDetailID] ASC ))

يجب أن يشير عمود OrderID في جدول PurchaseOrderDetail إلى أمر من جدول PurchaseOrder. يفرض التعريف التالي للمفتاح الخارجي هذا القيد.

ALTER TABLE [dbo].[PurchaseOrderDetail]  WITH CHECK ADD
CONSTRAINT [FK_OrderID_PurchaseOrder] FOREIGN KEY([OrderID])
REFERENCES [dbo].[PurchaseOrder] ([OrderID])

لاستخدام محددات قيم الجدول، يجب أن يكون لديك نوع جدول واحد محدد من قبل المستخدم لكل جدول هدف.

CREATE TYPE PurchaseOrderTableType AS TABLE
( OrderID INT,
    OrderDate DATETIME,
    CustomerID INT,
    Status NVARCHAR(50) );
GO

CREATE TYPE PurchaseOrderDetailTableType AS TABLE
( OrderID INT,
    ProductID INT,
    UnitPrice MONEY,
    OrderQty SMALLINT );
GO

ثم قم بتعريف إجراء مخزن يقبل جداول من هذه الأنواع. يسمح هذا الإجراء للتطبيق بدفع مجموعة من الأوامر، وتفاصيل الطلب محليًا في مكالمة واحدة. يوفر Transact-SQL التالي إعلان بربط الإجراء المخزن بالكامل لمثال أمر الشراء هذا.

CREATE PROCEDURE sp_InsertOrdersBatch (
@orders as PurchaseOrderTableType READONLY,
@details as PurchaseOrderDetailTableType READONLY )
AS
SET NOCOUNT ON;

-- Table that connects the order identifiers in the @orders
-- table with the actual order identifiers in the PurchaseOrder table
DECLARE @IdentityLink AS TABLE (
SubmittedKey int,
ActualKey int,
RowNumber int identity(1,1)
);

-- Add new orders to the PurchaseOrder table, storing the actual
-- order identifiers in the @IdentityLink table
INSERT INTO PurchaseOrder ([OrderDate], [CustomerID], [Status])
OUTPUT inserted.OrderID INTO @IdentityLink (ActualKey)
SELECT [OrderDate], [CustomerID], [Status] FROM @orders ORDER BY OrderID;

-- Match the passed-in order identifiers with the actual identifiers
-- and complete the @IdentityLink table for use with inserting the details
WITH OrderedRows As (
SELECT OrderID, ROW_NUMBER () OVER (ORDER BY OrderID) As RowNumber
FROM @orders
)
UPDATE @IdentityLink SET SubmittedKey = M.OrderID
FROM @IdentityLink L JOIN OrderedRows M ON L.RowNumber = M.RowNumber;

-- Insert the order details into the PurchaseOrderDetail table,
-- using the actual order identifiers of the master table, PurchaseOrder
INSERT INTO PurchaseOrderDetail (
[OrderID],
[ProductID],
[UnitPrice],
[OrderQty] )
SELECT L.ActualKey, D.ProductID, D.UnitPrice, D.OrderQty
FROM @details D
JOIN @IdentityLink L ON L.SubmittedKey = D.OrderID;
GO

في هذا المثال، يخزن الجدول المعرف محليًا ⁧@IdentityLink⁩ قيم OrderID الفعلية من الصفوف المدرجة حديثًا. تختلف معرفات الترتيب هذه عن قيم OrderID المؤقتة في ⁧@orders⁩⁧@details⁩ المعلمات، وقيم الجدول. لهذا السبب، ⁧@IdentityLink⁩يقوم الجدول بعد ذلك بتوصيل قيم OrderID من ⁧@orders⁩المعلمة إلى قيم OrderID الحقيقية للصفوف الجديدة في جدول PurchaseOrder. بعد هذه الخطوة، ⁧@IdentityLink⁩ يمكن للجدول تسهيل إدراج تفاصيل الأمر مع OrderID الفعلي الذي يفي قيد المفتاح الخارجي.

يمكن استخدام هذا الإجراء المخزن من التعليمات البرمجية، أو من مكالمات Transact-SQL الأخرى. راجع مقطع معلمات قيمة الجدول من هذه الورقة؛ للحصول على مثال التعليمات البرمجية. يوضح SQL Transact التالي كيفية استدعاء sp_InsertOrdersBatch.

declare @orders as PurchaseOrderTableType
declare @details as PurchaseOrderDetailTableType

INSERT @orders
([OrderID], [OrderDate], [CustomerID], [Status])
VALUES(1, '1/1/2013', 1125, 'Complete'),
(2, '1/13/2013', 348, 'Processing'),
(3, '1/12/2013', 2504, 'Shipped')

INSERT @details
([OrderID], [ProductID], [UnitPrice], [OrderQty])
VALUES(1, 10, $11.50, 1),
(1, 12, $1.58, 1),
(2, 23, $2.57, 2),
(3, 4, $10.00, 1)

exec sp_InsertOrdersBatch @orders, @details

يتيح هذا الحل لكل دفعة إمكانية استخدام مجموعة من قيم OrderID التي تبدأ في 1. تصف قيم OrderID المؤقتة هذه العلاقات في المجموعة؛ ولكن يتم تحديد قيم OrderID الفعلية في وقت عملية الإدراج. يمكنك تشغيل نفس الجمل في المثال السابق بشكل متكرر، وإنشاء طلبات فريدة في قاعدة البيانات. لهذا السبب، خذ بعين الاعتبار إضافة المزيد من التعليمات البرمجية، أو منطق قاعدة البيانات التي تمنع تكرار الطلبات عند استخدام تقنية الدفع هذه.

يوضح هذا المثال أنه يمكن تجميع عمليات قاعدة البيانات الأكثر تعقيدًا؛ مثل: عمليات التفاصيل الرئيسية، باستخدام معلمات ذات قيمة جدول.

Upsert

يتضمن سيناريو تجميع آخر تحديث الصفوف الموجودة، وإدخال صفوف جديدة في نفس الوقت. يشار إلى هذه العملية أحيانًا باسم عملية "UPSERT" (تحديث + إدراج). بدلاً من إجراء مكالمات منفصلة إلى INSERT وUPDATE، يمكن أن تكون عبارة MERGE بديلاً مناسبًا. يمكن تنفيذ عبارة MERGE عمليات الإدراج والتحديث في مكالمة واحدة. تعمل ميكانيكا تأمين عبارة MERGE بشكل مختلف عن عبارات INSERT وUPDATE المنفصلة. اختبر أحمال العمل الخاصة بك قبل النشر للإنتاج.

يمكن استخدام المعلمات ذات القيمة الجدولية مع عبارة MERGE لإجراء التحديثات وإدراجها. على سبيل المثال: ضع في اعتبارك جدول موظف مبسط، يحتوي على الأعمدة التالية: EmployeeID، وFirstName، وLastName، وSocialSecurityNumber:

CREATE TABLE [dbo].[Employee](
[EmployeeID] [int] IDENTITY(1,1) NOT NULL,
[FirstName] [nvarchar](50) NOT NULL,
[LastName] [nvarchar](50) NOT NULL,
[SocialSecurityNumber] [nvarchar](50) NOT NULL,
CONSTRAINT [PrimaryKey_Employee] PRIMARY KEY CLUSTERED
([EmployeeID] ASC ))

في هذا المثال، يمكنك استخدام حقيقة أن SocialSecurityNumber فريدة من نوعها لتنفيذ دمج من عدة موظفين. أولاً، قم بإنشاء نوع جدول معرف من قبل المستخدم:

CREATE TYPE EmployeeTableType AS TABLE
( Employee_ID INT,
    FirstName NVARCHAR(50),
    LastName NVARCHAR(50),
    SocialSecurityNumber NVARCHAR(50) );
GO

بعد ذلك، قم بإنشاء إجراء مخزن، أو كتابة كود يستخدم عبارة MERGE لتنفيذ التحديث والإدراج. يستخدم المثال التالي عبارة MERGE على معلمة ذات قيمة ⁧@employees⁩ جدول، من نوع EmployeeTableType. ⁧@employees⁩محتويات الجدول غير مبينة هنا.

MERGE Employee AS target
USING (SELECT [FirstName], [LastName], [SocialSecurityNumber] FROM @employees)
AS source ([FirstName], [LastName], [SocialSecurityNumber])
ON (target.[SocialSecurityNumber] = source.[SocialSecurityNumber])
WHEN MATCHED THEN
UPDATE SET
target.FirstName = source.FirstName,
target.LastName = source.LastName
WHEN NOT MATCHED THEN
    INSERT ([FirstName], [LastName], [SocialSecurityNumber])
    VALUES (source.[FirstName], source.[LastName], source.[SocialSecurityNumber]);

لمزيد من المعلومات، راجع الوثائق والأمثلة لعبارة MERGE. على الرغم من إمكانية تنفيذ نفس العمل في استدعاء إجراء مخزن متعدد الخطوات مع عمليات INSERT و UPDATE منفصلة، إلا أن عبارة MERGE أكثر فعالية. يمكن أيضًا إنشاء تعليمات برمجية لقاعدة بيانات استدعاءات transact-SQL التي تستخدم عبارة MERGE مباشرة دون الحاجة إلى استدعاءين قاعدة بيانات INSERT و UPDATE.

ملخص التوصية

تقدم القائمة التالية ملخصًا لتوصيات الدفعات التي نوقشت في هذه المقالة:

  • استخدم التخزين المؤقت والدفعات لزيادة أداء تطبيقات Azure SQL Database، وAzure SQL Managed Instance، وإمكانية تطويرها.
  • فهم المقايضات بين الدفع/ التخزين المؤقت والمرونة. أثناء فشل دور، قد تفوق مخاطر فقدان دفعة غير معالجة من البيانات الهامة للأعمال فائدة الأداء للدفعات.
  • حاول الاحتفاظ بكافة المكالمات إلى قاعدة البيانات داخل مركز بيانات واحد لتقليل زمن الوصول.
  • إذا اخترت تقنية دفع واحدة، فإن المعلمات ذات القيمة الجدولية توفر أفضل أداء ومرونة.
  • للحصول على أداء الإدراج الأسرع اتبع هذه الإرشادات العامة؛ ولكن اختبر السيناريو الخاص بك:
    • بالنسبة لـ< 100 صف، استخدم أحد أوامر INSERT المفردة ذات المعلمات.
    • بالنسبة لعدد < 1000 صف، استخدم معلمات ذات قيم الجدول.
    • بالنسبة إلى >= 1000 صف، استخدم SqlBulkCopy.
  • بالنسبة لعمليات التحديث والحذف، استخدم معلمات قيم الجدول مع منطق الإجراء المخزن الذي يحدد العملية الصحيحة على كل صف في معلمة الجدول.
  • إرشادات حجم الدفعة:
    • استخدم أكبر أحجام الدفعات التي تناسب التطبيقات، ومتطلبات الشركات.
    • موازنة مكاسب الأداء للدفعات الكبيرة مع مخاطر حدوث حالات الفشل المؤقتة أو الكارثية. ما هي نتيجة إعادة المحاولة، أو فقدان البيانات في الدفعة؟
    • قم باختبار أكبر حجم دفعة للتحقق من أن قاعدة بيانات Azure SQL، أو مثيل Azure SQL المدار لا يرفضه.
    • إنشاء إعدادات التكوين التي تتحكم في التجميع في دفعات؛ مثل: حجم الدفعة، أو نافذة وقت التخزين المؤقت. توفر هذه الإعدادات مرونة. يمكنك تغيير سلوك التجميع في دفعات دون إعادة نشر خدمة السحابة.
  • تجنب التنفيذ المتوازي للدفعات التي تعمل على جدول واحد في قاعدة بيانات واحدة. إذا اخترت تقسيم دفعة واحدة عبر مؤشرات ترابط منفذة متعددة، فقم بتشغيل الاختبارات لتحديد العدد المثالي من مؤشرات الترابط. بعد عتبة غير محددة، سيقلل المزيد من مؤشرات الترابط من الأداء بدلاً من زيادته.
  • فكر في التخزين المؤقت على الحجم والوقت كطريقة لتنفيذ التجميع على دفعات لمزيد من السيناريوهات.

الخطوات التالية

ركزت هذه المقالة على كيفية تحسين تقنيات تصميم قواعد البيانات وترميزها المرتبطة بعمليات التجميع لأداء التطبيقات، وإمكانية تطويرها. ولكن هذا مجرد عامل واحد في إستراتيجيتك الشاملة. لمزيد من الطرق لتحسين الأداء، وقابلية التوسع، راجع ⁧⁩إرشادات أداء قاعدة البيانات⁧⁩⁧⁩، واعتبارات السعر والأداء لتجمع مرن.⁧