حل الاختناقات مع فهارس وأقسام SQL
نشرت: 2022-03-11في الدرس الأول من شرح فهارس SQL ، تعلمنا أن استعلامات SELECT تكون أسرع عندما تكون البيانات مرتبة بالفعل حسب قيم أعمدة معينة.
في الدرس الثاني ، تعلمنا الهيكل الأساسي لفهارس B-tree وكيفية استخدامها لتقليل حجم البيانات التي نصل إليها أثناء تنفيذ استعلام. اكتشفنا أيضًا كيفية تنفيذ الاستعلامات التي تربط بين جداول متعددة وكيف يمكن للفهارس تسريع مثل هذه الاستعلامات.
لقد أبرزنا أيضًا سيناريوهين حيث يكون استخدام الفهارس في SQL مفيدًا. عندما تغطي الفهارس الفهارس ، وتحتوي على جميع الأعمدة من الاستعلام - من شروط WHERE ، وشروط JOIN ، وقائمة SELECT - نتجنب قراءة الجدول المقابل بالكامل. بدلاً من ذلك ، يمكن أن تساعد الفهارس عندما تقلل عدد كتل البيانات التي يتم الوصول إليها إلى جزء صغير من حجم الجدول.
بخلاف ذلك ، يكون فحص الجدول بأكمله أكثر فاعلية من القراءة من فهرس والانتقال عشوائيًا ذهابًا وإيابًا إلى صفوف الجدول المقابلة.
استعلامات نطاق SQL
تتضمن الاستعلامات التي يمكنها الاستفادة من الفهارس عادةً الشروط التي تقلل بشكل ملحوظ نطاق القيم المحتملة التي يمكن أن يتخذها عمود واحد أو أكثر. تقيد استعلامات النطاق البيانات بناءً على شروط مثل "يجب أن تكون قيمة العمود A بين X و Y."
وخير مثال على ذلك هو الاستعلام من التمرين 4 للدرس الثاني:
SELECT c.ClientName FROM Reservations r JOIN Clients c ON r.ClientID = c.ClientID WHERE r.DateFrom BETWEEN ( TO_DATE('2020-08-13', 'YYYY-MM-DD') AND TO_DATE('2020-08-14', 'YYYY-MM-DD') ) AND r.HotelID = 3; هنا لدينا مجالان. الأول هو نطاق التواريخ ، الفترة بين 13 أغسطس 2020 و 14 أغسطس 2020. والثاني هو أصغر نطاق رقمي ممكن. الشرط يعادل r.HotelID BETWEEN 3 AND 3 .
التمرين 1: الفترات (استعلامات النطاق الزمني والتاريخ)
دعنا نضيف عمودًا يسمى CheckInTime إلى جدول Reservations . يمكنك مشاهدة عينة من البيانات في جدول البيانات هذا. لاحظ أن هناك فهرسًا واحدًا يغطي كلاً من CheckInTime و ClientId .
اكتب استعلامًا يعرض أسماء العملاء الذين سجلوا الوصول في 15 أغسطس 2020.
عادةً ما يكتب مطورو SQL عديمي الخبرة الاستعلام التالي:
SELECT c.ClientName FROM Reservations r JOIN Clients c ON r.ClientID = c.ClientID WHERE TO_DATE(r.CheckInTime, 'YYYY-MM-DD') = '2020-08-15';يفترضون أن تنفيذ الاستعلام سيبدو كما يلي:
Get first row from IX_CheckInTime_ClientID where TO_DATE(CheckInTime, 'YYYY-MM-DD') = '2020-08-15' While found and TO_DATE(CheckInTime, 'YYYY-MM-DD') = '2020-08-15' Fetch Clients.* where ClientID = IX_CheckInTime_ClientID.ClientID Write down Clients.ClientName Get next row from IX_CheckInTime_ClientID تكمن المشكلة في عدم وجود نظام RDBMS واحد في وقت كتابة هذا التقرير قادرًا على إنشاء خطة التنفيذ هذه. يرون TO_DATE (بناء جملة Oracle) كدالة تحول قيمة العمود CheckInTime إلى شيء غير مفهرس. لذا ، فإن خطط التنفيذ التي يميلون إلى إنشائها تبدو كما يلي:
For each row from IX_CheckInTime_ClientID If TO_DATE(CheckInTime, 'YYYY-MM-DD') = '2020-08-15' then Fetch Clients.* where ClientID = IX_CheckInTime_ClientID.ClientID Write down Clients.ClientName سيكون تنفيذ هذا أسرع من قراءة جميع الصفوف من جدول Reservations لأن صف الفهرس أضيق من صف الجدول. يعني الصف الأصغر أنه يلزم الوصول إلى كتل أقل من القرص.
ومع ذلك ، فإننا نعلم أن خطة التنفيذ الأولى ستكون أكثر كفاءة. لإقناع نظام RDBMS الخاص بنا باستخدام هذا النهج ، نحتاج إلى إعادة كتابة الاستعلام:
SELECT c.ClientName FROM Reservations r JOIN Clients c ON r.ClientID = c.ClientID WHERE r.CheckInTime >= TO_DATE('2020-08-15 00:00:00', 'YYYY-MM-DD HH:MI:SS') AND r.CheckInTime < TO_DATE('2020-08-16 00:00:00', 'YYYY-MM-DD HH:MI:SS'); هذا استعلام نطاق مناسب ، وهو استعلام يفهمه كل RDBMS جيد. يكتشف نظام RDBMS الخاص بنا أننا نريد بيانات من جدول Reservations حيث تنتمي قيمة CheckInTime - وليس شيئًا مشتقًا منه - إلى النطاق المحدد جيدًا. ستكون خطة التنفيذ التي تنشئها أشبه بما يلي:
Get first row from IX_CheckInTime_ClientID where CheckInTime >= '2020-08-15 00:00:00' While found and CheckInTime < '2020-08-16 00:00:00' Fetch Clients.* where ClientID = IX_CheckInTime_ClientID.ClientID Write down Clients.ClientName Get next row from IX_CheckInTime_ClientIDهذا ما نريده حقًا: الاستفادة ليس فقط من الفهرس نفسه ولكن أيضًا من حقيقة ترتيبه.
التمرين 2: LIKE استخدام حرف البدل في البداية
هذه المرة يأتي المحقق الخاص بنا إلى الفندق بمعلومات غامضة عن المشتبه به: فقط أن الاسم الأخير ينتهي بـ "-son". يريد المحقق الاسم الأول والأخير لجميع هؤلاء الضيوف:
SELECT FirstName, LastName FROM Clients WHERE LastName LIKE '%son'; بالنسبة لجدول Clients وفهرس في LastName ، سنستخدم جدول البيانات هذا. اكتب النتائج التي سيعود بها الاستعلام. فكر في الأساليب المختلفة التي يمكنك تطبيقها.
نهج مسح الجدول
أبسط إستراتيجية هي قراءة جميع البيانات من الجدول وكتابة أسماء الضيوف عندما تنتهي أسمائهم الأخيرة بـ "-son":
For each row from Clients If LastName like '%son' then write down FirstName, LastNameهنا ، علينا قراءة الجدول بأكمله بالتسلسل.
باستخدام فهرس
دعنا نحاول الاستفادة من الفهرس الموجود في عمود LastName . انتقل إلى ورقة IX_LastName ، واستخدمها للعثور على جميع العملاء الذين يستوفون المعيار المحدد ، واكتب أسمائهم.
اتضح أنه يجب عليك قراءة الفهرس بأكمله للعثور على جميع Andersons و Robinsons و Thompsons من الجدول. هل هذا أفضل من استخدام فحص الجدول؟ بالإضافة إلى قراءة الفهرس بالكامل ، كان عليك أيضًا أن تبحث ، لكل إدخال مطابق ، عن الصف المقابل من الجدول باستخدام قيمة rowAddress ، ثم اكتب FirstName من هناك:
For each row from IX_LastName If LastName like '%son' then Fetch Clients.* where RowAddress = IX_LastName.RowAddress Write down FirstName, LastNameبالنسبة لنا ، كان من الأسهل والأسرع قراءة الجدول بالتتابع. بالنسبة لنظام RDBMS الخاص بنا ، فإنه سيعتمد على النسبة المئوية للصفوف التي تفي بالمعايير. إذا كان هناك عدد قليل فقط من Andersons و Robinsons و Thompsons في جدول كبير ، فإن RDBMS سيقرأ كتل بيانات أقل من إدخالات فهرس أضيق بكثير حتى لو كان عليه قراءة كتل قليلة من الجدول عند العثور على تطابق. خلاف ذلك ، يستغرق فحص الجدول وقتًا أقل.
لا يقدم طلب البيانات في الفهرس أي مساعدة في مثل هذا الاستعلام. يمكن أن يكون الحجم الأصغر لصف الفهرس مفيدًا - ولكن في بعض الأحيان فقط.
التمرين 3: LIKE استخدام حرف البدل في النهاية
في المرة القادمة التي يأتي فيها المحقق ، نحتاج إلى العثور على جميع العملاء الذين تبدأ أسماؤهم بـ "Rob-."
SELECT FirstName, LastName FROM Clients WHERE LastName LIKE 'Rob%';حاول استخراج البيانات المطابقة للاستعلام من نفس جدول البيانات.
إذا استخدمت نهج فحص الجدول ، فقد فاتتك فرصة الاستفادة الكاملة من فهرس IX_LastName . من الأسرع تحديد موقع الإدخال الأول من الفهرس بدءًا من "Rob-" (Roberts) ، وقراءة الصفوف اللاحقة (كل من Robertses و Robinsons) ، والتوقف عندما لا يتطابق LastName مع المعيار:
Get first row from IX_LastName where LastName <= 'Rob' While found and LastName < 'Roc' Fetch Clients.* where rowAddress = IX_LastName.rowAddress Write down FirstName, LastName Get next from IX_LastNameفي هذه الحالة ، بعد البحث B-tree عن الإدخال الأول ، نقرأ فقط الإدخالات التي تفي بالمعيار. نتوقف عن القراءة بمجرد أن نقرأ اسمًا لا يتطابق مع المعيار.
معالجة مشاكل تحجيم B-Tree
عادةً عندما ننشر قاعدة بيانات جديدة ، هناك عدد قليل من جداول البحث المعبأة وجداول المعاملات الفارغة. يعمل النظام بسلاسة من البداية ، خاصة إذا احترمنا الممارسات الجيدة لتصميم قاعدة البيانات من خلال تسوية الجداول ؛ إنشاء مفاتيح أساسية وأجنبية وفريدة من نوعها ؛ ودعم المفاتيح الخارجية بالفهارس المقابلة.
بعد بضعة أشهر أو سنوات ، عندما زاد حجم البيانات بشكل كبير من تعقيد النظام وقاعدة البيانات ، نبدأ في ملاحظة تدهور الأداء. تظهر آراء حول سبب تباطؤ النظام وماذا تفعل حيال ذلك.
غالبًا ما يكون الرأي السائد هو أن حجم قاعدة البيانات هو الجاني الأساسي. يبدو أن الحل هو إزالة البيانات التاريخية التي لا نحتاجها يوميًا ووضعها في قاعدة بيانات منفصلة لإعداد التقارير والتحليلات.
دعنا نستكشف الافتراض الرئيسي أولاً.
استعلامات نطاق SQL: هل يعتمد وقت التنفيذ على حجم الجدول؟
ضع في اعتبارك استعلام نطاق نموذجي من جدول واحد:
SELECT Column1, …, ColumnN FROM Table WHERE Column BETWEEN X AND Y; بافتراض وجود فهرس في Column ، فإن خطة التنفيذ المثلى هي:
Get first row from IX_Column where Column between X and Y While found and Column <= Y Fetch Table.* where rowAddress = IX_Column.rowAddress Write down Column1, …, ColumnN Get next row from IX_Columnدعنا نحسب الكتل التي سيتعين على RDBMS قراءتها لإرجاع هذه البيانات.
يتم تنفيذ جزء Get first row من خلال بحث B-tree الذي قدمناه في الدرس الثاني. عدد الكتل التي يجب أن تقرأها يساوي عمق الشجرة B. بعد ذلك ، نقرأ العناصر اللاحقة من مستوى الورقة في الفهرس.
باستخدام استعلامات OLTP ، عادةً ما يتم العثور على جميع النتائج داخل كتلة فهرس واحدة (أحيانًا اثنتين ولكن نادرًا ما تكون أكثر). بالإضافة إلى ذلك ، لكل إدخال فهرس ، لدينا وصول إلى كتلة في الجدول للعثور على الصف المقابل بناءً على عنوانه. قد تكون بعض صفوف الجدول ضمن نفس كتلة الجدول التي قمنا بتحميلها بالفعل ، ولكن لتبسيط التقدير ، دعنا نفترض أننا نقوم بتحميل كتلة جديدة في كل مرة.
إذن الصيغة هي:
ب = د + 1 + ص
B هو إجمالي عدد الكتل المقروءة ، و D هو عمق الشجرة B ، و R هو عدد الصفوف التي تم إرجاعها بواسطة الاستعلام.
المعلمة الوحيدة التي تعتمد على عدد الصفوف في الجدول هي D ، عمق الشجرة B.
لتبسيط العمليات الحسابية وتوضيح نقطة ، افترض أن 1000 مدخل فهرس تتناسب مع كتلة واحدة. د = 1 طالما أن هناك أقل من 1000 صف في الجدول. بالنسبة للجداول التي تحتوي على معاملات تجارية ، قد يكون هذا هو الحال في أول يوم عمل بعد نشر النظام. قريباً ، سيزداد عمق الشجرة B. طالما أن هناك أقل من مليون صف في الجدول ، فسيتكون المؤشر من مستويين.
إذا أزعجنا أوقات استجابة قاعدة البيانات البطيئة وألقي باللوم على حجم البيانات في ذلك ، فلاحظ أن جداول المعاملات غالبًا ما تحتوي على ملايين الصفوف فقط. نظرًا لأن مليون صف فقط تتلاءم مع فهرس شجرة B من مستويين ، يجب أن يكون العمق ثلاثة على الأقل. لن ينمو العمق إلى أربعة إلا إذا كان هناك أكثر من مليار صف في الجدول. الآن لدينا تقدير أكثر دقة:
ب = 4 + ص
إذا كانت R صغيرة ، فإن خفض عمق B-tree مرة أخرى إلى اثنين من شأنه أن يسرع الاستعلام بشكل كبير. عندما نبحث عن قيمة مفتاح أساسية أو فريدة ، سيقرأ النظام أربع كتل بدلاً من خمسة ، وهو ما يمثل تحسنًا بنسبة 20٪. إذا أرجع الاستعلام المزيد من الصفوف ، فقد لا يكون التحسن ملحوظًا. تكمن المشكلة ، بالنسبة للعديد من التطبيقات ، في أننا قد لا نكون قادرين على دعم العمليات التجارية المطلوبة من خلال الاحتفاظ بأقل من مليون معاملة في قاعدة البيانات.

