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

فهرس قاعدة البيانات هو هيكل بحث منفصل يساعد قاعدة البيانات على العثور على الصفوف بشكل أسرع. ليس نسخة ثانية من جدولك. فكّر فيه مثل صفحات الفهرس في كتاب: تستخدم الفهرس للقفز إلى المكان الصحيح تقريبًا، ثم تقرأ الصفحة (الصف) الدقيقة التي تحتاجها.
بدون فهرس، غالبًا ما تملك قاعدة البيانات خيارًا آمنًا واحدًا: قراءة العديد من الصفوف للتحقق أيها يطابق الاستعلام. قد يكون ذلك مقبولًا عندما يحتوي الجدول على بضعة آلاف من الصفوف. مع نمو الجدول إلى ملايين الصفوف، يصبح "التحقق من مزيد من الصفوف" مزيدًا من عمليات القراءة من القرص، ومزيدًا من ضغط الذاكرة، ومزيدًا من عمل CPU—فتصبح نفس الاستعلام التي كانت فورية سابقًا بطيئة.
تقلل الفهارس كمية البيانات التي يجب على قاعدة البيانات فحصها للإجابة عن أسئلة مثل "ابحث عن الطلب بالمعرِّف 123" أو "استرجع المستخدمين بهذا البريد الإلكتروني". بدلاً من المسح الكامل، تتبع قاعدة البيانات بنية مدمجة تضيق عملية البحث بسرعة.
لكن الفهرسة ليست حلًّا شاملًا. بعض الاستعلامات لا تزال تحتاج معالجة عدد كبير من الصفوف (تقارير شاملة، عوامل تصفية ذات انتقائية منخفضة، تجميعات مكثفة). وللفهارس تكلفة حقيقية: مساحة تخزين إضافية وبطء في عمليات الكتابة، لأن الإدخالات والتحديثات يجب أن تُحدَّث أيضًا في الفهرس.
سترى:
عندما تنفذ قاعدة البيانات استعلامًا، أمامها خياران عامان: مسح الجدول صفًا صفًا، أو القفز مباشرة إلى الصفوف المطابقة. معظم مكاسب الفهرسة تأتي من تجنُّب قراءات غير ضرورية.
المسح الكامل للجدول Full table scan هو بالضبط ما يبدو عليه: تقرأ قاعدة البيانات كل صف، تتحقق مما إذا كان يطابق شرط الـ WHERE، ثم تُرجع النتائج. هذا مقبول للجداول الصغيرة، لكنه يبطئ بطريقة متوقعة مع نمو الجدول—كلما زاد عدد الصفوف زاد العمل.
باستخدام فهرس، يمكن لقاعدة البيانات غالبًا تجنُّب قراءة معظم الصفوف. بدلاً من ذلك، تستشير الفهرس أولًا (هيكل مدمج مُصمَّم للبحث) لتجد أين تعيش الصفوف المطابقة، ثم تقرأ تلك الصفوف فقط.
تخيَّل كتابًا. إذا أردت كل الصفحات التي تذكر "التمثيل الضوئي" يمكنك قراءة الكتاب صفحة صفحة (مسح كامل). أو يمكنك استخدام فهرس الكتاب، والقفز إلى الصفحات المدرجة وقراءة تلك الأقسام فقط (بحث بالفهرس). الطريقة الثانية أسرع لأنها تتخطى معظم الصفحات.
تقضي قواعد البيانات وقتًا طويلًا تنتظر عمليات القراءة—خصوصًا عندما لا تكون البيانات في الذاكرة. تقليل عدد الصفوف (والصفحات) التي يجب لمسها يقلل عادةً من:
تفيد الفهارس أكثر عندما تكون البيانات كبيرة ونمط الاستعلام انتقائي (مثلًا، استرجاع 20 صفًا مطابِقًا من بين 10 ملايين). إذا كان استعلامك يُرجع معظم الصفوف على أي حال، أو الجدول صغير بما فيه الكفاية ليلائم الذاكرة، فقد يكون المسح الكامل بنفس سرعة الفهرس—أو أسرع.
تعمل الفهارس لأنها ترتب القيم بحيث يمكن لقاعدة البيانات القفز قريبًا مما تريد بدلًا من فحص كل صف.
أكثر هياكل الفهرس شيوعًا في قواعد بيانات SQL هي B-tree (غالبًا تُكتب "B-tree" أو "B+tree"). من الناحية المفاهيمية:
بسبب الفرز، تكون أشجار B ممتازة لكل من البحث بالتطابق (WHERE email = ...) واستعلامات النطاق (WHERE created_at >= ... AND created_at < ...). يمكن لقاعدة البيانات الانتقال إلى الحي المناسب من القيم ثم المسح للأمام بترتيبها.
يقول الناس إن عمليات بحث شجرة B "لوغاريتمية". عمليًا، هذا يعني: عندما ينمو جدولك من آلاف إلى ملايين الصفوف، عدد الخطوات للعثور على قيمة ينمو ببطء، وليس بنسبة مباشرة. بدلًا من "ضعف البيانات يعني ضعف العمل"، إنه أقرب إلى "زيادة كبيرة في البيانات تعني بضعة خطوات إضافية فقط"، لأن قاعدة البيانات تتبع مؤشرات عبر عدد قليل من مستويات الشجرة.
توفّر بعض المحركات أيضًا فهارس هاش. يمكن أن تكون هذه سريعة جدًا للتطابقات الدقيقة لأن القيمة تُحوَّل إلى هاش وتستخدم للعثور على الإدخال مباشرة.
المقايضة: الفهارس الهاشية عادةً لا تساعد في النطاقات أو المسح المرتب، وتوافرها/سلوكها يختلف بين قواعد البيانات.
PostgreSQL، MySQL/InnoDB، SQL Server وغيرها تخزن وتستخدم الفهارس بشكل مختلف (حجم الصفحة، التجميع، الأعمدة المضمنة، فحوصات الرؤية). لكن المفهوم الأساسي يصح: الفهارس تخلق بنية مدمجة قابلة للملاحة تتيح لقاعدة البيانات تحديد مواقع الصفوف المطابقة بعمل أقل بكثير من مسح الجدول كله.
الفهارس لا تسرع "الـ SQL" عمومًا—إنما تسرع أنماط وصول محددة. عندما يتوافق الفهرس مع طريقة تصفية الاستعلام أو ربطه أو فرزه، يمكن لقاعدة البيانات القفز مباشرة إلى الصفوف ذات الصلة بدلًا من قراءة الجدول كله.
1) عوامل WHERE (خاصةً على أعمدة انتقائية)
إذا كان استعلامك يضيّق جدولًا كبيرًا إلى مجموعة صغيرة من الصفوف، فعادةً الفهرس هو أول ما تنظر إليه. مثال كلاسيكي هو البحث عن مستخدم بمعرف.
بدون فهرس على users.email قد تضطر قاعدة البيانات إلى مسح كل الصفوف:
SELECT * FROM users WHERE email = '[email protected]';
مع فهرس على email يمكنها تحديد الصفوف المطابقة بسرعة والتوقف.
2) مفاتيح JOIN (المفاتيح الأجنبية والمفاتيح المرجعية)
الانضمامات هي حيث "الكفاءات الصغيرة" تتحول إلى تكاليف كبيرة. إذا كنت تربط orders.user_id بـ users.id، فإن فهرسة أعمدة الربط (عادة orders.user_id والمفتاح الأساسي users.id) تساعد قاعدة البيانات على مطابقة الصفوف دون المسح المتكرر.
3) ORDER BY (عندما تريد النتائج مرتبة بالفعل)
الفرز مكلف عندما تضطر قاعدة البيانات إلى جمع الكثير من الصفوف وفرزها بعد ذلك. إذا كنت تُشغّل كثيرًا:
SELECT * FROM orders WHERE user_id = 42 ORDER BY created_at DESC;
يمكن أن يسمح فهرس يطابق user_id وعمود الفرز للـ engine بقراءة الصفوف بالترتيب المطلوب بدلًا من فرز نتيجة وسيطة كبيرة.
4) GROUP BY (عندما يتوافق التجميع مع الفهرس)
قد يستفيد التجميع عندما تقرأ قاعدة البيانات البيانات بترتيب المجموعات. ليس ضمانًا، لكن إذا كنت تجمع غالبًا بحسب عمود يُستخدم للتصفية (أو متجمّع بطبيعته في الفهرس)، قد يقل العمل.
شجر B جيدة جدًا لظروف النطاق—فكر في التواريخ والأسعار واستعلامات "بين":
SELECT * FROM orders
WHERE created_at >= '2025-01-01' AND created_at < '2025-02-01';
لهذا تُستخدم الفهارس على عمود النطاق في لوحات التحكم والتقارير وشاشات "النشاط الأخير" بشكل واسع، وغالبًا ما تنتج تحسينًا فوريًا.
الموضوع بسيط: الفهارس تفيد عندما تعكس طريقة البحث والفرز لديك. إذا كانت استعلاماتك تتماشى مع أنماط الوصول هذه، يمكن لقاعدة البيانات إجراء قراءات مستهدفة بدلًا من المسوح العريضة.
يفيد الفهرس أكثر عندما يقلل بشدة عدد الصفوف التي يجب لمسها. هذه الخاصية تُسمى الانتقائية.
الانتقائية هي ببساطة: كم صفًا يطابق قيمة معينة؟ العمود عالي الانتقائية له قيم مميزة كثيرة، لذا كل بحث يطابق عددًا قليلاً من الصفوف.
email, user_id, order_number (غالبًا فريدة أو قريبة من ذلك)is_active, is_deleted, status بقيم قليلة شائعةمع الانتقائية العالية، يمكن للفهرس القفز إلى مجموعة صغيرة من الصفوف. مع الانتقائية المنخفضة، يشير الفهرس إلى جزء ضخم من الجدول—فتضطر قاعدة البيانات لقراءة وتصفيه الكثير.
افترض وجود جدول به 10 ملايين صف وعمود is_deleted حيث 98% قيمته false. فهرس على is_deleted لا يوفر كثيرًا لاستعلام:
SELECT * FROM orders WHERE is_deleted = false;
مجموعة المطابقين لا تزال تقريبًا الجدول كله. وقد يكون استخدام الفهرس أبطأ من المسح التسلسلي لأن المحرك يقوم بمزيد من العمل بين إدخالات الفهرس وصفحات الجدول.
مخطط الاستعلام يقدر التكاليف. إذا لم يقلل الفهرس من العمل بما فيه الكفاية—لأن العديد من الصفوف تطابق، أو لأن الاستعلام يحتاج معظم الأعمدة—قد يختار المسح الكامل.
توزيع البيانات ليس ثابتًا. قد يبدأ عمود status موزعًا بشكل متساوٍ، ثم ينحرف بحيث يُهيمن عليه قيمة واحدة. إذا لم تُحدَّث الإحصاءات، قد يتخذ المخطط قرارات سيئة، وقد يتوقف فهرس كان مفيدًا عن العطاء.
فهارس عمود واحد بداية جيدة، لكن العديد من الاستعلامات الحقيقية تُفلتر بواسطة عمود وتُرتب أو تُفلتر بواسطة آخر. هنا تتألق الفهارس المركبة: فهرس واحد يمكن أن يخدم عدة أجزاء من الاستعلام.
تستطيع معظم قواعد البيانات (خصوصًا مع فهارس B-tree) استخدام الفهرس المركب بكفاءة بدءًا من الأعمدة اليسارية. فكر في الفهرس كمرتب أولًا حسب العمود A، ثم ضمنه حسب العمود B.
هذا يعني:
account_id ثم تُصفّي أو تُرتّب حسب created_atcreated_at فقط (لأنه ليس العمود الأيسر)عبء شائع هو "أرني أحدث الأحداث لهذا الحساب". نمط الاستعلام:
SELECT id, created_at, type
FROM events
WHERE account_id = ?
ORDER BY created_at DESC
LIMIT 50;
غالبًا ما يستفيد كثيرًا من:
CREATE INDEX events_account_created_at
ON events (account_id, created_at);
يمكن لقاعدة البيانات القفز مباشرة إلى جزء الحساب في الفهرس وقراءة الصفوف بترتيب الوقت، بدلًا من المسح والفرز لمجموعة كبيرة.
الفهرس المغطٍّ يحتوي كل الأعمدة التي يحتاجها الاستعلام بحيث يمكن لقاعدة البيانات إرجاع النتائج من الفهرس دون الرجوع إلى صفوف الجدول (قراءات أقل، I/O عشوائي أقل).
كن حذرًا: إضافة أعمدة إضافية يمكن أن تجعل الفهرس كبيرًا ومكلفًا.
الفهارس المركبة العريضة يمكن أن تبطئ الكتابة وتستهلك مساحة تخزين كبيرة. أضفها فقط لاستعلامات ذات قيمة عالية محددة، وتحقق باستخدام خطة EXPLAIN وقياسات حقيقية قبل وبعد.
غالبًا ما يُوصف الفهرس بأنه "سرعة مجانية"، لكنه ليس مجانيًا. يجب صيانة هياكل الفهارس في كل مرة يتغير فيها الجدول، وتستهلك موارد فعلية.
عند إجراء INSERT لصف جديد، لا تكتب قاعدة البيانات الصف فقط—بل تُدرَج أيضًا إدخالات مقابلة في كل فهرس على هذا الجدول. ونفس الشيء لـ DELETE والعديد من UPDATE.
لهذا السبب "المزيد من الفهارس" يمكن أن يبطئ أحمال الكتابة بشكل ملحوظ. قد يكون UPDATE الذي يغيّر عمودًا مفهرسًا مُكلِفًا خاصةً: قد تضطر قاعدة البيانات لإزالة إدخال الفهرس القديم وإضافة جديد (وفي بعض المحركات قد يؤدي ذلك إلى انقسام صفحات أو إعادة توازن داخلية).
كل فهرس يشغل مساحة على القرص. على الجداول الكبيرة، يمكن أن تنافس الفهارس (أو تتجاوز) حجم الجدول، خاصةً مع فهارس متداخلة.
ويؤثر ذلك أيضًا على الذاكرة. تعتمد قواعد البيانات كثيرًا على التخزين المؤقت؛ إذا كانت مجموعة العمل تشمل عدة فهارس كبيرة، يجب أن يحتفظ الكاش بالمزيد من الصفحات ليظل سريعًا. وإلا سترى مزيدًا من عمليات I/O من القرص وأداءً أقل قابلية للتنبؤ.
الفهرسة شأنها شأن اختيار ما نُسرِّع. إذا كان حمل العمل قراءة-ثقيلة، فالفهارس الإضافية قد تستحق التكلفة. إذا كان حمل العمل كتابة-ثقيل، ففضّل الفهارس التي تدعم استعلاماتك الأهم وتجنّب التكرارات. قاعدة مفيدة: أضف فهرسًا فقط عندما تستطيع تسمية الاستعلام الذي يساعده—وتتحقق أن مكسب سرعة القراءة يفوق تكلفة الكتابة والصيانة.
إضافة فهرس "يبدو" أنها يجب أن تساعد—لكن يمكنك (ويجب) التحقق فعليًا. الأداتان اللتان تصنعان ذلك ملموسًا هما خطة الاستعلام (EXPLAIN) والقياسات الحقيقية قبل/بعد.
شغّل EXPLAIN (أو EXPLAIN ANALYZE) على نفس الاستعلام الذي يهمك.
EXPLAIN ANALYZE): إذا خمن المخطط 100 صفًا لكنه لمس فعليًا 100,000، فالمحسّن أخطأ—غالبًا لأن الإحصاءات قديمة أو الفلتر أقل انتقائية مما اعتُقد.\n- خطوات الفرز: إذا رأيت عملية Sort صريحة، فقاعدة البيانات تقوم بترتيب النتائج بعد جلبها. إذا أزال فهرس جديد هذا الفرز لأنه يطابق ORDER BY، فقد يكون ذلك مكسبًا كبيرًا.قم بقياس الاستعلام بنفس المعلمات، على حجم بيانات ممثل، وسجل زمن الاستجابة (p50/p95)، والصفوف الممسوحة، وتأثير CPU/IO. احفظ خطة الاستعلام الحالية لمقارنتها لاحقًا.
انتبه للتخزين المؤقت: التشغيل الأول قد يكون أبطأ لأن البيانات ليست في الذاكرة؛ وتشغيلات متكررة قد تبدو "مُحلَّاة" حتى بدون فهرس. للمقارنة، شغّل عدة مرات وركّز على ما إذا تغيّرت الخطة (استخدام الفهرس، عدد الصفوف المقروءة) إلى جانب الزمن.
إذا أظهر EXPLAIN ANALYZE صفوفًا أقل ملموسًا وخطوات مكلفة أقل (مثل اختفاء الفرز)، فقد أثبتت أن الفهرس مفيد—ليس مجرد تكهن.
يمكنك إضافة الفهرس "الصحيح" وما زلت لا ترى تسارعًا إذا كُتِب الاستعلام بطريقة تمنع قاعدة البيانات من استخدامه. هذه المشكلات غالبًا ما تكون دقيقة؛ لأن الاستعلام ما زال يرجع النتائج الصحيحة—إلا أنه يعمل في خطة أبطأ.
1) الوايلدكارد في البداية
عندما تكتب:
WHERE name LIKE '%term'
لا يستطيع الفهرس العادي من نوع B-tree معرفة أين يبدأ "%term" في الترتيب، لذلك يعود المسح إلى الكثير من الصفوف.
بدائل:
WHERE name LIKE 'term%'.2) دوال على الأعمدة المفهرسة
قد يبدو هذا غير ضار:
WHERE LOWER(email) = '[email protected]'
لكن LOWER(email) يغيّر التعبير، لذا لا يمكن استخدام الفهرس على email مباشرة.
بدائل:
WHERE email = ....التحويلات الضمنية للنوع: مقارنة أنواع مختلفة قد تجبر قاعدة البيانات على تحويل أحد الطرفين، مما يعطل استخدام الفهرس (مثال: مقارنة عمود عدد صحيح بسلسلة نصية).
مطابقات/ترميزات مختلفة: إذا استخدمت المقارنة ترتيبًا زمنيًا (collation) مختلفًا عن ذلك الذي بُني به الفهرس، قد يتجنب المحسن استخدام الفهرس.
LIKE '%x')?LOWER(col), DATE(col), CAST(col))؟EXPLAIN لتؤكد ما اختاره المحرك؟الفهارس ليست "اضبطها وانسها". مع مرور الوقت، تتغير البيانات، تنقلب أنماط الاستعلام، ويتغير الشكل الفيزيائي للجداول/الفهارس. الفهرس الجيّد قد يصبح أقل فاعلية—أو حتى ضارًا—إذا لم تُجرِ صيانته.
تعتمد معظم قواعد البيانات على مخطط استعلام لاختيار كيفية تنفيذ استعلام: أي فهرس يُستخدم، أي ترتيب للانضمام يُختار، وما إذا كانت عمليات الفهرسة مفيدة. لاتخاذ تلك القرارات، يستخدم المخطط إحصاءات—ملخصات عن توزيع القيم، أعداد الصفوف، والانحراف.
عندما تصبح الإحصاءات قديمة تكون تقديرات الصفوف خاطئة. يؤدي ذلك إلى اختيار خطط سيئة، مثل اختيار فهرس يُرجع صفوفًا أكثر بكثير مما كان متوقعًا، أو تجاهل فهرس كان سيفيد.
إصلاح روتيني: جدولة تحديث الإحصاءات بانتظام (غالبًا يُسمى "ANALYZE" أو ما شابه). بعد تحميل بيانات كبيرة، حذف واسع، أو تغيّر كبير، حدّث الإحصاءات سريعًا.
أثناء الإدراج، التحديث، والحذف، يمكن للفهارس أن تتراكم فيها تضخُّم (صفحات إضافية لا تحمل بيانات مفيدة) وتجزؤ (بيانات منتشرة تزيد I/O). النتيجة فهارس أكبر وقراءات أكثر وبطء في المسوح—خصوصًا لاستعلامات النطاق.
إصلاح روتيني: قم بإعادة بناء أو إعادة تنظيم الفهارس المستخدمة بكثافة عندما تنمو بشكل غير متناسب أو يهبط الأداء. الأدوات والتأثير يختلفان حسب قاعدة البيانات، لذا عامل هذا كعملية محكومة وليس قاعدة عامة.
أعد مراقبة:
تغذية راجعة كهذه تساعدك على اكتشاف متى تحتاج صيانة—ومتى يجب تعديل الفهرس أو حذفه. للمزيد عن التحقق من التحسينات، انظر /blog/how-to-prove-an-index-helps-explain-and-measurements.
يجب أن تكون إضافة فهرس تغييرًا مقصودًا، لا تخمينًا. سير عمل خفيف يحافظ على تركيزك على مكاسب قابلة للقياس ويمنع "تشتت الفهارس".
ابدأ بالأدلة: سجلات الاستعلامات البطيئة، تتبعات APM، أو تقارير المستخدم. اختر استعلامًا بطيئًا ومتكررًا—فالتقرير النادر بمدة 10 ثوانٍ أقل أهمية من عملية بحث شائعة 200 مللي.
احصل على SQL الدقيق ونمط المعاملات (مثال: WHERE user_id = ? AND status = ? ORDER BY created_at DESC LIMIT 50). اختلافات صغيرة تغيّر الفهرس المناسب.
سجّل زمن الاستجابة الحالي (p50/p95)، الصفوف الممسوحة، وتأثير CPU/IO. احفظ إخراج الخطة الحالي (EXPLAIN / EXPLAIN ANALYZE) للمقارنة لاحقًا.
اختر الأعمدة التي تطابق كيفية تصفية الاستعلام وفرزه. فضّل الفهرس الأدنى الذي يجعل الخطة تتوقف عن مسح نطاقات ضخمة.
اختبر في بيئة staging بحجم بيانات شبيه بالإنتاج؛ الفهارس قد تبدو رائعة على بيانات صغيرة وتخيب عند النطاق.
على الجداول الكبيرة استخدم خيارات عبر الإنترنت حيث تتوفر (مثلاً PostgreSQL CREATE INDEX CONCURRENTLY). جدولة التغييرات في فترات حركة أقل إذا كان النظام قد يقفل عمليات الكتابة.
أعد تشغيل الاستعلام ذاته وقارن:
إذا زاد تكلفة الكتابة أو تسبب الفهرس في تضخُّم الذاكرة، احذفه بأمان (مثلاً DROP INDEX CONCURRENTLY حيثما أمكن). اجعل التغيير قابلًا للعكس.
في المهاجرات أو ملاحظات المخطط، اكتب أي استعلام يخدمه الفهرس وما الذي تحسَّن. سيعرفك أو زميلك المستقبلي لماذا وُجد هذا الفهرس ومتى يمكن حذفه.
إذا كنت تبني خدمة جديدة وتريد تجنُّب "تشتت الفهارس" مبكرًا، يمكن أن تساعدك Koder.ai على التسريع في الحلقة الكاملة: توليد تطبيق React + Go + PostgreSQL من خلال الدردشة، تعديل مخططات الجداول والـ migrations للفهارس عند تغيّر المتطلبات، ثم تصدير الشيفرة المصدرية عندما تكون جاهزًا للإدارة اليدوية. عمليًا، يسهل الانتقال من "نقطة النهاية هذه بطيئة" إلى "ها هو EXPLAIN، الفهرس الأدنى، ومهاجرة قابلة للتراجع" دون انتظار خطوط أنابيب تقليدية طويلة.
الفهارس رافعة قوية، لكنها ليست زرًا سحريًا. أحيانًا جزء الطلب البطيء يحدث بعد أن تجد قاعدة البيانات الصفوف الصحيحة—أو نمط الاستعلام يجعل الفهرسة الخيار الخاطئ أولًا.
إذا كان استعلامك يستخدم فهرسًا جيدًا ومع ذلك بطيء، ابحث عن هذه الأسباب الشائعة:
OFFSET 999000 قد يكون بطيئًا حتى مع الفهارس. فضّل التصفّح بالمفتاح (keyset pagination) مثلًا "أعطني الصفوف بعد آخر id/timestamp مرئي".SELECT *) أو إرجاع عشرات الآلاف من السجلات يمكن أن يؤدي إلى عنق زجاجة في الشبكة، أو التسلسل إلى JSON، أو معالجة التطبيق.إن أردت منهجًا أعمق لتشخيص عنق الزجاجة، اقترن هذا بدليل العمل في /blog/how-to-prove-an-index-helps.
لا تخمن. قس أين يقضي الوقت (تنفيذ قاعدة البيانات مقابل الصفوف المرجعة مقابل كود التطبيق). إذا كانت قاعدة البيانات سريعة لكن الـ API بطيء، فالمزيد من الفهارس لن يساعد.
هيكل فهرسي منفصل (غالبًا شجرة B) يخزن قيم أعمدة مختارة بشكل مرتَّب وقابل للبحث مع مؤشرات تشير إلى صفوف الجدول. تستخدمه قاعدة البيانات لتجنب قراءة معظم الجدول عند تنفيذ استعلامات انتقائية.
ليس نسخة كاملة من الجدول، لكنه يكرر بعض بيانات الأعمدة مع بيانات وصفية، لذا يستهلك مساحة تخزين إضافية.
بدون فهرس، قد تضطر قاعدة البيانات لإجراء مسح كامل للجدول: قراءة الكثير (أو كل) الصفوف ومطابقة كل صف مع شرط WHERE.
مع فهرس، يمكنها غالبًا القفز مباشرة إلى مواقع الصفوف المطابقة وقراءة تلك الصفوف فقط، ما يقلل عمليات القراءة من القرص/SSD، ووقت المعالجة على CPU، وضغط الذاكرة.
يحافظ فهرس شجرة B على القيم مرتبة ومنظمة في صفحات تشير إلى صفحات أخرى، لذلك يمكن لقاعدة البيانات التنقل بسرعة إلى الحيّ الصحيح من القيم.
لهذا تعمل أشجار B جيدًا لكل من:
WHERE email = ...)WHERE created_at >= ... AND created_at < ...)يمكن أن تكون الفهارس الهاشية سريعة جدًا في التطابقات الدقيقة (=) لأنها تقوم بتجزئة القيمة والانتقال مباشرة إلى الدلو المناسب.
المقايضات:
في كثير من سيناريوهات العالم الحقيقي، تبقى أشجار B هي الافتراضية لأنها تدعم أنماط استعلام أوسع.
تفيد الفهارس عادةً أكثر في:
WHERE الانتقائية (عندما تطابق عددًا قليلًا من الصفوف)JOIN (المفاتيح الأجنبية والمفتاح المرجعي)ORDER BY الذي يتوافق مع ترتيب الفهرس (قد يتجنب عملية الفرز)GROUP BY عندما تقرأ البيانات بترتيب مجموعات يقلل العملالانتقائية هي "كم عدد الصفوف التي تطابق قيمة معينة؟". تفيد الفهارس عندما يُضيّق الشرط مجموعة النتائج بشكل كبير.
الأعمدة قليلة التمييز (مثل is_deleted, is_active, أو حالات status القليلة) غالبًا ما تطابق نسبة كبيرة من الجدول، وفي هذه الحالة لا يستحق استخدام الفهرس لأنه قد يكون أبطأ من المسح التسلسلي.
لأن المُحسّن (query planner) يقدر أن استخدامه لن يقلل العمل بما يكفي.
أسباب شائعة:
في معظم تطبيقات شجرة B، يُرتب الفهرس أولًا حسب العمود الأول، ثم داخل ذلك حسب العمود الثاني، وهكذا. لذلك يمكن استخدام الفهرس بكفاءة بدءًا من العمود/الأعمدة اليسرى.
مثال:
(account_id, created_at) ممتاز لعمليات WHERE account_id = ? مع تصفية/فرز بالوقت.created_at لأن ذلك ليس العمود الأيسر.الفهرس المغطٍّ يحتوي كل الأعمدة التي يحتاجها الاستعلام بحيث يمكن لقاعدة البيانات إرجاع النتيجة من الفهرس دون الرجوع إلى صفوف الجدول.
الفوائد:
التكاليف:
يُستخدَم الفهرس المغطٍّ لاستعلامات عالية القيمة وليس "فقط تحسُّبًا".
تحقق من أمرين:
EXPLAIN / EXPLAIN ANALYZE وتأكد من أن الخطة تغيرت (مثل Seq Scan → Index Scan/Seek, عدد الصفوف المقروءة انخفض، خطوة الفرز اختفت).\n- القياسات الحقيقية: قارن زمن الاستجابة قبل/بعد في نفس الظروف وبحجم بيانات ممثل.\n\nراقب أيضًا تأثيره على عمليات الكتابة، لأن الفهارس الجديدة قد تبطئ //.إذا كان الاستعلام يُرجع جزءًا كبيرًا من الجدول، فالفائدة غالبًا ما تكون ضئيلة.
INSERTUPDATEDELETE