كانت استعلاماتنا صندوقاً أسود: كيف أنقذنا الأمر ‘EXPLAIN’ من جحيم التحسين الأعمى؟

“يا جماعة الخير، الموقع راح يوقع!”

أذكرها وكأنها البارحة. كنا في خضم إطلاق ميزة جديدة في أحد المشاريع الكبيرة، ميزة انتظرها المستخدمون بفارغ الصبر. الساعات الأولى كانت احتفالية، الأرقام في ارتفاع، والكل مبسوط. لكن مع زيادة الضغط، بدأت تظهر أعراض الكارثة: بطء شديد في تحميل صفحة رئيسية تعتمد على استعلام (Query) معقد. بدأت رسائل الخطأ تظهر، والمستخدمون يشتكون، والمدير، الله يسامحه، “ولّعت معو” وبدأ يضغط على الفريق.

اجتمعنا كفريق، وبدأ “مهرجان الاقتراحات”:

  • “يمكن لازم نزيد الـ RAM على سيرفر قاعدة البيانات!”
  • “لأ، المشكلة في الكود، خلينا نعمل Caching لكل شيء!”
  • “شو رأيكم نعمل Restart للسيرفر؟ مرات بتزبط!”

كانت كل هذه الاقتراحات بمثابة إطلاق نار في الظلام. كنا نتعامل مع استعلاماتنا كصندوق أسود غامض؛ نرسل له طلباً، وننتظر الإجابة، وإذا تأخرت، لا نعرف السبب الحقيقي. في تلك اللحظة، أوقفت الجميع وقلت لهم: “يا جماعة، اهدوا شوي. قبل ما نكب مصاري ووقت على حلول عشوائية، خلينا نسأل قاعدة البيانات نفسها: شو القصة؟ كيف بتنفذي هاد الاستعلام؟”. وهنا، كان بطل قصتنا، الأمر البسيط العظيم: EXPLAIN.

ما هو الأمر `EXPLAIN`؟ ولماذا هو صديقك الصدوق؟

ببساطة شديدة، الأمر EXPLAIN (أو EXPLAIN ANALYZE في بعض قواعد البيانات مثل PostgreSQL) هو أداة تشخيصية تطلب من قاعدة البيانات أن تشرح لك “خطة التنفيذ” (Execution Plan) التي ستتبعها لتنفيذ استعلامك. بدلاً من أن تنفذ الاستعلام فعلياً، هي تخبرك بالخطوات التي ستتخذها، الجداول التي ستزورها، الفهارس (Indexes) التي ستستخدمها (أو لن تستخدمها!)، وتقديرها لعدد الأسطر التي ستحتاج لمسحها في كل خطوة.

تخيل أنك تريد الذهاب من نقطة (أ) إلى نقطة (ب) في مدينة مزدحمة. بدلاً من أن تقود سيارتك بشكل عشوائي، أنت تفتح تطبيق الخرائط الذي يريك أفضل طريق، الشوارع التي سيمر بها، وأماكن الازدحام المحتملة. هذا بالضبط ما يفعله EXPLAIN لاستعلامك.

هذه الأداة تحول عملية تحسين الأداء من تخمين أعمى إلى علم دقيق ومبني على بيانات حقيقية من قلب قاعدة البيانات.

تشريح مخرجات `EXPLAIN`: كيف نفهم لغة قاعدة البيانات؟

عندما تنفذ EXPLAIN، ستحصل على جدول من المعلومات قد يبدو مربكاً في البداية. لكن لا تقلق، سنركز على أهم الأعمدة التي تحتاجها بنسبة 90% من الوقت. لنأخذ مثالاً على مخرجات MySQL/MariaDB:


EXPLAIN SELECT * FROM users WHERE city = 'Nablus';

قد ترى شيئاً كهذا:

id select_type table type possible_keys key rows Extra
1 SIMPLE users ALL NULL NULL 1000000 Using where

دعنا نفصِّل الأعمدة الأهم:

1. العمود `table`

اسم الجدول الذي تتم عليه العملية في هذه الخطوة. بسيط وواضح.