لذا يبدو أن الاستنتاج هو أن حجم الجدول لا يهم ؛ بمعنى آخر ، يعد نقل البيانات التاريخية مضيعة للوقت والموارد.
لكن ليس بهذه السرعة: دعنا نتعلم المزيد عن بنية فهرس B-tree وكيف تؤثر تغييرات البيانات عليه.
تفاصيل تنفيذ مؤشر B- شجرة
في تغطيتنا لمؤشرات B-tree في الدرس الثاني ، رأينا أن جميع مستويات الشجرة المتوازنة مرتبة (ماديًا) حسب قيم العمود الأساسي. ومع ذلك ، عندما نريد إدراج عنصر أو تحديثه أو حذفه ، فغالبًا ما يتعين علينا نقل كمية كبيرة من البيانات للحفاظ على الطلب.
لنفترض أننا ندخل في منتصف الكتلة التي تصادف أنها ممتلئة. يتعين علينا تقسيم الكتلة وإعادة ترتيب البيانات وأحيانًا تحديث البيانات على مستوى شجرة B آخر مشيرًا إلى المستوى الحالي.
لجعل مثل هذه الحالات أكثر فاعلية ، يحتوي كل عنصر فهرس على مؤشرات للصفين السابق والتالي ، مما يجعله مرتبطًا بشكل مزدوج. للإدراج بشكل عام ، هذا يعني أننا نكتب العناصر الجديدة في أقرب وقت ممكن من العنصر السابق ونصحح المؤشرات.
عندما نحتاج إلى تقسيم كتلة أيضًا ، يجب أن نكتب عنصرًا جديدًا في مستوى B-tree السابق. هذه مجرد مسألة تصحيح بعض المؤشرات - لا داعي لإعادة كتابة أجزاء كبيرة من الشجرة. بعد الانقسام ، تصبح كلتا كتلي البيانات نصف ممتلئة تقريبًا. اعتمادًا على المكان الذي توجد فيه مساحة خالية على القرص ، يمكن أن تكون الكتل "المجاورة" بعيدة جدًا فعليًا.
بعد مرور بعض الوقت ، يزداد تجزئة الفهرس ويصبح تباطؤ تنفيذ الاستعلام ملحوظًا. مع تنفيذ RDBMS للاستعلامات بالطريقة التي وصفناها بها ، يصبح افتراض ترتيب العناصر وقربها أقل صحة ، مما يؤدي إلى المزيد من القراءة. في أسوأ الحالات ، مع كون جميع كتل البيانات نصف فارغة ، يتعين على النظام قراءة ضعف عدد الكتل.
صيانة مؤشر B- شجرة
وعلاج هذا هو إلغاء تجزئة الفهرس (أو "إعادة الفهرسة"). يوفر كل RDBMS ميزة لإعادة إنشاء فهرس كامل ؛ بعد إعادة الفهرسة ، يتم ترتيب الفهارس فعليًا مرة أخرى.
تعد Reindexing عملية سريعة جدًا ، على الرغم من أنها تقرأ وتكتب حجمًا كبيرًا من البيانات. تقدم أنظمة RDBMS الحديثة عادةً وضعين لإعادة الفهرسة ، حيث يتطلب الوضع الأسرع قفل الجداول أثناء المعالجة. في كلتا الحالتين ، يُفضل إعادة الفهرسة في غير ساعات الذروة. خلاف ذلك ، قد تؤدي المعالجة إلى إبطاء أداء قاعدة البيانات.
حذف البيانات التاريخية
عندما يكون لدينا جداول بالمليارات أو حتى مئات الملايين من الصفوف ، فقد لا يكون من الممكن إكمال عملية إعادة الفهرسة خلال ساعات الذروة.
لتجنب هذا الموقف ، قد يكون نقل البيانات التاريخية من قاعدة بيانات OLTP هو الحل. ومع ذلك ، إذا قمنا ببساطة بحذف الصفوف الأقدم من عتبة معينة ، فإننا نجعل الفهارس مجزأة بشكل أكبر ، ونحتاج إلى إعادة فهرستها بشكل أكثر تكرارًا.
SQL التقسيم إلى الإنقاذ؟
هناك طريقة لتجنب التجزئة الناتجة عن إزالة البيانات التاريخية ، مع الاحتفاظ بالمعاملات "النشطة" فقط في قاعدة بيانات الإنتاج. الفكرة التي تقوم بتنفيذها جميع أنظمة RDBMS الرئيسية هي تقسيم الجدول إلى أجزاء أصغر (تسمى الأقسام ) وتوفير القدرة على إضافتها وإزالتها وحتى تبديلها بين الجداول (على سبيل المثال ، من جدول نشط إلى جدول تاريخي له نفس الشيء. هيكل).
دعنا نلقي نظرة على جدول Reservations كما هو مقسم في جدول البيانات هذا. يتم تقسيم الجدول حسب الشهر ، مع تعيين أسماء الأقسام لفترات التاريخ وجداول البيانات الأخرى. لمعرفة كيفية تنفيذ استعلام على جدول مقسم ، سنقوم ببعض التمارين.
التمرين 4: استعلام التقسيم في SQL
من جدول البيانات المرتبط أعلاه ، حاول استخراج البيانات المطلوبة بواسطة الاستعلام التالي - دون استخدام أي فهارس:
SELECT HotelID, ReservationID, ClientID, DateFrom, DateTo FROM Reservations WHERE DateFrom BETWEEN TO_DATE('2021-03-01','YYYY-MM-DD') AND TO_DATE('2021-03-03');ربما اكتشفت أنه عليك أولاً إلقاء نظرة على ورقة تعيين الأقسام والعثور على القسم الذي يحتوي على حجوزات من مارس 2021. بعد ذلك ، قمت بفتح القسم المقابل ، وقراءة البيانات بالتسلسل ، وتصفية الصفوف التي لا تلبي شرط.
على الرغم من كونه واضحًا ، فمن المحتمل أنك لا تحب الاحتفاظ بعدد قليل من الصفوف بعد قراءة الكثير. كانت قراءة قسم مارس أفضل من قراءة جدول الحجز بالكامل ولكنها لا تزال غير مثالية. ماذا عن الفهارس؟
الفهارس العالمية
تسمح لنا أنظمة RDBMS بإنشاء فهرس عالمي لتغطية جميع أقسام الجدول المقسم. ولكن لا يوجد فرق بين كيفية عمل الفهارس العمومية والعادية تحتها: الفهارس العالمية ليست على دراية بالقسم. وبالتالي ، لا تتضمن استعلامات CRUD التي تستخدم فهرسًا عامًا خريطة التقسيم لجدولها.
نحتاج فقط إلى تحديث خريطة التقسيم عندما نسقط قسمًا كاملاً. يتعين علينا بعد ذلك إزالة أي صفوف من الفهرس تشير إلى القسم الذي تمت إزالته. هذا يعني أن المؤشر العالمي بأكمله بحاجة إلى إعادة البناء.
تظل نافذة الانقطاع ضرورية لأنه لا يمكن استخدام الفهارس حتى تتم إزالة العناصر القديمة. إذا تمكنا من إسقاط الأقسام بانتظام ، والحد من عدد الأقسام النشطة ، فقد تتناسب عملية إعادة الفهرسة مع نافذة الانقطاع. لذا فإن استخدام الأقسام يساعد في حل المشكلة الأصلية عن طريق تقصير الوقت اللازم لمهام الصيانة ، بما في ذلك صيانة الفهارس العالمية.
ولكن ماذا لو كنا لا نزال غير قادرين على تحمل الانقطاع؟
الفهارس المقسمة عالميا
هذه الإستراتيجية تحل هذه المشكلة: نحن ببساطة نقسم الفهرس بنفس الطريقة التي نقسم بها الجدول. في جداول البيانات التي يرتبط بها جدول بيانات القسم ، يحتوي كل قسم على الجزء الخاص به من جدول Reservations وورقة فهرس تسمى IX_DateFrom ، وكلاهما مقسم بواسطة DateFrom .
لتنفيذ الاستعلام من التمرين 4 ، سيبحث RDBMS أولاً في خريطة أقسام الفهرس ويحدد الأقسام التي تحتوي على تواريخ من النطاق. (في حالتنا ، إنه قسم فهرس واحد فقط.) بعد ذلك ، سيستخدم عمليات البحث B-tree ، والتنقل عبر مستوى الورقة ، والوصول أخيرًا إلى الجدول باستخدام عنوان الصف المقابل.
عندما نسقط قسمًا من الجدول ، يكفي إسقاط القسم المقابل من الفهرس. لا حاجة للتوقف عن العمل.
فهارس محلية
يتمثل العيب الرئيسي في الفهارس المقسمة عالميًا في أنه يتعين علينا الاهتمام بإسقاط كل من الجدول وقسم الفهرس المقابل. لا يوجد سوى تكلفة إضافية طفيفة مرتبطة بالقراءة من خريطة أقسام الفهرس والحفاظ عليها.
تتضمن الفهارس المحلية نهجًا مشابهًا ولكنه مختلف قليلاً. بدلاً من تقسيم فهرس عالمي واحد ، نقوم بإنشاء فهرس محلي داخل كل قسم من أقسام الجدول. من خلال القيام بذلك ، تشترك الفهارس المحلية في الميزة الرئيسية للفهارس المقسمة عالميًا - أي عدم حدوث تعطل - مع تجنب عيوبها.
يبدو أنه حل مثالي. ولكن قبل الاحتفال ، دعنا نتحرى عن خطة التنفيذ المحتملة لبعض الاستفسارات.
التمرين 5: الفهرس المقسم محليًا
حاول تشغيل الاستعلام مرة أخرى ، هذه المرة باستخدام الفهرس المقسم محليًا في DateFrom .
ربما استخدمت خطة التنفيذ هذه:
For all partitions where [StartDateFrom, StartDateTo) intersects ['2021-03-01', '2021-03-03'] Get first row from IX_DateFrom where DateFrom between '2021-03-01' and '2021-03-03' While found and DateFrom < '2021-03-04' Fetch Reservations.* where RowAddress = IX_DateFrom.RowAddress Write down HotelID, ReservationID, ClientID, DateFrom, DateTo Get next row from IX_DateFromنحن محظوظون لأن جميع التواريخ تنتمي إلى قسم واحد ، لذلك كان علينا اجتياز فهرس محلي واحد فقط. إذا كانت الفترة ستة أشهر ، فسنضطر إلى قراءة ستة فهارس محلية.
التمرين 6: على النقيض
مهمتك هي استخدام خريطة قسم الحجوزات مرة أخرى ، هذه المرة لعمل قائمة بالفترات التي زار فيها العميل 124 فندق 1:
SELECT DateFrom, DateTo FROM Reservations WHERE ClientID = 124 AND HotelID = 1; هنا يمكننا أن نرى العيب الرئيسي للفهارس المحلية. كان علينا قراءة ورقة الفهرس المحلية IX_HotelID_CientID من كل قسم في جدول Reservations :
For all partitions Get first row from IX_HotelID_ClientID where ClientID = 124 and HotelID = 1 While found and ClientID = 124 and HotelID = 1 Fetch Reservations.* where RowAddress = IX_HotelID_ClientID.RowAddress Write down DateFrom, DateTo Get next row from IX_HotelID_ClientIDسيؤدي هذا التنفيذ بوضوح إلى قراءة المزيد من الكتل وسيستغرق وقتًا أطول مما لو لم يتم تقسيم الجدول.
لذلك ، بينما وجدنا طريقة للحفاظ على صحة مؤشراتنا خلال فترة خارج الذروة ، جعلت الإستراتيجية أيضًا بعض استفساراتنا أبطأ.
إذا كان نموذج أعمالنا يسمح لنا بالاحتفاظ بعدد صغير من الأقسام أو على الأقل تحتوي الاستعلامات الأكثر شيوعًا على معايير تسمح لنظام RDBMS بقراءة قسم واحد أو قسمين فقط ، فقد يكون هذا الحل هو ما نحتاج إليه. بخلاف ذلك ، من الأفضل تجنب التقسيم والعمل على تحسين نموذج البيانات والفهارس والاستعلامات - وتحسين خادم قاعدة البيانات.
الفهارس في SQL: ما يجب تعلمه بعد ذلك
هذه نهاية رحلتنا. في شرح فهارس SQL ، ركزت على تطبيق الفهرس المشترك لجميع أنظمة RDBMS الحديثة. ركزت أيضًا على الموضوعات التي يهتم بها مطورو التطبيقات ، على حساب الموضوعات التي تتعلق عادةً بمسؤولي قواعد البيانات. من الأفضل أن يقوم الأخير ببحث تأثير عامل التعبئة على تجزئة الفهرس ، ولكن من المرجح أن يجد الأشخاص في كلا الدورين أنه من المفيد قراءة المزيد حول:
- التخزين المؤقت للبيانات والفهرس
- هياكل فهرس ليست من النوع B ، مثل التجزئة ، و GiST ، والصورة النقطية ، وفهارس مخزن الأعمدة
- الفهارس العنقودية (المعروفة بالجداول المنظمة بواسطة الفهرس في Oracle)
- فهارس وظيفية
- فهارس جزئية
نهج التقسيم الذي ناقشناه هو تقسيم النطاق . إنه أكثر أنواع التقسيم شيوعًا ، ولكن هناك أنواع أخرى ، مثل تقسيم التجزئة وتقسيم القائمة. أيضًا ، توفر بعض أنظمة RDBMS خيار مستويات متعددة من التقسيم.
أخيرًا ، من الأفضل لمطوري SQL استكشاف موضوعات مهمة أخرى حول تنفيذ استعلام RDBMS - أولاً ، تحليل الاستعلام ، ثم تجميع خطة التنفيذ القائمة على التكلفة والتخزين المؤقت وإعادة الاستخدام.
بالنسبة لـ RDMBSs الأربعة التي لدي خبرة بها ، أوصي بهذه الموارد كخطوات تالية:
وحي
- نظرة عامة على المُحسِّن
- الفهارس والجداول المنظمة
- إدارة الفهارس
- نظرة عامة على الأقسام
- دليل التقسيم
- اسأل توم
PostgreSQL
- معالجة الاستعلام
- الفهارس في PostgreSQL
- الفهارس في PostgreSQL (التوثيق الرسمي)
- إدارة العازلة
- تقسيم الجدول
- دليل التقسيم
خادم مايكروسوفت SQL
- هندسة معالجة الاستعلام
- فهارس
- جداول وفهارس مقسمة
MySQL / MariaDB
- فهم خطة تنفيذ الاستعلام
- التحسين والفهارس
- التقسيم - الأساسيات
- التقسيم - التوثيق
- وثائق MariaDB: تحسين الاستعلام والفهارس
