إذا كان تطبيقك بطيئًا، فالمشكلة غالبًا ليست في إطار العمل. المشكلة الحقيقية تكمن دائمًا في قاعدة البيانات. لقد أمضيت سنوات في بناء أنظمة خلفية معقدة. في كل مشروع، كان تحسين أداء قواعد البيانات هو الحل الأقوى والأكثر تأثيرًا.
الساعة الثانية صباحًا يوم الإثنين. موعد إطلاق منصة لعميل تجارة إلكترونية كان في التاسعة. كلما حاولنا تحميل لوحة المبيعات، يتجمد النظام لثلاثين ثانية ثم ينهار. اعتقد فريقنا أن المشكلة في الخادم. ضاعفنا موارده دون أي جدوى تذكر. كنت محبطًا أراقب شاشة الخطأ. تساءلت كيف سأبرر هذا التأخير الكارثي للعميل في الصباح.
في تلك اللحظة، قررت إيقاف التخمين العشوائي. تركت محاولات التخزين المؤقت وفتحت سطر الأوامر. استهدفت جذر المشكلة مباشرة. استخدمت أداة EXPLAIN ANALYZE داخل محرك PostgreSQL لفحص ما يحدث. النتيجة كانت صادمة تمامًا. النظام يقرأ مائتي ألف سجل لاستخراج عشرين نتيجة فقط. السبب كان استعلامات معقدة مخفية خلف إطار العمل.
أضفت فهرسًا مركبًا بسيطًا وأعدت كتابة الاستعلام. النتيجة الفورية كانت انخفاض زمن الاستجابة من ثلاث ثوانٍ إلى 400 مللي ثانية. حققت تحسينًا تجاوز 60% بفضل سطر كود واحد. تم الإطلاق بنجاح. المهارة الحقيقية تكمن في التشخيص الدقيق. لهذا بنيت منصة Hcouch Digital، لأن المطورين العرب يستحقون هذه الاختصارات التقنية.
- 1 تحليل الاستعلامات والتوصيف: اعرف قبل أن تحسّن
- 2 استراتيجيات الفهرسة: التغيير الأعلى تأثيرًا
- 3 إعادة هيكلة الاستعلامات: إصلاح ما يخطئ به ORM
- 4 إعادة تصميم المخطط لتحسين الأداء
- 5 تجميع الاتصالات والنسخ المتماثلة للقراءة
- 6 طبقات التخزين المؤقت: أسرع استعلام هو الذي لا يتم
- 7 خدعة تتبع الذاكرة المؤقتة: ما تتجاهله أدوات ORM
- 8 التميز في الأداء الهندسي
تحليل الاستعلامات والتوصيف: اعرف قبل أن تحسّن