2. العمود `type` (الأهم على الإطلاق!)

هذا هو قلب خطة التنفيذ. يخبرك “كيف” ستصل قاعدة البيانات إلى البيانات في الجدول. ترتيبها من الأسوأ إلى الأفضل مهم جداً:

  • ALL: (سيء جداً 🚨) هذا يعني أن قاعدة البيانات ستقوم بعمل “مسح كامل للجدول” (Full Table Scan). أي أنها ستقرأ كل سطر في الجدول لتبحث عن طلبك. تخيل أنك تبحث عن اسم في دفتر هاتف لا يحتوي على فهرس أبجدي، ستضطر لقراءة كل الأسماء من البداية للنهاية. إذا رأيت هذا في جدول كبير، فهنا تكمن مشكلتك غالباً.
  • index: أفضل قليلاً من ALL، حيث يتم مسح الفهرس بالكامل، ولكنه لا يزال غير مثالي.
  • range: جيد. يعني أن قاعدة البيانات تستخدم فهرساً للبحث في نطاق معين (مثلاً، BETWEEN, <, >).
  • ref: جيد جداً. يستخدم عند ربط الجداول (JOINs) أو عند البحث بقيمة غير فريدة في عمود مفهرس.
  • eq_ref: ممتاز. عادةً ما تراه في الـ JOINs عند الربط باستخدام Primary Key.
  • const / system: (الأفضل) يعني أن قاعدة البيانات وجدت سطراً واحداً فقط باستخدام فهرس فريد (Primary Key or Unique Index)، وقرأته مرة واحدة وتعاملت معه كثابت.

3. العمودان `possible_keys` و `key`

possible_keys: يخبرك بالفهارس التي “تعتقد” قاعدة البيانات أنها قد تكون مفيدة. key: يخبرك بالفهرس الذي “استخدمته فعلاً”. إذا كان possible_keys يحتوي على اسم فهرس ولكن key هو NULL، فهذه إشارة إلى أن قاعدة البيانات قررت لسبب ما أن المسح الكامل للجدول أسرع (أو أن هناك مشكلة تمنع استخدام الفهرس).

4. العمود `rows`

تقدير لعدد الأسطر التي ستحتاج قاعدة البيانات لفحصها لتنفيذ هذه الخطوة. الرقم الكبير هنا، خاصة مع type: ALL، هو علامة خطر واضحة.

5. العمود `Extra`

يحتوي على معلومات إضافية قيمة جداً. ابحث عن هذه الإشارات السيئة:

  • Using filesort: هذا يعني أن قاعدة البيانات لم تستطع استخدام فهرس لترتيب النتائج (ORDER BY)، فاضطرت لكتابة النتائج في ملف مؤقت على القرص ثم ترتيبها. هذه عملية بطيئة جداً.
  • Using temporary: يعني أن قاعدة البيانات اضطرت لإنشاء جدول مؤقت لتخزين النتائج الوسيطة (مثلاً في GROUP BY). هذا أيضاً مكلف من ناحية الأداء.

من النظرية إلى التطبيق: قصة تحسين استعلام “قاتل”

لنعد إلى قصتنا. الاستعلام البطيء كان يشبه هذا (مع تبسيط):


-- لدينا جدول للمستخدمين وجدول للطلبات
-- نريد كل الطلبات للمستخدمين من مدينة معينة الذين سجلوا بعد تاريخ معين
SELECT
  u.name,
  u.email,
  o.order_id,
  o.order_date
FROM
  users u
JOIN
  orders o ON u.id = o.user_id
WHERE
  u.city = 'Gaza' AND u.registration_date > '2023-01-01';

الخطوة 1: التشخيص باستخدام `EXPLAIN`

وضعنا كلمة EXPLAIN قبل الاستعلام. كانت النتيجة على جدول users كارثية:

  • table: users
  • type: ALL
  • possible_keys: NULL
  • key: NULL
  • rows: 5,000,000 (لدينا 5 مليون مستخدم)
  • Extra: Using where

القصة كانت واضحة كالشمس. لتجد المستخدمين من غزة، كانت قاعدة البيانات تفحص كل الـ 5 ملايين مستخدم! كانت هذه هي عنق الزجاجة.

