0Pricing
Coding Interview Prep · درس

اكتشاف الاستعلامات البطيئة وإصلاحها

قائمة فحص تشخيصية لسؤال المقابلة: «هذا الاستعلام بطيء، أصلحه»

اكتشاف الاستعلامات البطيئة وإصلاحها درس مجاني في Coding Interview Prep على CoddyKit. هذا هو الدرس 4 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Coding Interview Prep، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Coding Interview Prep 4 دروس في المجموع.

سؤال «هذا الاستعلام بطيء، أصلحه»

هذا هو سؤال المقابلة الختامي: يقدّم لكم المحاور استعلامًا بطيئًا وخطة EXPLAIN ANALYZE، ثم يطلب منكم تشخيص المشكلة. وهو يختبر منهجية، لا حيلًا محفوظة.

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

حافظوا على منهجية منظمة واشرحوا منطقكم، فهذا ما يمنحكم التقييم المتقدم.

الخطوة 1: القياس باستخدام EXPLAIN ANALYZE

لا تخمّنوا أبدًا اعتمادًا على SQL وحده. احصلوا على الخطة الفعلية باستخدام EXPLAIN (ANALYZE, BUFFERS).

يوفّر ANALYZE أزمنة التنفيذ الفعلية وعدد الصفوف الفعلي؛ بينما يوضح BUFFERS ما إذا كنتم تصلون إلى الذاكرة المخبئية أو تقرؤون من القرص. ويخبركم الاثنان معًا ما إذا كان الاستعلام مقيّدًا بالمعالج، أو بعمليات الإدخال والإخراج، أو أنه ينفذ عملًا أكثر من اللازم.

شغّلوه بضع مرات؛ فقد تتحمل المرة الأولى تكلفة ذاكرة مخبئية باردة تشوّه التوقيت.

EXPLAIN (ANALYZE, BUFFERS)
SELECT * FROM orders o
JOIN customers c ON c.id = o.customer_id
WHERE o.created_at >= '2026-01-01';

الخطوة 2: العثور على العقدة المهيمنة

لا تقرؤوا الخطة من الأعلى إلى الأسفل بحثًا عشوائيًا. ابحثوا عن العقدة التي يُستهلك فيها معظم الوقت فعلًا.

احسبوا الزمن الذاتي لكل عقدة: زمن actual time الإجمالي مطروحًا منه زمن العقد الفرعية، ثم اضربوا الناتج في loops. العقدة التي تملك الحصة الأكبر هي هدفكم؛ وما عداها ضجيج.

قولوا في المقابلات: يُستهلك 80 بالمئة من زمن التنفيذ في عقدة Seq Scan هذه، ولذلك أركّز عليها. وسيكون تحسين أي شيء آخر جهدًا ضائعًا.

الخطوة 3: مقارنة التقديري بالفعلي

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

يوضح المثال أن التقدير أقل من الواقع بمقدار 1000 مرة. وقبل إعادة تصميم أي شيء، حدّثوا الإحصاءات؛ فكثيرًا ما يصلح هذا الأمر الواحد الخطة مجانًا.

يعيد ANALYZE حساب إحصاءات الأعمدة، بينما ينظف VACUUM ANALYZE أيضًا الصفوف الميتة ويحدّث خريطة الرؤية.

-- estimate rows=100, actual rows=120000  -> stale stats
ANALYZE orders;
-- or, for bloated tables:
VACUUM ANALYZE orders;

سبب شائع: استخدام دالة على عمود مفهرس

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

يفرض المثال إجراء فحص كامل لأن DATE() تُطبّق على كل صف. أعيدوا كتابته على هيئة شرط نطاق يعتمد على العمود مباشرةً (صيغة قابلة للاستفادة من الفهرس)، وعندها يبدأ استخدام الفهرس على created_at.

