labZero:~$ cd ./lab / lab-mysql-optimizer-skip...
الـ Query Optimizer يتجاهل الفهرس — متى تصبح الأرقام أهم من الخطة؟
استعلام على جدول 200K صف مع `INDEX(status)` — الـ optimizer اختار Full Table Scan. السبب: 78% من الصفوف بنفس القيمة.
السياق
جدول 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 يعرف ما لا تعرفه — قبل بناء فهرس على عمود حالة، افحص توزيع القيم الحقيقي، وفكّر في فهرس مركّب يشمل عمود الفرز.