الخطأ الأكبر الذي يرتكبه المطورون هو التخمين العشوائي. يضيفون الفهارس عشوائيًا أو يعيدون كتابة الأكواد بحدسهم المسبق. أبدأ كل عملية تحسين بخطوة واحدة حاسمة وهي التوصيف الدقيق (Profiling).
1.1 تفعيل تسجيل الاستعلامات البطيئة
يجب أن تعرف ما الذي يستغرق وقتًا طويلاً. في PostgreSQL، أقوم بتعديل ملف التهيئة postgresql.conf. أضبط القيمة log_min_duration_statement على 200 مللي ثانية. هكذا أسجل فقط الاستعلامات البطيئة وأتجاهل الباقي.
في محرك MySQL، أفعل خيار slow_query_log وأحدد السقف الزمني بدقة. في أحد مشاريعي السابقة، راجعت سجل الاستعلامات البطيئة أسبوعيًا بانتظام. اكتشفت فرص تحسين لم تكن لتظهر في مراجعة الكود العادية. الاستعلامات التي تبدو نظيفة كانت تولد خطط تنفيذ كارثية.
1.2 استخدام EXPLAIN ANALYZE بفعالية
الأمر EXPLAIN يعرض خطة الاستعلام النظرية فقط. لكن EXPLAIN ANALYZE ينفذ الاستعلام ويعرض بيانات التوقيت الفعلية. الفرق بينهما جوهري للغاية في بيئة الإنتاج.
واجهت استعلامًا يبدو نظيفًا لكنه أجرى مسحًا تسلسليًا (Sequential Scan). كان يقرأ 200 ألف صف لفلترة 187 ألفًا منها. استخدمت هذه الأداة لتحويل التحسين من تخمين إلى هندسة دقيقة. هذا الجانب التشخيصي يقودنا مباشرة إلى الحل الأكثر تأثيرًا في الخطوة التالية.
استراتيجيات الفهرسة: التغيير الأعلى تأثيرًا
الفهرسة هي التقنية الأقوى في صندوق أدواتي كمطور. فهرس واحد في مكانه الصحيح يختصر ثانيتين إلى 5 مللي ثانية. لكن الفهارس تستهلك مساحة التخزين وتبطئ عمليات الكتابة بشكل ملحوظ.
2.1 الفهارس المركبة والفهارس الجزئية
ترتيب الأعمدة في الفهارس المركبة (Composite Indexes) مهم جدًا. الفهرس على الحالة وتاريخ الإنشاء يفيد عند الفلترة بهما معًا. في نظام توظيف، أنشأت فهرسًا مركبًا ألغى المسح التسلسلي بالكامل.
انخفض زمن التنفيذ من 1843 مللي ثانية إلى 12 مللي ثانية. استخدمت أيضًا الفهارس الجزئية (Partial Indexes) بحذر. قمت بفهرسة الوظائف النشطة فقط في النظام. قلل هذا حجم الفهرس بنسبة 85% وسرّع عمليات القراءة بشكل هائل.
2.2 الفهارس الشاملة (Index-Only Scans)
الفهرس الشامل يتضمن كل الأعمدة التي يطلبها الاستعلام. هذا يسمح لمحرك PostgreSQL بخدمة الطلب من الفهرس مباشرة. لا حاجة للمس الجدول الفعلي (Heap) أبدًا.
هذا الإجراء يلغي عمليات الإدخال والإخراج العشوائية المكلفة. الهدف دائمًا هو الوصول إلى نتيجة Heap Fetches: 0. حينها يحقق النظام أقصى سرعة ممكنة دون استهلاك موارد إضافية.
2.3 منهجية الفهرسة المنهجية
أتبع عملية منهجية واضحة في كل مشروع. أستخرج أبطأ 20 استعلامًا من السجل المتوفر. أطبق أمر EXPLAIN ANALYZE على كل واحد منها بدقة.
أبحث عن المسح التسلسلي في الجداول التي تتجاوز 10 آلاف سجل. أراقب أداء الكتابة بعد إضافة الفهارس المستهدفة للتأكد من استقرارها. هذه المنهجية قلصت زمن الاستعلام الإجمالي بنسبة 60%. لكن الفهارس وحدها لا تكفي إذا كان الكود المولد سيئًا كما سنرى تاليًا.
إعادة هيكلة الاستعلامات: إصلاح ما يخطئ به ORM