وينطبق الأمر نفسه على WHERE lower(email)=...: فإما أن تخزّنوا البيانات بصيغة موحّدة، أو تستعلموا عن العمود مباشرةً، أو تنشئوا فهرسًا تعبيريًا.

-- Not sargable: index unusable
WHERE DATE(created_at) = '2026-01-01'

-- Sargable: range over the bare column
WHERE created_at >= '2026-01-01'
  AND created_at <  '2026-01-02'

سبب شائع: فهرس مفقود

إذا كانت العقدة المهيمنة هي Seq Scan مع مرشح انتقائي بدرجة كبيرة، أو كانت Nested Loop تحتوي على قيمة loops ضخمة فوق مفتاح داخلي غير مفهرس، فعادةً ما يكون الإصلاح هو إضافة فهرس.

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

تحققوا من ذلك بإعادة تشغيل EXPLAIN ANALYZE، ولا تفترضوا أن الفهرس أفاد.

CREATE INDEX idx_orders_customer
  ON orders (customer_id);

سبب شائع: استخدام SELECT * وصفوف عريضة

يجلب SELECT * كل عمود من القرص وعبر الشبكة، كما يمنع الفحوص التي يقتصر فيها الوصول على الفهرس، لأن الفهرس نادرًا ما يغطي جميع الأعمدة.

اختاروا الأعمدة التي تحتاجون إليها فقط. فهذا يقلل عرض الصف، ويخفض عمليات الإدخال والإخراج، وقد يتيح فحصًا يغطيه الفهرس دون الوصول إلى الجدول.

يريد المحاور الذي يضع SELECT * في السؤال منكم ملاحظته. وغالبًا ما يكون تقليص قائمة الأعمدة تحسينًا سريعًا وفعليًا في الجداول العريضة.

-- Before
SELECT * FROM orders WHERE customer_id = 42;

-- After: only needed columns (may enable index-only scan)
SELECT order_id, amount FROM orders WHERE customer_id = 42;

سبب شائع: تفريغ البيانات إلى القرص

إذا أبلغت عقدة Sort أو Hash عن استخدام القرص (Sort Method: external merge Disk: 25000kB أو Batches: > 1)، فهذا يعني أن العملية تجاوزت work_mem وفرّغت البيانات إلى القرص.

الخيارات المتاحة هي: رفع قيمة work_mem للجلسة، أو تقليل عدد الصفوف التي تصل إلى الفرز أو التجزئة (بالتصفية في وقت أبكر)، أو إضافة فهرس يوفّر ترتيبًا جاهزًا حتى لا تكون هناك حاجة إلى الفرز أصلًا.

هذا تشخيص دقيق على مستوى الخبرة، ويقدّره المحاورون.

Sort  (actual rows=2000000 loops=1)
  Sort Key: o.amount
  Sort Method: external merge  Disk: 25000kB

سبب شائع: جلب صفوف أكثر من اللازم

انتبهوا إلى ظهور Rows Removed by Filter: 9500000. فقد قرأ الاستعلام عشرة ملايين صف وتخلّص من معظمها تقريبًا، وهذا مثال كلاسيكي على العمل الضائع.

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

المبدأ هو: نفّذوا أقل قدر ممكن من العمل، وطبّقوا التصفية في أبكر وقت ممكن وبأقل تكلفة ممكنة.

Seq Scan on events
  Filter: (event_type = 'purchase')
  Rows Removed by Filter: 9500000

قائمة التحقق التشخيصية

ردّدوا هذه الخطوات في المقابلة، ولن تضلوا الطريق:

  • القياس باستخدام EXPLAIN (ANALYZE, BUFFERS).
  • تحديد العقدة التي تستهلك معظم الوقت.
  • المقارنة بين الصفوف المقدّرة والفعلية، وإصلاح الإحصاءات القديمة أولًا.
  • التحقق من قابلية استخدام الفهرس، وإزالة الدوال من الأعمدة التي تجري تصفيتها.
  • الفهرسة للمرشحات الانتقائية ومفاتيح الربط.
  • تقليص الأعمدة وتجنب SELECT *.
  • مراقبة عمليات تفريغ البيانات إلى القرص وجلب الصفوف الزائدة.
  • التحقق بإعادة تشغيل الخطة.