الخطوة 2: الحل – الفهرسة الذكية

السبب كان بسيطاً: لا يوجد فهرس على الأعمدة التي نبحث بها (city و registration_date). الحل هو إنشاء فهرس مركب (Composite Index) عليها.

نصيحة أبو عمر: عند إنشاء فهرس مركب، ترتيب الأعمدة مهم. ضع العمود الذي يتمتع بانتقائية أعلى (Cardinality) أولاً. في حالتنا، عدد المدن أقل بكثير من التواريخ المختلفة، لكن بما أننا نبحث بكليهما معاً، فإن إنشاء فهرس عليهما سيساعد بشكل كبير. سنضع city أولاً لأنه يستخدم مع عامل المساواة (=).


CREATE INDEX idx_users_city_reg_date ON users (city, registration_date);

الخطوة 3: التحقق من التحسين بـ `EXPLAIN` مرة أخرى

بعد إنشاء الفهرس، أعدنا تنفيذ نفس الأمر EXPLAIN. كانت النتيجة مختلفة تماماً:

  • table: users
  • type: range (أو ref إذا كان البحث بـ city فقط)
  • possible_keys: idx_users_city_reg_date
  • key: idx_users_city_reg_date
  • rows: 2500 (تقدير لعدد المستخدمين في غزة المسجلين بعد ذلك التاريخ)
  • Extra: Using index condition

انظر إلى الفرق! انتقلنا من فحص 5 ملايين سطر إلى 2500 سطر فقط. ومن type: ALL إلى type: range. سرعة الاستعلام تحسنت من دقائق إلى أجزاء من الثانية. لقد أنقذنا الموقف.

نصائح أبو عمر الذهبية لتحسين الاستعلامات

  1. لا تثق، بل تحقق (Don’t Trust, Verify): اجعل EXPLAIN جزءاً لا يتجزأ من روتين عملك. قبل أن تدمج أي استعلام معقد في الكود، شغله مع EXPLAIN. وبعد أي تعديل تظن أنه يحسّن الأداء، شغله مرة أخرى وقارن النتائج.
  2. الفهرسة فن مش “عن عن”: لا تضف فهارس لكل الأعمدة. كل فهرس تبنيه له تكلفة على عمليات الكتابة (INSERT, UPDATE, DELETE) لأنه يحتاج إلى تحديث. قم بفهرسة الأعمدة المستخدمة بكثرة في شروط WHERE و JOIN و ORDER BY.
  3. احذر من “الأعداء الصامتين” للفهارس: قد يكون لديك فهرس، لكنك تكتب استعلامك بطريقة تمنع قاعدة البيانات من استخدامه. أشهر مثال هو استخدام الدوال على الأعمدة المفهرسة.
    • سيء: WHERE YEAR(registration_date) = 2023 (يمنع استخدام الفهرس على `registration_date`).
    • جيد: WHERE registration_date >= '2023-01-01' AND registration_date < '2024-01-01' (يسمح باستخدام الفهرس).
  4. `SELECT *` هو عدوك اللدود: اطلب فقط الأعمدة التي تحتاجها. إذا كان الاستعلام يستطيع الحصول على كل البيانات التي يحتاجها من الفهرس وحده (يسمى Covering Index)، فإنه لن يحتاج للرجوع إلى الجدول الأساسي أبداً، وهذا سريع جداً. ستجد في عمود `Extra` عبارة `Using index` في هذه الحالة.

الخلاصة: افتح الصندوق الأسود! ✅

التعامل مع قواعد البيانات لا يجب أن يكون عملية تخمين. الأمر EXPLAIN هو مفتاحك لفهم “عقل” قاعدة البيانات، وهو يحولك من مبرمج يكتب استعلامات إلى مهندس بيانات يفهم كيف تعمل هذه الاستعلامات ويحسنها بذكاء وثقة.