أدوات ORM ممتازة للإنتاجية وتوفير الوقت. لكنها غالبًا ما تولد كود SQL غير مثالي إطلاقًا. أواجه هذه المشاكل الخفية في معظم المشاريع التي أستلمها. هنا ندرك أهمية مراجعة الأكواد المولدة، ويمكن أن تساعدك أدوات البرمجة الذكية في رصد هذه الأخطاء مبكرًا.
3.1 القضاء على مشكلة N+1
مشكلة N+1 هي الأشهر والأكثر تدميرًا للأداء. يقوم النظام باستعلام واحد رئيسي، ثم استعلام لكل نتيجة فرعية. في إطار Laravel، أستخدم التحميل المسبق (Eager Loading) عبر دالة with.
إذا كنت أحتاج العدد فقط، أستخدم دالة withCount. في منصة مالية، كان تحميل صفحة يستغرق 3 ثوانٍ بسبب 150 استعلامًا. أعدت الهيكلة باستخدام التحميل المسبق. انخفض العدد إلى 8 استعلامات وزمن الاستجابة إلى 400 مللي ثانية.
3.2 استبدال الاستعلامات الفرعية بـ JOINs و CTEs
تولد أدوات ORM أحيانًا استعلامات فرعية مرتبطة (Correlated Subqueries). هذا يعني تنفيذ الاستعلام الفرعي مرة لكل صف مستخرج. أستبدل هذه الكوارث البرمجية بعمليات JOIN أو عبارات CTE المتقدمة.
هذا التعديل يسمح لقاعدة البيانات بمعالجة البيانات في تمريرة واحدة. النتيجة هي استهلاك أقل لوحدة المعالجة المركزية وسرعة استجابة أعلى. المطور المحترف يكتب SQL بيده عند تعقيد الأمور.
3.3 استخدام دوال النافذة (Window Functions)
احتجت لعرض اتجاهات البيانات شهريًا في إحدى المنصات التحليلية. التنفيذ الأولي استخدم استعلامات متعددة وتجميعًا عبر جافا سكريبت. استبدلت ذلك باستعلام واحد يستخدم دوال النافذة (Window Functions).
هذا نقل عبء المعالجة إلى محرك قاعدة البيانات المحسن لذلك. انخفض زمن استجابة واجهة برمجة التطبيقات من ثانيتين إلى 280 مللي ثانية. هذا التغيير في الاستعلامات يمهد الطريق لتعديلات هيكلية أعمق في المخطط.
إعادة تصميم المخطط لتحسين الأداء
أحيانًا لا تكون المشكلة في الاستعلام نفسه بل في المخطط. يُدرَّس التطبيع (Normalization) كقاعدة مقدسة في الجامعات. لكن الأنظمة الحقيقية تتطلب إلغاء تطبيع استراتيجي للعمل بكفاءة عالية.
4.1 العروض المادية (Materialized Views)
في لوحة تحكم تحليلية، احتجت لتجميع بيانات من خمسة جداول. رغم الفهارس، كان التجميع بطيئًا جدًا للوقت الفعلي. قدمت العروض المادية (Materialized Views) كحل جذري وفعال.
أقوم بتخزين نتائج الاستعلام المعقد مسبقًا في جدول منفصل. أعددت مهمة مجدولة (Cron Job) لتحديث العرض كل 15 دقيقة. استعلام اللوحة الذي استغرق 3.8 ثانية، أصبح يكتمل في 10 مللي ثانية.
4.2 إلغاء التطبيع الاستراتيجي
لتسريع صفحات الملفات الشخصية، تجنبت ربط خمسة جداول في كل طلب. أضفت عمود JSONB لتخزين الإحصائيات المجمعة مباشرة. يتم تحديث هذا العمود عبر وظيفة خلفية كل ساعة.
هناك مقايضة هنا، فقد تكون البيانات قديمة بساعة واحدة. لكن في هذه الحالة، التأخر الطفيف مقبول تمامًا للمستخدم. المكسب في الأداء كان هائلاً، حيث انخفض الزمن إلى 8 مللي ثانية. هذا التصميم يقلل الضغط، لكننا نحتاج لإدارة الاتصالات مع تزايد الزيارات.
تجميع الاتصالات والنسخ المتماثلة للقراءة

