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

قبل أن تلمس إعدادات الخادم، قم بمراجعة استعلاماتك بشكل منهجي.

ابدأ بالتشخيص الصحيح

لا تقم بالتحسين في الظلام. إعادة كتابة الاستعلامات بشكل عشوائي هي مجرد تخمين، والتخمين يهدر الساعات.

تحتاج إلى العثور على الجمل التي تستهلك أكبر قدر من الوقت الإجمالي. راقب تطبيقك تحت ضغط العمل الحقيقي. يوفر لك Laravel Telescope رؤية واضحة لكل استعلام يتم تنفيذه أثناء الطلب، مع توقيت التنفيذ. يقوم Laravel Debugbar بإظهارها في متصفحك أثناء التطوير المحلي حتى تتمكن من رصد أي حالات شاذة فوراً. وعندما تحتاج إلى رصد المشكلات في بيئة الإنتاج، قم بتفعيل MySQL Slow Query Log؛ فهو يسجل الجمل التي تتجاوز حداً معيناً تحدده أنت، مما يجعله مثالياً للعثور على المفاجآت التي لا تظهر في مجموعات البيانات الصغيرة. وإذا كنت تشغل شيئاً أكبر، يمكن لأداة مراقبة أداء التطبيقات (APM) ربط نقاط نهاية HTTP البطيئة باستدعاءات قاعدة بيانات محددة.

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

توقف عن طلب كل شيء

استخدام SELECT * مريح، ولكنه مكلف أيضاً. عندما تكتب Model::all() أو تجلب مجموعة نتائج دون تسمية الأعمدة، يقوم MySQL بسحب كل حقل لكل صف مطابق. يتضمن ذلك حقول النصوص الكبيرة، وJSON blobs، وأي شيء آخر موجود في الجدول. تنمو مجموعة النتائج، ويرتفع استهلاك الذاكرة، ويزداد الوقت المستغرق في تسلسل (serializing) الاستجابة.

كن صريحاً. إذا كان المتحكم (controller) الخاص بك يحتاج فقط إلى حقول id و name و email فقم بطلب تلك الحقول تحديداً:

User::select('id', 'name', 'email')->get();

في منشئ الاستعلامات (query builder)، ينطبق المبدأ نفسه. الحمولات (payloads) الأصغر تنتقل بشكل أسرع عبر الشبكة وتستهلك ذاكرة وصول عشوائي (RAM) أقل في خادم التطبيق الخاص بك. هذا أحد أسهل الحلول وأقلها تكلفة، ومع ذلك من السهل تجاهله لأن Laravel يجعل SELECT * هو السلوك الافتراضي.

اجعل EXPLAIN يوجه تغييراتك

لا تقم أبداً بإعادة هيكلة (refactor) استعلام بطيء دون تشغيل EXPLAIN أولاً. في MySQL، تُظهر الكلمة المفتاحية EXPLAIN خطة تنفيذ الاستعلام، فهي تكشف بالضبط كيف ينوي المُحسِّن (optimizer) العثور على بياناتك.

انتبه لعمود type. إذا رأيت ALL فهذا يعني أن MySQL يقوم بعمل مسح كامل للجدول (full table scan)، أي أنه يقرأ كل صف لتلبية شرط WHERE الخاص بك. انظر إلى عمود key لترى ما إذا كان المُحسِّن يستخدم فهرساً (index) على الإطلاق. ثم تحقق من عمود Extra؛ إذا لاحظت Using temporary أو Using filesort فهذا يعني أن MySQL يقوم ببناء جداول وسيطة أو الفرز في الذاكرة لأن هيكلك الحالي لا يمكنه تلبية الاستعلام بسلاسة.

قم بتشغيل EXPLAIN في عميل MySQL الخاص بك، أو استخدم أداة تقوم بتنسيق المخرجات لك. بمجرد رؤية الخطة، ستعرف ما إذا كانت المشكلة هي فقدان فهرس، أو عملية ربط (join) سيئة، أو شرط (predicate) لا يمكن للمحرك تحسينه. عندها يصبح التخمين غير ضروري.

استخدم الفهارس عن قصد

الفهارس هي أقوى أداة لتسريع عمليات البحث، لكنها تعمل فقط عندما تتوافق مع طريقة استعلامك. بدون الفهرس الصحيح، يقوم MySQL بالمسح صفاً بصف. قد يبدو الأمر جيداً في مرحلة التطوير على جدول يحتوي على ألف صف، ثم ينهار في بيئة الإنتاج على جدول يحتوي على عشرة ملايين صف.

ابدأ بفهارس العمود الواحد للحقول التي تظهر بشكل متكرر في جمل WHERE. إذا كنت تقوم بالفلترة باستمرار حسب status فقم بإضافة فهرس على status.

عندما يقوم الاستعلام بالفلترة على أعمدة متعددة معاً، انتقل إلى الفهارس المركبة (composite indexes). ترتيب الأعمدة داخل الفهرس مهم لأن MySQL يقرأ الفهارس المركبة من اليسار إلى اليمين، ويُعرف هذا باسم قاعدة البادئة اليسرى (leftmost prefix rule). إذا كان استعلامك يبحث بواسطة user_id ثم يرتب حسب created_at فإن فهرساً مركباً على (user_id, created_at) سيساعد بشكل كبير. إذا عكست الترتيب، فقد لا يستخدم المُحسِّن الفهرس للفلترة على الإطلاق.

لا تقم بفهرسة كل عمود. فكل فهرس يضيف عبئاً (overhead) على عمليات الإدراج والتحديث والحذف لأن MySQL يجب أن يحافظ على الهيكل. أضفها عن قصد بناءً على الأنماط التي وجدتها أثناء القياس.

ابعد الدوال عن الأعمدة

يؤدي هذا الخطأ إلى تعطيل الفهارس بصمت. عندما تضع عموداً داخل دالة ضمن جملة WHERE ، فإن MySQL لا تستطيع