مرحبا بالجميع! أنا مطور خلفية ، أكتب خدمات microservices في Java + Spring. أنا أعمل في أحد فرق تطوير المنتجات الداخلية في Tinkoff.

يطرح فريقنا في كثير من الأحيان مسألة تحسين الاستعلام في DBMS. تريد دائمًا أسرع قليلاً ، لكن لا يمكنك دائمًا الحصول على فهارس مصممة جيدًا - يجب عليك البحث عن بعض الحلول. خلال أحد هذه التجوالات حول الشبكة بحثًا عن تحسينات معقولة عند العمل مع قاعدة البيانات ، وجدت المدونة المفيدة بلا حدود لماركوس فيناند ، مؤلف كتاب "شرح أداء SQL". هذا هو النوع النادر جدًا من المدونات حيث يمكنك قراءة جميع المقالات على التوالي.
أريد أن أترجم لك مقالة قصيرة كتبها ماركوس. يمكن أن يطلق عليه ، إلى حد ما ، بيان يسعى إلى لفت الانتباه إلى القضية القديمة ولكن لا تزال ذات صلة بأداء عملية الإزاحة وفقًا لمعيار SQL.
في بعض الأماكن ، سأكمل المؤلف بتفسيرات وملاحظات. سأخصص كل هذه الأماكن كـ "تقريبًا". من أجل الوضوح.
مقدمة صغيرة
أعتقد أن الكثير من الناس يعرفون مدى صعوبة العمل مع تحديدات الصفحات من خلال الإزاحة. ولكن هل تعلم أنه يمكن استبدالها ببساطة مع تصميم أكثر إنتاجية؟
لذلك ، تخبر الكلمة الأساسية "الإزاحة" قاعدة البيانات بتخطي الإدخالات n الأولى في الطلب. ومع ذلك ، لا يزال يتعين على قاعدة البيانات قراءة هذه السجلات n الأولى من القرص ، وفي الترتيب المحدد (ملاحظة: تطبيق الفرز إذا تم تحديد واحد) ، وبعد ذلك فقط سيكون من الممكن إرجاع السجلات التي تبدأ من n + 1 فصاعدًا. الشيء الأكثر إثارة للاهتمام هو أن المشكلة ليست في التنفيذ الملموس في DBMS ، ولكن في التعريف الأولي وفقًا للمعيار:
... يتم فرز الصفوف أولاً وفقًا لـ <ترتيب بحسب جملة> ثم يتم تحديدها عن طريق إسقاط عدد الصفوف المحددة في <شرط تعويض النتيجة> من البداية ...
-SQL: 2016 ، الجزء 2 ، 4.15.3 الجداول المشتقة (ملاحظة: الآن المعيار الأكثر استخدامًا)
النقطة الأساسية هنا هي أن الإزاحة تتطلب معلمة واحدة - عدد السجلات التي يجب تخطيها ، وهذا هو. باتباع هذا التعريف ، يمكن لنظام إدارة قواعد البيانات الحصول على جميع السجلات فقط ثم تجاهل السجلات غير الضرورية. من الواضح أن هذا التعريف للتعويض يفرض عليك القيام بعمل إضافي. ولا يهم حتى إذا كانت SQL أو NoSQL.
بعض المزيد من الألم
لا تنتهي مشاكل الإزاحة ، ولهذا السبب. إذا أدخلت عملية أخرى سجلاً جديداً بين قراءة صفحتين من البيانات من القرص ، فما الذي سيحدث في هذه الحالة؟

