إتقان الفهارس المركبة في MongoDB
يستعرض هذا الدليل تفاصيل الفهارس المركبة في MongoDB، وهي تقنية بالغة الأهمية لتحسين أداء قاعدة البيانات. سنغطي كل شيء بدءًا من فهم آليات عملها الأساسية وصولاً إلى تحليل فعاليتها واستكشاف الأخطاء الشائعة.
- فهم الفهارس المركبة
- إنشاء الفهارس المركبة
- الاستعلام باستخدام الفهارس المركبة
- تحليل استخدام الفهرس
- أفضل الممارسات والتحسين
- الأسئلة الشائعة
1. فهم الفهارس المركبة
في MongoDB، الفهرس المركب هو فهرس يمتد على حقول متعددة ضمن مستند واحد. على عكس الفهارس أحادية الحقل، التي تسرع فقط الاستعلامات على حقل واحد، فإن الفهارس المركبة تعزز بشكل كبير سرعة الاستعلامات التي تتضمن حقولًا متعددة، مما يؤثر بشكل كبير على أداء التطبيق. إن ترتيب الحقول داخل الفهرس أمر بالغ الأهمية؛ حيث تستخدم MongoDB هذا الترتيب لتحديد مواقع المستندات بكفاءة.
تحصل الاستعلامات التي تطابق الحقول الرائدة للفهرس على أكبر قدر من الفائدة. إذا استخدم الاستعلام الحقول الرائدة فقط، فسيتم استخدام الفهرس بالكامل لكل من الترشيح والفرز. ومع ذلك، إذا استخدم الاستعلام حقولًا تتجاوز الفهرس، فقد تستخدم MongoDB الفهرس جزئيًا، وقد تلجأ إلى مسح المجموعة بالكامل للمعايير المتبقية، مما يلغي مكاسب الأداء.
على سبيل المثال، ضع في اعتبارك مجموعة products
مع الحقول category
و price
و name
. يعمل الفهرس المركب على { "category": 1, "price": -1 }
على تحسين الاستعلامات التي تقوم بالترشيح حسب category
ثم الفرز حسب price
بترتيب تنازلي.
2. إنشاء الفهارس المركبة
إنشاء الفهارس المركبة أمر بسيط باستخدام طريقة db.collection.createIndex()
. الوسيط عبارة عن مستند حيث تمثل المفاتيح أسماء الحقول، وتمثل القيم ترتيب الفرز (1 تصاعديًا، -1 تنازليًا).
// إنشاء فهرس مركب على category (تصاعديًا) و price (تنازليًا)
db.products.createIndex( { category: 1, price: -1 } );
// إنشاء فهرس مركب على حقول متعددة، مع أوامر فرز مختلفة
db.users.createIndex( { firstName: 1, lastName: -1, age: 1 } );
// إضافة خيارات unique و sparse
db.products.createIndex( { category: 1, price: -1 }, { unique: true, sparse: true } );
unique: true
يفرض التفرد عبر الحقول المفهرسة، بينما sparse: true
يفهرس فقط المستندات التي لا تكون الحقول المفهرسة فيها خالية.
3. الاستعلام باستخدام الفهارس المركبة
يقوم مُحسِّن الاستعلامات في MongoDB تلقائيًا باستخدام فهرس مركب إذا تطابقت الاستعلام مع الحقول الرائدة وتتوافق ترتيب الفرز. على سبيل المثال، يستفيد الاستعلام التالي من الفهرس { "category": 1, "price": -1 }
:
db.products.find( { category: "Electronics" } ).sort( { price: -1 } );
يقوم هذا الاستعلام بالترشيح حسب category
(الحقل الرائد في الفهرس) والفرز حسب price
(مطابقة ترتيب الفرز في الفهرس). ومع ذلك، لن يستفيد هذا الاستعلام بنفس القدر:
db.products.find( { price: { $lt: 100 } } ).sort( { category: 1 } );
price
ليس الحقل الرائد، ولا يتوافق ترتيب الفرز مع الفهرس.
4. تحليل استخدام الفهرس
استخدم db.collection.find().explain()
لتحليل استخدام الفهرس. يوفر هذا معلومات مفصلة حول خطة تنفيذ الاستعلام، بما في ذلك الفهارس المستخدمة (أو غير المستخدمة) وأوقات التنفيذ.
db.products.find( { category: "Electronics" } ).sort( { price: -1 } ).explain();
افحص قسم “executionStats”. إذا تم استخدام الفهرس، فسترى استخدام مفتاح الفهرس وعدد المستندات التي تم فحصها. يشير “COLLSCAN” إلى مسح كامل للمجموعة، وهو أقل كفاءة من استخدام الفهرس.
5. أفضل الممارسات والتحسين
يُعد اختيار الفهرس بعناية أمرًا بالغ الأهمية. يؤدي الإفراط في الفهرسة إلى إبطاء عمليات الكتابة، بينما يؤدي نقص الفهرسة إلى استعلامات غير فعالة. أعط الأولوية لأنماط الاستعلامات المستخدمة بشكل متكرر، وقم بإضافة فهارس بشكل تدريجي حسب الحاجة. راقب استخدام الفهرس بانتظام وقم بالتعديل بناءً على احتياجات التطبيق. ضع في اعتبارك استخدام واجهة مستخدم MongoDB Compass GUI لإدارة الفهارس بشكل أبسط.
6. الأسئلة الشائعة
- كم عدد الفهارس المركبة التي يجب إنشاؤها؟ يعتمد العدد الأمثل على أنماط استعلامات تطبيقك. هناك حاجة إلى التوازن لتجنب الإفراط في الفهرسة.
- ماذا لو لم يتطابق استعلامي تمامًا مع الفهرس؟ قد تستخدم MongoDB أجزاء من الفهرس ولكنها قد تلجأ أيضًا إلى مسح المجموعة بالكامل للأجزاء غير المطابقة.
- هل يمكنني إنشاء فهارس مركبة مع حقول تصاعدية/تنازلية مختلطة؟ نعم، الترتيب بالغ الأهمية لتحقيق الأداء الأمثل.
- كيف أحذف فهرسًا مركبًا؟ استخدم
db.collection.dropIndex()
، وحدد اسم الفهرس أو المواصفات:
// الحذف حسب الاسم
db.products.dropIndex("category_1_price_-1");
// الحذف حسب المواصفات
db.products.dropIndex( { category: 1, price: -1 } );