في المرة القادمة التي تواجه فيها استعلاماً بطيئاً، لا تهرع لزيادة الموارد أو إعادة تشغيل الخوادم. خذ نفساً عميقاً، اكتب EXPLAIN، ودع قاعدة البيانات تخبرك بالقصة كاملة. يلا يا جماعة، شدّوا حيلكم، وما تخلوا أي استعلام بطيء يغلّبكم. افتحوا الصندوق الأسود وشوفوا العجب. 🚀

أبو عمر

سجل دخولك لعمل نقاش تفاعلي

كافة المحادثات خاصة ولا يتم عرضها على الموقع نهائياً

آراء من النقاشات

لا توجد آراء منشورة بعد. كن أول من يشارك رأيه!

آخر المدونات

تجربة المستخدم والابداع البصري

كان تطبيقنا حصناً منيعاً أمام ذوي الإعاقة: كيف أنقذتنا معايير الوصول (Accessibility) من جحيم الاستبعاد الرقمي؟

كنا نظن أن تطبيقنا تحفة فنية، حتى وصلتنا رسالة من مستخدم كفيف كشفت لنا أننا بنينا حصناً منيعاً ضده. هذه قصة تعلمنا المرير، ودليلكم العملي...

17 مايو، 2026 قراءة المزيد
الحوسبة السحابية

خوادمنا كانت نائمة 90% من الوقت: كيف أنقذتنا الحوسبة الخوادمية (Serverless) من جحيم الفواتير؟

قصة حقيقية من قلب المعركة التقنية، كيف انتقلنا من دفع فواتير ضخمة لخوادم نائمة معظم الوقت إلى نموذج فعال وموفر للتكاليف باستخدام الحوسبة الخوادمية (Serverless)....

17 مايو، 2026 قراءة المزيد
التوظيف وبناء الهوية التقنية

كانت المقابلات التقنية كابوساً: كيف أنقذني ‘المشروع المنزلي’ من جحيم الخوارزميات على السبورة البيضاء؟

من قلب المعاناة مع ألغاز السبورة البيضاء التي لا تنتهي، أسرد لكم تجربتي كـ "أبو عمر" في عالم المقابلات التقنية. هذه مقالة تكشف كيف كان...

17 مايو، 2026 قراءة المزيد
التوسع والأداء العالي والأحمال

طلبات لا تنتهي؟ كيف أنقذتنا قوائم انتظار الرسائل (Message Queues) من انهيار النظام

أشارككم قصة حقيقية من أرض المعركة البرمجية، كيف انتقلنا من نظام على حافة الانهيار بسبب الضغط، إلى نظام مرن وقابل للتوسع بفضل "قوائم انتظار الرسائل"....

16 مايو، 2026 قراءة المزيد
التكنلوجيا المالية Fintech

كانت بيانات بطاقات عملائنا قنبلة موقوتة: كيف أنقذنا ‘الترميز’ (Tokenization) من جحيم تخزين البيانات الحساسة؟

أشارككم قصة حقيقية من قلب المعركة التقنية، عندما كانت بيانات بطاقات عملائنا قنبلة موقوتة تهدد مستقبل شركتنا. اكتشفوا كيف كانت تقنية "الترميز" (Tokenization) طوق النجاة...

16 مايو، 2026 قراءة المزيد
البنية التحتية وإدارة السيرفرات

من الخوادم “الثلجية” إلى الكود: كيف أنقذتنا Terraform من جحيم الانحراف التكويني

أشارككم قصة حقيقية عن معاناة فريقنا مع الخوادم الفريدة التي لا تتكرر، وكيف كانت "البنية التحتية كود" (IaC) باستخدام Terraform هي طوق النجاة الذي أخرجنا...

16 مايو، 2026 قراءة المزيد
ادارة الفرق والتنمية البشرية

المسار المهني المزدوج: كيف أنقذنا أفضل مبرمجينا من “الترقية إلى عدم الكفاءة”؟

كان أفضل مبرمجينا يرحلون أو يتحولون إلى مديرين سيئين. في هذه المقالة، أسرد لكم قصة كيف أنقذنا "المسار المهني المزدوج" من هذا الجحيم، وكيف يمكن...

16 مايو، 2026 قراءة المزيد
البودكاست