عند استخدام الإزاحة لتخطي السجلات من الصفحات السابقة ، في حالة إضافة سجل جديد بين عمليات قراءة الصفحات المختلفة ، فمن المحتمل أن تحصل على تكرارات (ملاحظة: هذا ممكن عندما نقرأ صفحة بالصفحة باستخدام الترتيب حسب البناء ، ثم في منتصف الإخراج لدينا الحصول على رقم قياسي جديد).
يوضح الشكل بوضوح مثل هذا الموقف. تقرأ القاعدة السجلات العشرة الأولى ، وبعد ذلك يتم إدراج سجل جديد ، والذي يحول جميع السجلات المقروءة بمقدار 1. ثم تأخذ القاعدة صفحة جديدة من السجلات العشرة التالية وتبدأ ليس من الحادية عشر كما ينبغي ، ولكن من العاشرة ، مكررة هذا السجل. هناك حالات شاذة أخرى مرتبطة باستخدام هذا التعبير ، ولكن هذا هو الأكثر شيوعا.
كما اكتشفنا بالفعل ، هذه ليست مشكلات خاصة بقواعد بيانات إدارة قواعد البيانات أو تنفيذها. المشكلة هي تعريف ترقيم الصفحات وفقًا لمعيار SQL. نقول لـ DBMS الصفحة التي يجب الحصول عليها أو عدد السجلات التي يجب تخطيها. الأساس ببساطة غير قادر على تحسين مثل هذا الطلب ، لأن هناك القليل من المعلومات لهذا الغرض.
يجدر أيضًا توضيح أن هذه ليست مشكلة كلمات رئيسية محددة ، وإنما هي دلالات الاستعلام. هناك العديد من بناء الجملة متطابقة من حيث إشكالية:
- الكلمة الأساسية الإزاحة ، كما ذكر سابقًا.
- حد بناء كلمتين رئيسيتين [إزاحة] (على الرغم من أن الحد نفسه ليس سيئًا للغاية).
- التصفية حسب الحدود الأدنى بناءً على ترقيم الخطوط (على سبيل المثال ، row_number () ، rownum ، إلخ).
كل هذه التعبيرات تشير ببساطة إلى عدد الخطوط التي يجب تخطيها ، أو عدم وجود معلومات أو سياق إضافي.
لاحقًا في هذه المقالة ، يتم استخدام الكلمة الأساسية "الإزاحة" كتعميم لكل هذه الخيارات.
الحياة بدون OFFSET
الآن تخيل كيف سيكون عالمنا دون كل هذه المشاكل. اتضح أن الحياة بدون إزاحة ليست معقدة للغاية: يمكنك فقط تحديد تلك السطور التي لم نرها (ملاحظة: أي تلك التي لم تكن موجودة في الصفحة الأخيرة) باستخدام الشرط في المكان.
في هذه الحالة ، نبني على حقيقة أنه يتم تنفيذ التحديدات على مجموعة مرتبة (ترتيب قديم جيد). نظرًا لأن لدينا مجموعة مرتبة ، يمكننا استخدام عامل تصفية بسيط إلى حد ما للحصول على البيانات الموجودة خلف السجل الأخير من الصفحة السابقة فقط:
SELECT ... FROM ... WHERE ... AND id < ?last_seen_id ORDER BY id DESC FETCH FIRST 10 ROWS ONLY
هذا هو المبدأ الكامل لهذا النهج. بالطبع ، عند الفرز حسب العديد من الأعمدة ، يصبح كل شيء أكثر متعة ، لكن الفكرة هي نفسها. من المهم ملاحظة أن هذا البناء قابل للتطبيق على العديد من حلول N o S Q L.
هذا النهج يسمى طريقة البحث أو ترقيم الصفحات. يحل المشكلة مع نتيجة عائمة (ملاحظة: الموقف من الكتابة بين قراءة الصفحة ، الموصوفة سابقًا) ، وبالطبع ، نحن نحب جميعًا ، يعمل بشكل أسرع وأكثر استقرارًا من الإزاحة الكلاسيكية. يكمن الاستقرار في حقيقة أن وقت معالجة الاستعلام لا يزداد بما يتناسب مع عدد الجدول المطلوب (ملاحظة: إذا كنت تريد معرفة المزيد حول عمل الطرق المختلفة لترقيم الصفحات ، فيمكنك الاطلاع على عرض تقديمي للمؤلف . ويمكنك أيضًا العثور على علامات مقارنة للطرق المختلفة).
تخبر إحدى الشرائح أن ترقيم الصفحات الرئيسي ، بالطبع ، ليس كلي القدرة - له حدوده الخاصة. الأهم من ذلك - ليس لديها القدرة على قراءة الصفحات العشوائية (ملاحظة: بشكل غير متسق). ومع ذلك ، في عصر التمرير الذي لا نهاية له (ملاحظة: في النهاية الأمامية) ، هذه ليست مشكلة. يعد تحديد رقم الصفحة للنقرة قرارًا سيئًا على أي حال عند تطوير واجهة مستخدم (ملاحظة: رأي مؤلف المقال).
ماذا عن الأدوات؟
غالبًا ما يكون ترقيم الصفحات غير مناسب بسبب عدم وجود دعم فعال لهذه الطريقة. معظم أدوات التطوير ، بما في ذلك الأطر المختلفة ، لا تعطي الخيار في طريقة تنفيذ ترقيم الصفحات.
يتفاقم الموقف بسبب حقيقة أن الطريقة الموصوفة تتطلب دعمًا من طرف إلى آخر في التقنيات المستخدمة - من DBMS إلى تنفيذ طلب AJAX في المستعرض مع التمرير اللانهائي. بدلاً من تحديد رقم الصفحة فقط ، يجب عليك الآن تحديد مجموعة من المفاتيح لجميع الصفحات مرة واحدة.
ومع ذلك ، فإن عدد الأطر التي تدعم ترقيم الصفحات الرئيسية يتزايد تدريجياً. هنا ما هو في الوقت الحالي:
(ملاحظة: تمت إزالة بعض الروابط نظرًا لأنه في وقت الترجمة لم يتم تحديث بعض المكتبات من 2017-2018. إذا كنت مهتمًا ، يمكنك إلقاء نظرة على المصدر.)
في هذه اللحظة هناك حاجة لمساعدتكم. إذا كنت تقوم بتطوير أو دعم إطار يستخدم ترقيم الصفحات بطريقة أو بأخرى ، فأنا أطلب ، وأحث ، وأدعو لك لتقديم دعم محلي لترقيم الصفحات. إذا كانت لديك أسئلة أو كنت بحاجة إلى مساعدة ، فسيسعدني تقديم المساعدة ( المنتدى ، Twitter ، نموذج الاتصال ) (ملاحظة: في تجربتي مع Marcus ، يمكنني القول إنه متحمس حقًا لنشر هذا الموضوع).
إذا كنت تستخدم حلولًا جاهزة تعتقد أنها جديرة بدعم ترقيم الصفحات ، فعليك إنشاء طلب أو حتى تقديم حل جاهز ، إن أمكن. يمكنك أيضًا تحديد هذه المقالة في الرابط.
استنتاج
إن السبب وراء عدم انتشار هذا النهج البسيط والمفيد مثل ترقيم الصفحات الرئيسية هو أنه من الصعب في التنفيذ الفني أو يتطلب بعض الجهد الكبير. السبب الرئيسي هو أن الكثيرين اعتادوا على الرؤية والعمل مع الإزاحة - وهذا النهج تمليه المعايير نفسها.
نتيجة لذلك ، قليل من الناس يفكرون في تغيير طريقة ترقيم الصفحات ، ولهذا السبب ، فإن الدعم الفعال من الأطر والمكتبات يتطور بشكل سيء. لذلك ، إذا كنت على مقربة من فكرة وهدف ترقيم الصفحات بلا ضجة ، ساعد في نشرها!
المصدر: https://use-the-index-luke.com/no-offset
بواسطة: ماركوس ويناند