عند التوسع، لا يكفي تحسين الاستعلامات وحدها. يجب إدارة كيفية تعامل التطبيق مع اتصالات قاعدة البيانات. إنشاء اتصال جديد مكلف جدًا ويستهلك موارد الخادم بسرعة.
5.1 تجميع الاتصالات مع PgBouncer
ينشئ محرك PostgreSQL عملية جديدة لكل اتصال قادم. في أوقات الذروة، كان تطبيقنا يصل للحد الأقصى للاتصالات وتنهار الخدمة. نشرت أداة PgBouncer أمام قاعدة البيانات في وضع تجميع المعاملات.
هذا الإعداد سمح لـ 500 اتصال من التطبيق بمشاركة 25 اتصالاً فعليًا. استقر النظام تمامًا واختفت أخطاء رفض الاتصال نهائيًا. إدارة الاتصالات هي خط الدفاع الأول ضد ارتفاع الزيارات المفاجئ.
5.2 النسخ المتماثلة للقراءة (Read Replicas)
معظم تطبيقات الويب تعتمد على القراءة بكثافة عالية. قمت بفصل عمليات القراءة والكتابة عبر قواعد بيانات متعددة. توجه عمليات الإدخال (INSERT) إلى القاعدة الأساسية دائمًا.
بينما توجه استعلامات البحث والتقارير إلى النسخ المتماثلة. هذا الفصل قلل الحمل على القاعدة الأساسية بنسبة 70%. وأتاح لنا التوسع الأفقي بسهولة. هذه الخطوة ضرورية قبل التفكير في الطبقة الأخيرة من التحسين.
طبقات التخزين المؤقت: أسرع استعلام هو الذي لا يتم
بعد تحسين كل شيء في قاعدة البيانات، نصل لخط الدفاع الأخير. أسرع استعلام هو الذي لا تضطر لتنفيذه أبدًا. التخزين المؤقت هو الطبقة النهائية في استراتيجيتي الهندسية.
6.1 استخدام Redis للبيانات الساخنة
أستخدم Redis لتخزين البيانات التي تُطلب بشكل متكرر. أتحقق أولاً مما إذا كانت البيانات موجودة في الذاكرة. إذا كانت كذلك، أردها فورًا دون لمس قاعدة البيانات.
إذا لم تكن موجودة، أجلبها من القاعدة وأحفظها في Redis. أضع دائمًا وقت انتهاء صلاحية (TTL) لتجنب استهلاك الذاكرة. هذه الخطوة تخفف الحمل عن الخادم بشكل يضمن استقرار المنصة.
6.2 استراتيجيات إبطال ذاكرة التخزين المؤقت
إبطال التخزين المؤقت (Cache Invalidation) تحدٍ هندسي كبير ومعقد. أعتمد نهجًا عمليًا يجمع بين وقت الصلاحية والأحداث المباشرة. للبيانات التحليلية، أقبل ببيانات قديمة لربع ساعة دون مشكلة.
للبيانات الحساسة، أستخدم إبطالاً مبنيًا على الأحداث. عند تحديث سجل، أطلق حدثًا يمسح المفتاح المرتبط به فورًا. لمزيد من التفاصيل، راجع دليل استراتيجيات تحسين قواعد البيانات الذي يطابق هذه المنهجية بدقة.
خدعة تتبع الذاكرة المؤقتة: ما تتجاهله أدوات ORM
في أحد المشاريع المعقدة مع وكالة TwiceBox، واجهت استعلامًا يبدو سريعًا عند اختباره محليًا. لكنه كان يخنق قاعدة البيانات تمامًا في بيئة الإنتاج. كان EXPLAIN ANALYZE يعطيني زمن تنفيذ منخفضًا ومقبولاً. لكن الخادم كان يستهلك كامل قدرة وحدة المعالجة المركزية.
اكتشفت أن المشكلة لم تكن في الزمن، بل في الذاكرة. بدأت أستخدم أمر EXPLAIN (ANALYZE, BUFFERS) في تحليلاتي. هذه الإضافة البسيطة غيرت كل شيء في طريقة تشخيصي. أظهرت لي كمية البيانات التي يقرأها الاستعلام من الذاكرة ومن القرص.
كان الاستعلام يقرأ آلاف الصفحات من الذاكرة المؤقتة لإنتاج عشرة صفوف فقط. أعدت كتابة الاستعلام لتقليل هذه القراءات الوهمية بشكل جذري. انخفض استهلاك المعالج بنسبة 40% فورًا واستقر الخادم. لا تكتفِ بمراقبة الزمن فقط، بل راقب استهلاك الذاكرة لكل استعلام.
التميز في الأداء الهندسي
تحسين قاعدة البيانات ليس مشروعًا لمرة واحدة، بل انضباط هندسي مستمر. الاستعلام السريع اليوم قد يصبح عنق زجاجة غدًا مع تزايد البيانات. راقب أداء نظامك، واستخدم الفهارس بذكاء، ولا تثق دائمًا بالأكواد المولدة آليًا.
ما هي الأداة التي تستخدمها حاليًا لمراقبة الاستعلامات البطيئة في نظامك؟
اكتشاف المزيد من أشكوش ديجيتال
اشترك للحصول على أحدث التدوينات المرسلة إلى بريدك الإلكتروني.