تجميع المفاهيم

استعرضوا مثالًا كاملًا بصوت مسموع. تُظهر الخطة فحصًا تسلسليًا لجدول orders الذي يضم 50M صف، مع المرشح customer_id = 42، وتكون قيمة Rows Removed by Filter قريبة من 50M، بينما يطابق التقدير القيمة الفعلية تقريبًا.

التشخيص: المرشح انتقائي، ولا يوجد فهرس، لذا فإن الفحص هو التكلفة المهيمنة. الحل: CREATE INDEX ON orders(customer_id). أعيدوا التشغيل: تتحول الخطة إلى فحص بالفهرس، وينخفض الزمن من ثوانٍ إلى أقل من ميلي ثانية.

دورة القياس والتشخيص والإصلاح والتحقق هذه هي قالب الإجابة عن أي سؤال يتناول استعلامًا بطيئًا.

CREATE INDEX idx_orders_customer ON orders (customer_id);
EXPLAIN (ANALYZE, BUFFERS)
SELECT order_id, amount FROM orders WHERE customer_id = 42;

تحقق سريع

يستخدم استعلام شرط التصفية WHERE YEAR(order_date) = 2026، وتُظهر الخطة فحصًا تسلسليًا كاملًا رغم وجود فهرس B-tree مسبقًا على order_date. ما أفضل إصلاح أولي؟

مراجعة

أصبح لديكم الآن أسلوب قابل للتكرار للتعامل مع الأسئلة المتعلقة بالاستعلامات البطيئة:

  • احرصوا دائمًا على القياس باستخدام EXPLAIN (ANALYZE, BUFFERS)، وركّزوا على العقدة المهيمنة.
  • عالجوا الإحصاءات القديمة أولًا عندما تختلف التقديرات عن القيم الفعلية.
  • اجعلوا الشروط قابلة للاستفادة من الفهرس (sargable)، وأضيفوا فهارس للمرشحات الانتقائية ومفاتيح الربط، وتجنبوا SELECT *.
  • عالجوا الانسكاب إلى القرص وجلب البيانات الزائدة، ثم تحققوا من الخطة الجديدة.

استعرضوا قائمة التحقق، واقترحوا تغييرًا محددًا، ثم أعيدوا تشغيل الخطة لإثبات أثره؛ تلك هي إجابة المهندس الخبير.

الأسئلة الشائعة

هل درس «اكتشاف الاستعلامات البطيئة وإصلاحها» مجاني؟

نعم — نص درس «اكتشاف الاستعلامات البطيئة وإصلاحها» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Coding Interview Prep، انتقل إلى CoddyKit PRO. تتضمن دورة Coding Interview Prep 4 دروس في المجموع.

ماذا ستتعلم في «اكتشاف الاستعلامات البطيئة وإصلاحها»؟

قائمة فحص تشخيصية لسؤال المقابلة: «هذا الاستعلام بطيء، أصلحه» تتمرن على Coding Interview Prep مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ Coding Interview Prep؟

لا تُشترط خبرة سابقة. Coding Interview Prep على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 4 من أصل 4.

كم من الوقت يستغرق درس «اكتشاف الاستعلامات البطيئة وإصلاحها»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس Coding Interview Prep هذا؟

نعم. كل درس في Coding Interview Prep يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. قراءة خطة EXPLAIN
  2. Seq Scan مقابل Index Scan مقابل Index-Only
  3. خوارزميات الربط: Nested Loop وHash وMerge
  4. اكتشاف الاستعلامات البطيئة وإصلاحها
← العودة إلى Coding Interview Prep