labZero:~$ cd ./lab / lab-mysql-optimizer-skip...

المختبر
v0.1 قابل للاستخدام 4 يونيو 2026

الـ Query Optimizer يتجاهل الفهرس — متى تصبح الأرقام أهم من الخطة؟

استعلام على جدول 200K صف مع `INDEX(status)` — الـ optimizer اختار Full Table Scan. السبب: 78% من الصفوف بنفس القيمة.

#MySQL #Laravel #Performance

السياق

جدول orders بـ 200 ألف صف. عمود status يأخذ ثلاث قيم: pending (4K صف)، completed (156K)، cancelled (40K). استعلام WHERE status = 'pending' ORDER BY created_at مع INDEX(status) يأخذ 1.2 ثانية. EXPLAIN يُظهر type=ALL — Full Table Scan رغم الفهرس.

الإجراء

شغّلت SHOW INDEX FROM orders — cardinality = 3، وهو عدد القيم المختلفة فقط. استخدمت FORCE INDEX(status) — الاستعلام أبطأ بـ 40%! قارنت ثلاثة سيناريوهات: (أ) INDEX(status) المنفرد، (ب) INDEX(status, created_at) المركّب، (ج) ANALYZE TABLE لتحديث الإحصائيات. راقبت EXPLAIN FORMAT=JSON لقراءة rows_examined الفعلي.

النتيجة

الـ optimizer اختار الفهرس لـ pending (2% من الصفوف) وتجاهله لـ completed (78%). الفهرس المركّب (status, created_at) استُخدم في كلا الحالتين لأنه أتاح index-only scan مع أمر الفرز. ANALYZE TABLE حدّث الإحصائيات وحسّن الخطة لـ pending تلقائياً.

الدرس

الـ optimizer يعرف ما لا تعرفه — قبل بناء فهرس على عمود حالة، افحص توزيع القيم الحقيقي، وفكّر في فهرس مركّب يشمل عمود الفرز.

هل تشبه فكرتك هذا المشروع؟

إذا كان لديك مشروع قريب من هذه الفكرة، يمكننا الحديث عنه.

لنتحدّث عنها