MongoDB: أنواع الفهارس والميزات المتقدمة
آخر تحديث: 2026-08-26
ميزات الفهرسة المتقدمة — إتقان قواعد TTL والقواعد الجزئية وقواعد ESR لإنشاء نظام فهرسة على مستوى الإنتاج.
1. ما ستتعلمه
- فهرس فريد (فريد)
- فهرس متفرق
- مؤشر TTL (انتهاء الصلاحية التلقائي)
- جزئي: فهرس جزئي
- قاعدة ESR (المساواة/الترتيب/النطاق)
- أفضل الممارسات لتصميم الفهارس
graph TB
A[Index Types] --> B[unique<br/>Unique Index]
A --> C[sparse<br/>Sparse Index]
A --> D[TTL<br/>Automatic Expiration]
A --> E[partial<br/>Selected Indexes]
A --> F[compound<br/>Composite Index]
B --> B1[Ensure Uniqueness<br/>Repeated Throw Errors]
C --> C1[Skip null<br/>Space-saving]
D --> D1[Automatic Deletion<br/>Scheduled Cleanup]
E --> E1[Indexed subset only<br/>Performance + Savings]
style D fill:#d4edda
style E fill:#d4edda
2. الفهرس الفريد (unique)
شرح المفهوم: يضمن الفهرس الفريد أن تكون القيم الموجودة في الحقل المفهرس فريدة على مستوى المجموعة بأكملها، مما يوفر ضمانًا لسلامة البيانات على مستوى قاعدة البيانات. وعلى عكس عملية التحقق من الصحة على مستوى التطبيق، يتم فرض الفهرس الفريد بواسطة محرك قاعدة البيانات — حيث يتم رفض القيم المكررة بغض النظر عن العميل الذي يقوم بكتابتها.
كيفية العمل: يقوم فهرس فريد بفرض قيود التفرد داخل شجرة B+. في كل مرة يتم فيها إدراج أو تحديث، يتحقق المحرك أولاً مما إذا كان هناك مفتاح بقيمة مماثلة موجودًا بالفعل في الفهرس؛ وإذا كان الأمر كذلك، فإنه يطلق استثناء E11000 duplicate key error. يتطلب الفهرس الفريد المركب أن تكون مجموعة الحقول فريدة (بينما يجوز تكرار الحقول الفردية).
حالات الاستخدام:
- عنوان البريد الإلكتروني للمستخدم، ورقم الهاتف المحمول (المعرّف الفريد عالميًا)
- رقم SKU (المعرّف الفريد للمنتج)
- التفرد المركب: على سبيل المثال، يضمن
(userId, productId)أن كل مستخدم لا يمكنه تقييم المنتج نفسه إلا مرة واحدة. - غير مناسب: الحقول التي تسمح بقيم مكررة (مثل
category،status)
| البعد | الفهرس العادي | الفهرس الفريد |
|---|---|---|
| قيود القيم | يُسمح بالتكرار | لا يُسمح بالتكرار |
| إدراج فحص | تحديث شجرة B+ فقط | تحديث شجرة B+ + فحص التفرد |
| أداء الكتابة | اختبار الأداء | أبطأ قليلاً (+5٪ من عبء التكافؤ) |
| رسالة الخطأ | لا شيء | E11000 خطأ في تكرار المفتاح |
| فريد جزئيًا | غير مدعوم | تم تنفيذه باستخدام partialFilterExpression |
// === Create a unique index ===
db.users.createIndex({ email: 1 }, { unique: true });
// email Field values must be unique.
// === Composite Unique Index ===
db.products.createIndex({ sku: 1, variant: 1 }, { unique: true });
// (sku, variant) The combination must be unique.
// === Unique Index + Selected Fields ===
db.users.createIndex(
{ email: 1 },
{ unique: true, partialFilterExpression: { email: { $exists: true } } }
);
// Only for existing email Apply a unique constraint to the field's documentation
تحليل النقاط الرئيسية:
- يسمح الفهرس الفريد بوجود القيمة
null، ولكن لا يمكن أن توجد سوى قيمة واحدةnullفي المجموعة بأكملها (تُعتبر القيمة «null» نفس القيمة). - يتيح استخدام
partialFilterExpressionتطبيق قيد التفرد على بعض المستندات فقط، مما يحل مشكلة التفرد الفارغ. - قبل إنشاء فهرس فريد، يجب ألا تحتوي المجموعة على أي قيم مكررة؛ وإلا فسيفشل الإنشاء.
3. الفهارس المتفرقة
شرح المفهوم: يقوم الفهرس المتفرق بفهرسة المستندات التي تحتوي على الحقل المفهرس فقط، شريطة ألا يكون هذا الحقل فارغًا، متخطيًّا المستندات التي يفتقد فيها هذا الحقل أو يكون فارغًا. أما الفهرس العادي فيقوم بفهرسة جميع المستندات (معاملة الحقول المفقودة على أنها فارغة)، في حين أن الفهرس المتفرق يوفر المساحة عن طريق استبعاد الإدخالات غير الصالحة.
كيفية العمل: عند إنشاء فهرس متفرق، يتحقق المحرك من وجود الحقل المفهرس ومن أنه ليس فارغًا بعد إدراج المستند؛ ولا يقوم بإدراج مدخل فهرسي في شجرة B+ إلا في حالة استيفاء هذين الشرطين. وأثناء إجراء الاستعلام، في حالة استخدام فهرس متفرق، لن تتضمن النتائج المستندات التي تحتوي على حقول مفقودة (لأنها غير موجودة في الفهرس).
حالات الاستخدام:
- الحقول الاختيارية (مثل
discountوmiddleName)؛ هذا الحقل غير موجود في معظم الوثائق - فهرس فريد + متفرق = يسمح لعدة مستندات بعدم احتوائها على هذا الحقل مع الحفاظ في الوقت نفسه على تفرد القيم الموجودة
- غير مناسب: الاستعلامات التي تتطلب مستندات تحتوي على حقول مفقودة (لا يمكن للفهارس المتفرقة تغطية هذه المستندات)
graph LR
subgraph "Collection of Documents"
D1["{sku:'A', discount:10}"]
D2["{sku:'B'}"]
D3["{sku:'C', discount:null}"]
D4["{sku:'D', discount:20}"]
end
subgraph "Regular Index"
I1["A→D1, B→D2, C→D3, D→D4"]
end
subgraph "Sparse Index"
I2["10→D1, 20→D4<br/>(only 2 entries)"]
end
style I2 fill:#d4edda
| السيناريو | التوصية | السبب |
|---|---|---|
| غالبًا ما تكون الحقول مفقودة (مثل الحقول الاختيارية) | فهرس متفرق | يوفر المساحة من خلال فهرسة المستندات التي تحتوي على قيم فقط |
| يجب أن تحتوي الحقول على قيم | الفهرس العادي | لا داعي للتخطي؛ الفهرس المتفرق لا يقدم أي ميزة |
| فريد + يسمح بوجود عدة قيم فارغة | فهرس فريد متفرق | لا يتم تضمين القيم الفارغة في عمليات التحقق من التفرد |
| يجب أن تُرجع الاستعلامات المستندات ذات القيم الفارغة | الفهرس العادي | الفهارس المتفرقة تتجاهل المستندات ذات القيم الفارغة |
// === Sparse Index:Skip null Field ===
db.products.createIndex({ discount: 1 }, { sparse: true });
// Index only discount Field Documentation
// === Sparse Index vs Regular Index ===
// Regular Index:All documents are indexed(Including null)
// Sparse Index:Only non- null Indexing Documents
تحليل النقاط الرئيسية:
- لا تشمل الفهارس المتفرقة الوثائق التي تفتقد إلى حقول معينة؛ ولن تعرض
find({discount: null})الوثائق التي تفتقد إلى ذلك الحقل. - تركيبة «متفرقة + فريدة»: تسمح بعدم وجود هذا الحقل في مستندات متعددة مع ضمان أن تكون القيم الموجودة فريدة
- تُعد الفهارس الجزئية مجموعة شاملة للفهارس المتفرقة (وهي أكثر مرونة)؛ ويُنصح باستخدام الفهارس الجزئية أولاً.
4. فهارس TTL (انتهاء الصلاحية التلقائي)
شرح المفهوم: مؤشر TTL (Time-To-Live) هو آلية الانتهاء والحذف التلقائية في MongoDB، والتي تقوم تلقائيًا بحذف المستندات التي تجاوزت فترة زمنية محددة استنادًا إلى حقل التاريخ. ولا يتطلب ذلك أي عملية تنظيف يدوية أو مهام مجدولة؛ حيث يتولى محرك قاعدة البيانات هذه المهمة تلقائيًا في الخلفية — مما يجعله أداة قوية لإدارة الجلسات وتنظيف السجلات.
كيفية العمل: يضيف فهرس TTL خيطًا للتنظيف في الخلفية إلى شجرة B+. يعمل هذا الخيط كل 60 ثانية، ويقوم بمسح حقول التاريخ في الفهرس، وحساب currentTime - fieldValue > expireAfterSeconds، وحذف جميع المستندات منتهية الصلاحية. تتسبب عملية الحذف بحد ذاتها في عبء إضافي على الكتابة، وقد يؤدي وجود عدد كبير من المستندات منتهية الصلاحية إلى ارتفاع مفاجئ في ضغط الكتابة.
sequenceDiagram
participant App as Applications
participant TTL as TTLBackground Threads
participant IX as TTLIndexB+Tree
participant DOC as Collection of Documents
App->>DOC: Insert {token:'abc', createdAt: T1}
DOC->>IX: Index Entries createdAt=T1
Note over TTL: Every 60 seconds
loop Every 60 seconds
TTL->>IX: Scan createdAt values
IX-->>TTL: Back to All Entries
TTL->>TTL: Calculate the current time - createdAt
alt Expired ( > expireAfterSeconds )
TTL->>DOC: Delete Expired Documents
DOC->>IX: Delete the corresponding index entry
else Not expired
Note over TTL: Skip
end
end
حالات الاستخدام:
| السيناريو | مدة الصلاحية بالثواني | القيمة النموذجية |
|---|---|---|
| جلسة المستخدم | 30 دقيقة | 1,800 |
| رمز التحقق | 10 دقائق | 600 |
| بيانات السجل | تُحتفظ بها لمدة 90 يومًا | 7,776,000 |
| رمز مؤقت | ساعة واحدة | 3,600 |
| سجل تحديد معدل | يوم واحد | 86,400 |
⚠️ حد TTL:
| القيد | الوصف | الحل |
|---|---|---|
| لا يمكن أن يكون فهرسًا مركبًا | لا يمكن أن يستند TTL إلا إلى حقل تاريخ واحد | بالنسبة للشروط المركبة، استخدم فهرسًا جزئيًا + عملية تنظيف على مستوى التطبيق |
| يجب أن يكون الحقل من نوع «تاريخ» | لا يُسمح باستخدام أنواع «رقم» أو «سلسلة» | استخدم new Date() لتخزين الوقت |
| تأخير الحذف | يقوم الخيط الخلفي بالمسح مرة كل 60 ثانية | الحد الأقصى للتأخير هو 60 ثانية؛ والوقت ليس دقيقًا |
| لا يمكن ضمان دقة عملية الحذف | قد يتم حذف أعداد كبيرة من المستندات منتهية الصلاحية على دفعات | ضمان القدرة على تحمل الأعطال على مستوى طبقة الأعمال |
// === Create TTL Index(30 Automatically Deleted by the Queen)===
db.sessions.createIndex(
{ createdAt: 1 },
{ expireAfterSeconds: 30 * 24 * 60 * 60 }
);
// === Edit TTL ===
db.runCommand({
collMod: 'sessions',
index: { keyPattern: { createdAt: 1 }, expireAfterSeconds: 7 * 24 * 60 * 60 }
});
// === Cancel TTL(Set as false)===
db.runCommand({
collMod: 'sessions',
index: { keyPattern: { createdAt: 1 }, expireAfterSeconds: -1 }
});
السيناريوهات النموذجية:
- جلسة المستخدم (تنتهي صلاحيتها بعد 30 دقيقة)
- رمز التحقق (تنتهي صلاحيته بعد 10 دقائق)
- بيانات السجل (يتم الاحتفاظ بها لمدة 90 يومًا)
- رمز مؤقت (ينتهي صلاحيته بعد ساعة واحدة)
5. الفهارس الجزئية
شرح المفهوم: الفهرس الجزئي لا يفهرس سوى الوثائق التي تستوفي معايير التصفية؛ وهو نسخة محسّنة من الفهرس المتفرق. ومن خلال استخدام partialFilterExpression لتحديد الوثائق التي يتم تضمينها في الفهرس، فإنه يوفر المساحة ويحسّن دقة الفهرس في آن واحد، مما يجعله إحدى أكثر طرق تحسين الفهرس الموصى بها لبيئات الإنتاج.
كيفية العمل: عند إنشاء فهرس جزئي، يقوم المحرك بإدراج المستندات التي تستوفي partialFilterExpression فقط في شجرة B+Tree. وأثناء تنفيذ الاستعلام، لن يختار المُحسِّن هذا الفهرس إلا إذا كانت شروط الاستعلام «تغطي» تعبير التصفية الجزئي (partialFilterExpression) (أي أن تكون شروط الاستعلام مجموعة فرعية من شروط التصفية أو مكافئة لها)؛ وإلا، فلن يتم استخدامه.
حالات الاستخدام:
- فهرسة «المنتجات المعروضة للبيع» فقط (
isActive: true, stock: {$gt: 0})، مع تجاهل المنتجات التي تم إيقاف إنتاجها أو التي نفد مخزونها - فهرسة «المستخدمين المعتمدين» فقط (
emailVerified: true)، وتخطي المستخدمين غير المعتمدين - تنطبق القيود المتعلقة بالتفرد على مستندات معينة فقط (على سبيل المثال، يجب أن تكون عناوين المقالات المنشورة فريدة، في حين يُسمح بوجود عناوين مكررة في المسودات)
graph TB
subgraph "Gathering(10000Document)"
A[All Documents<br/>10000 items]
end
subgraph "Regular Index"
B[Index Entries<br/>10000 items<br/>~10MB]
end
subgraph "PartialIndex<br/>isActive=true, stock>0"
C[Index Entries<br/>3000 items<br/>~3MB]
end
A --> B
A --> C
style C fill:#d4edda
| معايير المقارنة | الفهرس العادي | الفهرس الجزئي | الفهرس المتفرق |
|---|---|---|---|
| شروط التصفية | لا شيء | يدعم $eq/$gt/$gte/$lt/$lte/$exists/$type/$and | وجود الحقل فقط |
| توفير المساحة | 0% | 30–70% | يعتمد على نسبة البيانات المفقودة |
| المرونة | المعيار | ⭐⭐⭐ الأكثر مرونة | ⭐ الأكثر تقييدًا |
| قيود الاستعلام | لا شيء | يجب أن تتطابق شروط الاستعلام مع شروط التصفية | يجب أن تتضمن الاستعلامات شروطًا متعلقة بالحقول |
| موصى به | الافتراضي | ✅ مفضل للاستخدام في بيئة الإنتاج | لسيناريوهات بسيطة فقط |
| التعبير | الدعم |
|---|---|
$eq / $gt / $gte / $lt / $lte |
✅ |
$exists: true |
✅ |
$type |
✅ |
$and |
✅ |
$or / $in / $nin |
❌ |
// === Selected Indexes:Index only documents that meet the criteria ===
db.products.createIndex(
{ category: 1, price: 1 },
{
partialFilterExpression: {
isActive: true,
stock: { $gt: 0 }
}
}
);
// Index only products currently for sale(isActive=true, stock>0)
// === Space-Saving Indexes ===
// Regular Index:1 Index all documents
// Partial Index: Only index 3000 products currently for sale (Saves 70% space)
▶ المثال 1: الفهرسة الجزئية في الممارسة العملية
// ShopHub:Index only products that are for sale and in stock,Savings 70% Index Space
db.products.insertMany([
{ sku: 'A001', category: 'Electronics', price: 599, isActive: true, stock: 50 },
{ sku: 'A002', category: 'Electronics', price: 299, isActive: false, stock: 0 },
{ sku: 'A003', category: 'Books', price: 29, isActive: true, stock: 100 },
{ sku: 'A004', category: 'Books', price: 49, isActive: true, stock: 0 }
]);
// Partial Index only isActive=true AND stock>0 the document(A001, A003)
db.products.createIndex(
{ category: 1, price: 1 },
{ partialFilterExpression: { isActive: true, stock: { $gt: 0 } } }
);
// The query must include filter Conditions for a hit
db.products.find({
category: 'Electronics',
isActive: true,
stock: { $gt: 0 },
price: { $gte: 100 }
}).explain();
// winningPlan.stage: IXSCAN ✅
// Missing filter Conditions → Missed
db.products.find({ category: 'Electronics', price: { $gte: 100 } }).explain();
// winningPlan.stage: COLLSCAN(Do not use partial Index)
الإخراج:
TEXT 📖 للعرض فقطالاستعلام مع شروط التصفية: winningPlan.stage: IXSCAN ✅ الاستعلام بدون شروط التصفية: winningPlan.stage: COLLSCAN (لا يستخدم الفهرس الجزئي) تم توفير 70% من مساحة الفهرس بفهرسة المنتجات النشطة فقط.
6. قواعد ESR
Equality → Sort → Range: القاعدة الذهبية لترتيب الحقول في الفهرس المركب.
شرح المفهوم: تعد قاعدة ESR أهم قاعدة في تصميم الفهارس في MongoDB. وهي تحدد الترتيب الأمثل للحقول في الفهرس المركب: حيث تأتي شروط المساواة أولاً، ثم حقول الفرز في الوسط، وأخيراً شروط النطاق. وقد يؤدي انتهاك قاعدة ESR إلى انخفاض كبير في كفاءة الفهرس أو حتى إلى جعله عديم الفعالية.
كيف يعمل:
- يتم فرز الحقول المتساوية أولاً: حيث يمكن لشروط التساوي أن تضيق نطاق البحث بسرعة من N إلى K (K << N)، مما يوفر نقطة انطلاق أكثر دقة للحقول اللاحقة
- حقل الفرز في الوسط: نظرًا لأن الفهارس مرتبة بطبيعتها، فإذا جاء حقل الفرز مباشرةً بعد حقل المساواة، تكون نتائج الاستعلام مرتبة بالفعل حسب ترتيب الفهرس، مما يلغي الحاجة إلى الفرز داخل الذاكرة (وتجنب مرحلة SORT).
- ضع حقل النطاق في النهاية: تقوم استعلامات النطاق بمسح نطاق متجاور في الفهرس، ولا يمكن للحقول اللاحقة ضمن هذا النطاق الاستفادة من الترتيب الذي يتميز به الفهرس؛ ولذلك، يجب وضع حقل النطاق في النهاية.
graph TB
subgraph "ESR Index {status, createdAt, price}"
A["Equality: status='paid'<br/>→ Locate all paid Document"] --> B["Sort: createdAt: -1<br/>→ The index is sorted,Memory-Free Sorting"]
B --> C["Range: price >= 100<br/>→ Range Scan,Interrupt Subsequent Fields"]
end
subgraph "Counterexample:RSE Index {price, status, createdAt}"
D["Range: price >= 100<br/>→ Scan a large number of index entries"] --> E["Sort: status<br/>→ Memory-based sorting required"]
E --> F["Equality: createdAt<br/>→ The index can no longer be used"]
end
style A fill:#d4edda
style B fill:#cce5ff
style C fill:#fff3cd
style D fill:#f8d7da
style E fill:#f8d7da
style F fill:#f8d7da
جدول مقارنة قواعد ESR:
| الترتيب | النوع | مثال | الوظيفة |
|---|---|---|---|
| الأول | المساواة | category: 'Electronics' |
تضييق نطاق الخيارات بسرعة |
| الثاني | المساواة | isActive: true |
تضييق نطاق الموضوع أكثر |
| 3 | الفرز | sort: { createdAt: -1 } |
يستخدم الطبيعة المرتبة للفهرس لتجنب عملية الفرز |
| الرابع | النطاق | price: { $gte: 100 } |
مسح النطاق، وضعه في النهاية |
عواقب مخالفة قواعد ESR:
| الترتيب غير الصحيح | العواقب | التفسير |
|---|---|---|
| النطاق قبل تحقيق المساواة | نطاق المسح واسع جدًّا | totalKeysExamined أكبر بكثير من nReturned |
| الفرز حسب النطاق | لا يمكن استخدام فهرس للفرز | تتم مرحلة SORT (الفرز في الذاكرة) |
| تخطي مرحلة المساواة والبدء في الفرز مباشرةً | الفرز دون نقطة بداية محددة | المسح الكامل للفهرس + الفرز |
// === Search:Equivalence Filtering + Sort + Scope ===
db.products.find({
category: 'Electronics', // Equality
isActive: true // Equality
}).sort({ createdAt: -1 }) // Sort
.limit(20);
// Price Range
db.products.find({
category: 'Electronics',
createdAt: { $gte: new Date('2026-01-01') } // Range
}).sort({ rating: -1 });
// === Best Index ===
db.products.createIndex({
category: 1, // E - Equivalent
isActive: 1, // E - Equivalent
createdAt: -1, // S - Sort(Index Direction Matching)
rating: -1 // R - Scope(Save it for last)
});
| الترتيب | النوع | مثال |
|---|---|---|
| الأول | المساواة | category: 'Electronics' |
| الثاني | المساواة | isActive: true |
| الثالث | الفرز | sort: { createdAt: -1 } |
| الرابع | المدى | createdAt: { $gte: ... } |
▶ المثال 2: مقارنة عملية لقواعد ESR
// TechCorp Order System:Comparison ESR Correct vs Execution Plan with Incorrect Ordering
db.orders.insertMany([
{ userId: 'user_001', status: 'paid', createdAt: new Date('2026-07-01'), total: 100 },
{ userId: 'user_002', status: 'pending', createdAt: new Date('2026-07-02'), total: 200 },
{ userId: 'user_001', status: 'paid', createdAt: new Date('2026-07-03'), total: 50 }
]);
// ❌ Index Errors: Range before Sort
db.orders.createIndex({ userId: 1, total: 1, status: 1, createdAt: -1 }, { name: 'idx_wrong' });
db.orders.find({ userId: 'user_001', status: 'paid' })
.sort({ createdAt: -1 }).explain('executionStats');
// appear SORT stage(Memory Sorting),totalKeysExamined much greater than nReturned
// ✅ Correct Indexing:ESR Order
db.orders.dropIndex('idx_wrong');
db.orders.createIndex({ userId: 1, status: 1, createdAt: -1, total: 1 }, { name: 'idx_esr' });
db.orders.find({ userId: 'user_001', status: 'paid' })
.sort({ createdAt: -1 }).explain('executionStats');
// No SORT stage, totalKeysExamined ≈ nReturned
الإخراج:
TEXT 📖 للعرض فقطالفهرس الخاطئ (RSE): تظهر مرحلة SORT (فرز في الذاكرة)، totalKeysExamined أكبر بكثير من nReturned الفهرس الصحيح (ESR): لا توجد مرحلة SORT، totalKeysExamined ≈ nReturned تحسين الأداء: إزالة الفرز في الذاكرة وتقليل عدد مفاتيح الفهرس المفحوصة.
7. أفضل الممارسات لتصميم الفهارس
شرح المفهوم: تصميم الفهرس ليس مجرد قرار تقني منعزل، بل هو عملية موازنة شاملة تستند إلى أنماط الاستعلامات وخصائص البيانات ومتطلبات العمل. فالتصميم الجيد للفهرس يمكن أن يجعل الاستعلامات تسير بسرعة فائقة، في حين أن التصميم السيئ يهدر المساحة ويبطئ عمليات الكتابة.
مبادئ التصميم:
- التصميم القائم على الاستعلامات — قم أولاً بتحليل
explain()وسجل الاستعلامات البطيئة، ثم قم بإنشاء الفهارس - أولوية ESR — يتبع ترتيب الحقول في الفهرس المركب الترتيب التالي: المساواة → الفرز → النطاق
- إعطاء الأولوية للانتقائية العالية — الحقول التي تحتوي على العديد من القيم الفريدة (مثل
userId) أكثر ملاءمة للفهرسة من الحقول ذات الانتقائية المنخفضة (مثلisActive) - تغطية الفهرس — ضع في اعتبارك استخدام تغطية الفهرس للاستعلامات عالية التكرار لتجنب عمليات البحث في الجداول
- التنظيف الدوري — استخدم
$indexStatsللبحث عن الفهارس غير المستخدمة وحذفها
graph TB
A[Analyzing Query Patterns<br/>Slow Query Log] --> B[Identifying High-Frequency Queries]
B --> C{Query Type?}
C -->|Equal Value Query| D[Single field/Unique Index]
C -->|Multiple Condition Combinations| E[Composite Index ESR]
C -->|Sort+Filter| F[ESR Composite Index]
C -->|Show only a few fields| G[Index Coverage]
B --> H[Evaluating Options]
H -->|High selectivity| I[✅ Create an index]
H -->|Low selectivity| J[❌ Do not build / use partial]
I --> K[Create+Verification<br/>explain]
K --> L[Monitoring Utilization<br/>$indexStats]
L --> M{Low utilization rate?}
M -->|Yes| N[Delete Index]
M -->|No| O[Retain]
style D fill:#d4edda
style E fill:#d4edda
style F fill:#d4edda
style G fill:#d4edda
(1) النهج الموصى به
// ✅ Recommendations 1:Single-Field Indexes Cover High-Frequency Queries
db.products.createIndex({ sku: 1 });
// ✅ Recommendations 2:Composite indexes follow ESR Principles
db.orders.createIndex({ userId: 1, status: 1, createdAt: -1 });
// ✅ Recommendations 3:TTL Automatic Cleanup
db.logs.createIndex({ createdAt: 1 }, { expireAfterSeconds: 30 * 86400 });
// ✅ Recommendations 4:Space-Saving Indexes
db.products.createIndex(
{ category: 1 },
{ partialFilterExpression: { isActive: true } }
);
// ✅ Recommendations 5:Unique indexes ensure data integrity
db.users.createIndex({ email: 1 }, { unique: true });
(2) الأنماط السلبية
الأنماط الخاطئة الشائعة وعواقبها:
| النمط الخاطئ | العواقب | أفضل الممارسات |
|---|---|---|
| حقول الفهرسة ذات الانتقائية المنخفضة | كفاءة الفهرس ضعيفة؛ وقد يقرر مُحسِّن الأداء عدم استخدامه | استخدم فهرسًا جزئيًا أو فهرسًا مركبًا |
| عدد الفهارس كبير جدًّا (>10 لكل مجموعة) | انخفاض حاد في أداء الكتابة | الاحتفاظ فقط بالفهارس الخاصة بالاستعلامات المتكررة |
| ترتيب غير صحيح لحقول الفهرس المركب | فشل جزئي في الفهرس | الامتثال لقواعد ESR |
| الفهارس غير الضرورية | المساحة المهدرة + عبء الكتابة | قم بالتنظيف بانتظام باستخدام $indexStats |
| تم الوصول إلى فهرس غير مُتحقق منه | ربما لم يتم استخدام الفهرس على الإطلاق | يجب تشغيل EXPLAIN للتحقق بعد الإنشاء |
// ❌ Anti-pattern 1:Indexing Fields with Low Selectivity
db.users.createIndex({ isActive: 1 });
// isActive Only true/false Two values,Low index efficiency
// ❌ Anti-pattern 2:Excessive Indexing
// A set containing more than 20 Index,Write performance has dropped significantly
// ❌ Anti-pattern 3:Incorrect Order of Fields in a Composite Index
db.products.createIndex({ price: 1, category: 1 });
// Wrong: price should be placed after category
// ❌ Anti-pattern 4:Unnecessary Indexes
db.users.createIndex({ lastLoginAt: 1 });
// If you rarely press lastLoginAt Search,Index Waste
8. مراقبة أداء المؤشر
شرح المفهوم: تُعد مراقبة الفهارس جزءًا أساسيًا من العمليات في بيئة الإنتاج. ومن خلال تحليل سجلات الاستعلامات البطيئة وإحصائيات استخدام الفهارس، يمكنك تحديد الفهارس غير المستخدمة (التي تهدر المساحة)، والفهارس غير الفعالة (التي تحتاج إلى تحسين)، والفهارس المفقودة (التي يجب إنشاؤها).
كيفية العمل: يقوم أداة التحليل المدمجة في MongoDB بتسجيل الاستعلامات البطيئة، بينما يتتبع مسار التجميع $indexStats عدد مرات استخدام كل فهرس والوقت المستغرق في كل استعلام. وتوفر هذه العناصر مجتمعةً نظرة شاملة على حالة الفهرس.
مقاييس الرصد:
| المقياس | الأمر | القيمة السليمة | قيمة التحذير |
|---|---|---|---|
| استخدام الفهرس | $indexStats |
العمليات > 1,000/يوم | العمليات = 0 (غير مستخدم) |
| عدد الاستعلامات البطيئة | system.profile |
0 | > 10 في الساعة |
| حجم الفهرس | totalIndexSize() |
أقل من 50% من البيانات | أكثر من 100% من البيانات |
| تجزئة الفهرس | collStats |
متوسط معدل الملء > 80% | < 60% |
// === Slow Query Analysis ===
db.setProfilingLevel(2, { slowms: 100 });
// Records exceeding 100ms Queries
// === View Slow Queries ===
db.system.profile.find({ millis: { $gt: 100 } })
.sort({ ts: -1 })
.limit(10);
// === View Index Usage Statistics ===
db.products.aggregate([
{ $indexStats: {} }
]);
// Find unused index
// === Delete Unused Indexes ===
db.products.dropIndex('unused_index_name');
تحليل النقاط الرئيسية:
- مستوى التحليل: 0 = معطل، 1 = الاستعلامات البطيئة فقط، 2 = جميع السجلات (يؤثر المستوى 2 على الأداء وهو مخصص لأغراض تصحيح الأخطاء فقط)
- إذا كانت قيمة
accesses.opsالخاصة بـ$indexStatsتساوي 0، فهذا يعني أنها لم تُستخدم مطلقًا منذ بدء تشغيل MongoDB. - بيئة الإنتاج الموصى بها: المستوى 1 + slowms: 100، لتحقيق التوازن بين دقة المراقبة والأداء
▶ مثال: مؤشر TTL + المؤشر الجزئي + ESR في الممارسة العملية
// Scene 1:TTL Automatically Clear Session Indexes(30 Expires in minutes)
db.sessions.insertMany([
{ userId: 'user_001', token: 'abc', createdAt: new Date() },
{ userId: 'user_002', token: 'xyz', createdAt: new Date(Date.now() - 31 * 60 * 1000) } // Expired
]);
db.sessions.createIndex(
{ createdAt: 1 },
{ expireAfterSeconds: 30 * 60 } // 30 minutes
);
// Waiting 60 seconds later,Expired documents are automatically deleted
// db.sessions.find() → Only user_001 The conversation
// Scene 2:Selected Indexes - Index only products currently for sale(Savings 70% Space)
db.products.createIndex(
{ category: 1, price: 1 },
{
partialFilterExpression: {
isActive: true,
stock: { $gt: 0 }
}
}
);
// Some indexes are only used when the query contains filter Use when conditions apply
db.products.find({
category: 'Electronics',
isActive: true, // Must match partialFilterExpression
stock: { $gt: 0 }, // Must match partialFilterExpression
price: { $gte: 100, $lte: 500 }
}).explain();
// winningPlan.inputStage.stage: IXSCAN(Partial indexing was used)
// Scene 3:ESR Rules in Practice - Order Inquiry
db.orders.insertMany([
{ userId: 'user_001', status: 'paid', createdAt: new Date('2026-07-01'), total: 100 },
{ userId: 'user_001', status: 'paid', createdAt: new Date('2026-07-02'), total: 200 },
{ userId: 'user_002', status: 'pending', createdAt: new Date('2026-07-03'), total: 50 }
]);
// ESR Best Index:Equality → Sort → Range
الإخراج:
TEXT 📖 للعرض فقطTTL: تم حذف الجلسات المنتهية صلاحيتها تلقائيًا. Partial Index: تم توفير 70% من المساحة بفهرسة المنتجات النشطة فقط. ESR: تم تحسين أداء استعلام الطلبات بإزالة مرحلة الفرز في الذاكرة.
db.orders.createIndex({ userId: 1, // E - Equivalence Filtering status: 1, // E - Equivalence Filtering createdAt: -1, // S - Sort(Directional Matching) total: 1 // R - Range Query(Save it for last) });
// Efficient Queries:Orders Paid by the User,In reverse chronological order,Price Range db.orders.find({ userId: 'user_001', // E status: 'paid', // E total: { $gte: 50, $lte: 300 } // R }).sort({ createdAt: -1 }).limit(20) // S .explain('executionStats');
// Output:Use composite indexes exclusively,No additional sorting required // totalKeysExamined: 2, totalDocsExamined: 2, nReturned: 2
> النتيجة: يقوم TTL تلقائيًا بمسح الجلسات المنتهية الصلاحية؛ وتساعد بعض الفهارس في توفير المساحة؛ كما تعمل الفهارس المركبة في ESR على تحسين أداء الاستعلامات.
---
## ❓ أسئلة شائعة
> **س: ما هي المدة التي يستغرقها حذف المستندات من فهرس TTL؟** **ج: يقوم الخيط الخلفي بمسح الفهرس مرة كل 60 ثانية. ويبلغ الحد الأقصى للتأخير 60 ثانية مضافًا إليها مدة صلاحية المستند.**
> **س: الفرق بين الفهارس الجزئية والفهارس المتفرقة؟** **ج: تدعم الفهارس الجزئية شروطًا أكثر تعقيدًا (`$gt/$gte/$and`)، بينما تعتمد الفهارس المتفرقة حصريًّا على وجود الحقل من عدمه. يُنصح باستخدام الفهارس الجزئية (لأنها أكثر مرونة).**
> **س: ما هو اتجاه حقل الفرز في قواعد ESR؟** **ج: يجب أن يتطابق اتجاه الفرز مع اتجاه الفهرس. يستخدم `sort({ createdAt: -1 })` فهرس `createdAt: -1`.**
> **س: متى يتم حذف الفهارس؟** **ج: (1) يدويًّا `dropIndex`؛ (2) حذف المجموعة؛ (3) انتهاء صلاحية الفهرس تلقائيًّا (حقول التاريخ فقط).**
---
## 📖 ملخص
- الفهرس الفريد: يضمن أن تكون قيم الحقول فريدة
- فهرس متفرق: يتخطى الحقول ذات القيمة «null»
- مؤشر TTL: انتهاء الصلاحية والحذف التلقائيان
- الفهرسة الجزئية: لا يتم فهرسة سوى الوثائق التي تستوفي المعايير
- قاعدة ESR: المساواة → الفرز → النطاق
- تصميم الفهارس: قم بإنشاء فهارس للحقول التي تتكرر الاستعلامات عليها بشكل كبير؛ وتوخى الحذر عند إنشاء فهارس للحقول ذات الانتقائية المنخفضة؛ وتأكد من أن الفهارس المركبة تتبع مبدأ ESR.
---
## 📝 تمارين
1. **السؤال الأساسي (⭐)**: قم بإنشاء فهرس فريد على العمود `email` لمجموعة `users`.
2. **السؤال الأساسي (⭐)**: أنشئ فهرس TTL لمجموعة `sessions` (تنتهي صلاحيته بعد 30 دقيقة).
3. **تمرين متقدم (⭐⭐)**: قم بإنشاء فهرس جزئي لمجموعة `products` (يقتصر على المنتجات المعروضة للبيع حاليًا).
4. **مشكلة متقدمة (⭐⭐)**: صمم فهرسًا مثاليًّا لمجموعة `orders` باستخدام قاعدة ESR.
5. **التحدي (⭐⭐⭐)**: تصميم مخطط كامل لفهرسة التجارة الإلكترونية (10 فهارس أو أكثر)، وتحليل الاستخدام باستخدام $indexStats، وحذف الفهارس غير المستخدمة.