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

المشكلة
لذا ، نحتاج إلى إصدار جميع الرسائل لطلب البحث "ترقيم الصفحات" ، بدءًا من الأحدث (تم تغيير آخرها من الأعلى ) أو بترتيب صعب. كل شيء على ما يرام ، طالما لدينا أقل من مائة من هذه الرسائل - نقوم فقط بالاختيار من قاعدة البيانات ونعيد البيانات:
طلب من العميل:
GET /message/text=/
طلب قاعدة البيانات:
SELECT FROM Message WHERE text LICENE "" ORDER BY changed DESC
مخطط استجابة JSON للعميل:
Array<{ id : number , text : string }>
لكن عدد الرسائل يتزايد ولدينا المشاكل التالية:
- أصبحت استعلامات قاعدة البيانات أبطأ حيث يجب جمع المزيد من البيانات.
- يستغرق إرسال البيانات عبر الشبكة وقتًا أطول.
- أصبح تقديم هذه البيانات على العميل أطول وأطول.
بدءًا من عتبة معينة ، تصبح التأخيرات كبيرة جدًا بحيث يصبح من المستحيل استخدام موقعنا. إذا ، بالطبع ، لم يستلق بعد ، سئم من عدد كبير من الطلبات الثقيلة الموازية.
أبسط حل ، والذي ربما يتبادر إلى الذهن أولاً ، ويمكنك مواجهته الآن في أي محمصة - لتوزيع البيانات ليس كلها بكميات كبيرة ، ولكن تم تقسيمها إلى صفحات. كل ما علينا القيام به هو مجرد إلقاء معلمة إضافية واحدة من العميل في طلب قاعدة البيانات:
GET /message/text=/page=5/
SELECT FROM Message WHERE text LICENE "" ORDER BY changed DESC SKIP 5 * 10 LIMIT 10
SELECT count(*) FROM Message WHERE text LICENE ""
{ pageItems : Array<{ id : number , text : string }> totalCount : number }
حسنًا ، نعم ، لا يزال يتعين علينا إعادة حساب جميع الرسائل حتى يتمكن العميل من رسم قائمة بالصفحات أو حساب ارتفاع التمرير الافتراضي ، ولكن على الأقل لم نكن بحاجة إلى الحصول على جميع هذه الرسائل 100500 من قاعدة البيانات.
وسيكون كل شيء على ما يرام إذا كان لدينا نوع من المنتدى غير المشهور لفترة طويلة لم تعد الموضوعات ذات الصلة. لكنهم يكتبون ويكتبون إلينا ، ويكتبون ويكتبون ، وبينما يقرأ المستخدم الصفحة الخامسة ، تتغير قائمة الرسائل إلى ما بعد التعرف عليها: يتم إضافة رسائل جديدة وحذف الرسائل القديمة. وبالتالي ، لدينا نوعان من المشاكل من وجهة نظر المستخدم:
- في الصفحة التالية ، قد تظهر الرسائل مرة أخرى على الصفحة السابقة.
- لن يرى المستخدم بعض الرسائل على الإطلاق ، حيث تمكن من الانتقال من الصفحة 6 إلى 5 بالضبط بين انتقال المستخدم من 5 إلى 6.
بالإضافة إلى ذلك ، لا تزال لدينا مشاكل في الأداء. يؤدي كل انتقال إلى الصفحة التالية إلى حقيقة أننا بحاجة إلى إجراء ما يصل إلى استفساري بحث في قاعدة البيانات مع عدد متزايد من العناصر التي تم تخطيها من الصفحات السابقة.
نعم ، والتنفيذ المختص من جانب العميل ليس بهذه البساطة - يجب أن تكون مستعدًا دائمًا لحقيقة أن أي استجابة للخادم يمكن أن تُعيد العدد الإجمالي الجديد للرسائل ، مما يعني أننا سنحتاج إلى إعادة رسم صفحة الترقيم وإعادة التوجيه إلى صفحة أخرى إذا كانت الصفحة الحالية فجأة فارغ. وبالطبع لا يمكنك الوقوع في حالة التكرار.
بالإضافة إلى ذلك ، يحتاج العميل أحيانًا إلى تحديث نتائج البحث ، ولكن سيستمر التحميل في تلقي البيانات التي قد تكون موجودة بالفعل من الطلبات السابقة.
كما ترون ، فإن ترقيم الصفحات لديه العديد من المشاكل. هل حقا لا يوجد حل أفضل؟
الحل
أولاً ، دعنا ننتبه إلى أنه عند العمل مع قاعدة البيانات ، هناك عمليتان مختلفتان بشكل أساسي:
- بحث. عملية ثقيلة نسبيًا للعثور على مؤشرات للبيانات لبعض الاستعلامات.
- أخذ العينات. عملية بسيطة نسبيًا للحصول على البيانات فعليًا.
ستكون مثالية:
- بمجرد البحث ومكان لتذكر نتائجه في شكل لقطة في وقت معين.
- حدد البيانات بسرعة في أجزاء صغيرة حسب الحاجة.
أين يتم تخزين اللقطات؟ هناك خياران:
- على الخادم. ولكن بعد ذلك نسدها بمجموعة من القمامة مع نتائج البحث التي يجب تنظيفها بمرور الوقت.
- للعميل. ولكن يجب عليك بعد ذلك نقل جميع اللقطات على الفور إلى العميل.
دعنا نقدر حجم اللقطة ، وهي مجرد قائمة من المعرّفات. من المشكوك فيه أن المستخدم كان لديه الصبر لتدوير 100 صفحة على الأقل دون استخدام التصفية والفرز. لنفترض أن لدينا 20 عنصرًا في الصفحة. لن يشغل كل معرف أكثر من 10 بايت في تمثيل json. اضرب واحصل على ما لا يزيد عن 20 كيلوبايت. وعلى الأرجح أقل بكثير. سيكون من المعقول وضع حد أقصى لحجم المخرجات في ، على سبيل المثال ، 1000 عنصر.
GET /message/text=/
SELECT id FROM Message WHERE text LICENE "" ORDER BY changed DESC LIMIT 1000
Array<number>
الآن يمكن للعميل أن يرسم على الأقل paginator ، على الأقل تمرير افتراضي ، يطلب بيانات فقط للمعرفات التي تهمه.
GET /message=49,48,47,46,45,42,41,40,39,37/
SELECT FROM Message WHERE id IN [49,48,47,46,45,42,41,40,39,37]
Array< { id : number , text : string } | { id : number , error : string } >
ما نحصل عليه في النهاية:
- Normalized API: البحث بشكل منفصل ، حدد البيانات بشكل منفصل.
- تقليل عدد استعلامات البحث.
- لا يمكنك طلب البيانات التي تم تنزيلها بالفعل ، أو تحديثها في الخلفية.
- رمز بسيط وعالمي نسبيًا من جانب العميل.
من أوجه القصور ، يمكن ملاحظة ما يلي:
- لإظهار شيء يحتاج المستخدم لإجراء طلبين متتاليين على الأقل.
- من الضروري معالجة الحالة عندما يكون المعرف ، ولم تعد البيانات الموجودة عليه متاحة.