MongoDB: تحسين الأداء ومراقبته: ضبط الأداء على مستوى الإنتاج
آخر تحديث: 2026-08-26
يُعد تحسين الأداء ومراقبته «المرحلة الأخيرة» في بيئة الإنتاج — حيث إن إتقان هذين الأمرين يمكن أن يمنع 90% من حوادث الأداء.
1. ما ستتعلمه
- ضبط تجمع الاتصالات
- تحسين الاستعلامات (hint / projection / lean)
- تحليل سجل الاستعلامات البطيئة
- أدوات مراقبة APM
- قائمة مراجعة لنشر الإنتاج
graph LR
App[Node.js Applications] -->|Request| Pool[Connection Pool<br/>maxPoolSize=50]
Pool -->|Connect 1| M1[mongod<br/>Primary]
Pool -->|Connect 2| M2[mongod<br/>Secondary]
Pool -->|Connect 3| M3[mongod<br/>Secondary]
M1 -->|Copy| M2
M1 -->|Copy| M3
App -.->|explain| M1
App -.->|indexStats| M1
style Pool fill:#d4edda
style M1 fill:#cce5ff
2. ضبط تجمع الاتصالات
ما أهمية تجمع الاتصالات؟ في كل مرة يقوم فيها MongoDB بإنشاء اتصال TCP، يتطلب ذلك: مصافحة ثلاثية + مصادقة SCRAM (3 جولات ذهاب وإياب) + تهيئة الجلسة — وهو ما يستغرق ما يقارب 10–50 مللي ثانية. في سيناريوهات التزامن العالي، إذا لم يكن هناك تجمع اتصالات، فإن إنشاء اتصال جديد لكل طلب قد يؤدي إلى «عاصفة اتصالات» تربك قاعدة البيانات. يقلل تجميع الاتصالات من وقت إنشاء الاتصال إلى أقل من 1 مللي ثانية عن طريق التسخين المسبق للاتصالات وإعادة استخدامها.
كيف تعمل مجموعات الاتصالات:
graph LR
subgraph "Node.js Applications"
R1[Request1] -->|Loan Out| Pool[(Connection Pool<br/>min=5 max=50)]
R2[Request2] -->|Loan Out| Pool
R3[Request3] -->|Wait in line| Queue[Waiting Queue<br/>waitQueueTimeoutMS]
end
Pool -->|Active Connections| Active[mongod Primary]
Pool -->|Idle Connections| Idle[Free Space Recovery<br/>maxIdleTimeMS]
R1 -.->|Return| Pool
R3 -.->|Get the Return Link| Pool
style Pool fill:#d4edda
style Queue fill:#fff3cd
style Active fill:#cce5ff
دليل ضبط معلمات تجمع الاتصالات:
| المعلمة | القيمة الافتراضية | استراتيجية الضبط | مخاطر الإعداد بقيمة عالية جدًّا | مخاطر الإعداد بقيمة منخفضة جدًّا |
|---|---|---|---|---|
| maxPoolSize | 100 | ذروة التزامن × 1.5 | استنفاد اتصالات قاعدة البيانات | انتهاء مهلة قائمة انتظار الطلبات |
| minPoolSize | 0 | يُضبط على 10٪ من الذروة | بطء في بدء التشغيل، واستهلاك كبير للذاكرة | تأخير في بدء التشغيل البارد |
| maxIdleTimeMS | 0 | 30000 (30 ثانية) | إنشاء اتصالات جديدة بشكل متكرر | تسربات الاتصالات |
| waitQueueTimeoutMS | 0 | 10000 (10 ثوانٍ) | الطلبات المتراكمة | الطلبات في حالة الانتظار الدائم |
| serverSelectionTimeoutMS | 30000 | 5000 | تأخير طويل | فشل سريع |
صيغة حساب عدد الاتصالات: maxPoolSize = peak concurrency * average query time (seconds) * safety factor (1.5). على سبيل المثال، إذا كان هناك 100 اتصال متزامن وكان متوسط وقت الاستعلام 0.1 ثانية، فإن maxPoolSize = 100 * 0.1 * 1.5 = 15.
// === mongoose Connection Pool Configuration ===
mongoose.connect(uri, {
maxPoolSize: 50, // Maximum Number of Connections(Default 100)
minPoolSize: 5, // Minimum Number of Connections(Default 0)
maxIdleTimeMS: 30000, // Connection Idle Timeout(Default: Unlimited)
waitQueueTimeoutMS: 10000 // Connection timeout
});
// === Monitor Connection Pool ===
const db = mongoose.connection.db;
const stats = await db.admin().command({ serverStatus: 1 });
console.log('Connections:', stats.connections);
| maxPoolSize | قابل للتطبيق |
|---|---|
| 10 | التطبيقات الصغيرة / التطوير |
| 50 | التطبيقات المتوسطة / خدمات الويب |
| 100 | التطبيقات الكبيرة / حركة المرور الكثيفة |
| 200+ | وضع الكتلة / مصدر شبكة توزيع المحتوى (CDN) |
3. تحسين الاستعلامات
منهجية تحسين الاستعلامات: الهدف الأساسي لتحسين الاستعلامات هو «تقليل عبء العمل على قاعدة البيانات» — من خلال مسح عدد أقل من المستندات، ونقل عدد أقل من الحقول، وتخطي عمليات التسلسل غير الضرورية. خطوات التحسين: ① استخدام explain() لتشخيص المشكلة؛ ② التحقق مما إذا كان الفهرس قد تم الوصول إليه؛ ③ تقليل عدد الحقول التي يتم إرجاعها؛ ④ تقليل عدد السجلات التي يتم إرجاعها؛ ⑤ تخطي الأعباء الإضافية غير الضرورية لـ Mongoose.
أربع تقنيات أساسية لتحسين الاستعلامات:
| تقنية التحسين | المبدأ | التأثير | الكود |
|---|---|---|---|
| تفرض الدالة hint() استخدام الفهرس | تتجاوز الاختيار الخاطئ لمُحسِّن الاستعلام | COLLSCAN → IXSCAN | .hint({field: 1}) |
| إسقاط select() | يعرض الحقول الضرورية فقط | انخفاض نقل البيانات بنسبة 90%+ | .select('sku title price') |
| lean() تخطي عملية الترطيب | لا تقم بإنشاء مستند Mongoose | تحسن الأداء بمقدار 5 أضعاف | .lean() |
| limit() يحدد عدد السجلات | يمنع إرجاع عدد كبير جدًا من المستندات | يقلل من استهلاك الذاكرة | .limit(20) |
أنماط الاستعلامات البطيئة الشائعة وحلولها:
| وضع الاستعلامات البطيئة | السبب | الحل |
|---|---|---|
| COLLSCAN (مسح كامل للجدول) | مؤشر مفقود أو فشل في العثور على المؤشر | إنشاء مؤشر مناسب + hint() |
| تم إرجاع عدد كبير جدًا من الحقول | find() لا select |
إضافة إسقاط .select() |
| $where + تنفيذ جافا سكريبت | تنفيذ وظائف جافا سكريبت لكل مستند | التبديل إلى العوامل الأصلية |
| $regex: لا يوجد مرساة | لا يمكن استخدام الفهرس | أضف البادئة ^ لتعيين مرساة |
| تخطي الترقيم العميق للصفحات | تخطي (بطيء مع مجموعات البيانات الكبيرة) | التبديل إلى الترقيم باستخدام المؤشر |
| N+1 استعلامات | الاستعلام واحدًا تلو الآخر في حلقة | التبديل إلى $in أو $lookup |
(1) تُفرض الفهرسة بواسطة hint()
// === Force Index Usage ===
const products = await Product.find({ category: 'Electronics' })
.hint({ category: 1 });
// === mongoose Equivalent ===
const products = await Product.find({ category: 'Electronics' })
.hint('category_1');
(2) الإسقاط: تقليل عدد الحقول
// === Query only the necessary fields ===
const products = await Product.find()
.select('sku title price') // Don't fetch description, images, etc.
.lean();
// === Reduce Network Traffic 90%+ ===
(3) تتخطى الدالة lean() عملية الترطيب
// === General Query: Build mongoose Document (slow) ===
const products = await Product.find();
// === lean(): Return plain object directly (fast) ===
const products = await Product.find().lean();
(4) تجنب استخدام $where
// Slow: $where executes JavaScript
db.products.find({ $where: 'this.price > 1000' });
// Fast: Use $gt
db.products.find({ price: { $gt: 1000 } });
4. تحليل الاستعلامات البطيئة
عملية تحليل الاستعلامات البطيئة: تحديد الاستعلامات البطيئة → تمكين أداة التحليل (Profiler) → تحليل explain() → تحديد نقاط الاختناق → التحسين → التحقق. هناك ثلاثة مستويات للتحليل: 0 (معطل)، و1 (تسجيل الاستعلامات البطيئة)، و2 (تسجيل جميع الاستعلامات). استخدم المستوى 1 في بيئات الإنتاج؛ حيث يتسبب المستوى 2 في زيادة في استهلاك الموارد.
سير عمل تشخيص الاستعلامات البطيئة:
graph TD
Start[Identifying Slow Queries] --> Enable[Enable Profiling Level 1<br/>slowms=100]
Enable --> Collect[Collect Slow Query Logs<br/>system.profile]
Collect --> Explain[explain executionStats<br/>Analyze the Execution Plan]
Explain --> Stage{Stage Type?}
Stage -->|COLLSCAN| AddIdx[Add an Index]
Stage -->|IXSCAN| CheckProj[Check the projection/Number of Articles]
Stage -->|FETCH| OptProj[Optimization select]
AddIdx --> Verify[Verify the effectiveness of the optimization]
CheckProj --> Verify
OptProj --> Verify
Verify --> Done[Performance Meets Standards ✅]
style COLLSCAN fill:#f8d7da
style Done fill:#d4edda
تُعرض وظيفة explain() الحقول الرئيسية:
| الحقل | الوصف | القيمة الطبيعية | القيمة غير الطبيعية |
|---|---|---|---|
| المرحلة | طريقة المسح | IXSCAN | COLLSCAN |
| totalKeysExamined | مفاتيح الفهرس التي تم فحصها | ~ nReturned | >> nReturned |
| إجمالي الوثائق التي تم فحصها | الوثائق التي تم مسحها ضوئيًا | ~ عدد الوثائق التي تم إرجاعها | >> عدد الوثائق التي تم إرجاعها |
| nReturned | عدد المستندات التي تم إرجاعها | — | — |
| executionTimeMillis | وقت التنفيذ | < 100 مللي ثانية | > 1000 مللي ثانية |
| الفهرس المستخدم | الفهرس المستخدم | اسم الفهرس المركب | لا شيء (COLLSCAN) |
النسبة الذهبية: totalDocsExamined : nReturned ≈ 1:1. إذا تم إرجاع 20 سجلًا فقط بعد مسح 10,000 سجل، فهذا يعني أن الفهرس ليس دقيقًا بما يكفي.
// === Enable the slow query log ===
mongoose.connection.db.admin().command({
setParameter: 1,
slowms: 100 // Record > 100ms Queries
});
// === mongoose Enable in the middle debug ===
mongoose.set('debug', true);
// Output all queries to console
// === mongoose-debug Slow Query Log ===
mongoose.set('debug', (collectionName, method, query, doc) => {
const start = Date.now();
console.log(`${collectionName}.${method}(${JSON.stringify(query)})`);
});
5. تحسين الفهرس
المبادئ الأساسية لتحسين الفهارس: زيادة عدد الفهارس لا يعني بالضرورة تحسين الأداء — فكل فهرس يشغل مساحة على القرص، ويزيد من عبء الكتابة (نظرًا لأن الفهارس يجب تحديثها مع كل عملية إدراج أو تحديث أو حذف)، ويستهلك الذاكرة (تحاول MongoDB الاحتفاظ بالفهارس في ذاكرة الوصول العشوائي (RAM)). ويكمن مفتاح تحسين الفهارس في إنشاء فهارس مفيدة، وإزالة الفهارس غير الضرورية، واختيار نوع الفهرس المناسب.
مؤشرات فحص حالة المؤشر:
| المقياس | كيفية الحصول عليه | القيمة الطبيعية | القيمة غير الطبيعية |
|---|---|---|---|
| استخدام الفهرس | $indexStats | عدد عمليات الوصول > 0 | عدد العمليات = 0 (غير مستخدم) |
| حجم الفهرس | db.stats() | أقل من 50% من ذاكرة الوصول العشوائي (RAM) | أكبر من ذاكرة الوصول العشوائي (RAM) (تبادل متكرر) |
| عدد الفهارس | getIndexes() | أقل من 10 / المجموعة | أكثر من 20 (عدد كبير جدًا) |
| زمن انتقال الكتابة | حالة الخادم | < 10 مللي ثانية | > 100 مللي ثانية (الفهرس يبطئ عمليات الكتابة) |
قرارات تحسين الفهرس:
| السيناريو | الإجراء | السبب |
|---|---|---|
| فهرس بقيمة ops=0 | حذف | مضيعة تامة للمساحة وأداء الكتابة |
| الفهارس الزائدة | الاحتفاظ بالفهارس المركبة وإزالة الفهارس أحادية الحقل | {a:1, b:1} تغطي بالفعل {a:1} |
| مؤشر ذو قاعدة منخفضة | حذف أو تعديل بعض الفهارس | تحتوي الخاصية isActive على قيمتين فقط، لذا فإن انتقائيتها منخفضة |
| التحقق من الفهارس غير المستخدمة | إضافة أو استخدام hint() | كارثة في أداء COLLSCAN |
// === Index Usage Analysis ===
db.products.aggregate([{ $indexStats: {} }]);
// Find unused indexes
// === Index Usage in Slow Queries ===
db.system.profile.find({ millis: { $gt: 100 } })
.sort({ ts: -1 })
.limit(20)
.forEach(profile => {
console.log('Query:', profile.command.find);
console.log('Plan:', profile.planSummary);
console.log('Time:', profile.millis, 'ms');
});
6. أدوات مراقبة APM
لماذا تحتاج إلى APM؟ لا تُعطيك المشكلات في بيئات الإنتاج إنذارًا مسبقًا — فالارتفاعات المفاجئة في عدد الاتصالات، وزيادة عدد الاستعلامات البطيئة، وتسربات الذاكرة، كلها تحدث تدريجيًّا. توفر APM (مراقبة أداء التطبيقات) جمع المقاييس في الوقت الفعلي، ولوحات التحكم المرئية، وتنبيهات تجاوز الحدود القصوى، مما يتيح لك تحديد المشكلات وحلها قبل أن يشتكي المستخدمون.
هرم مؤشرات الرصد:
graph TB
L4[Business Metrics<br/>Orders/Error Rate/Response Time] --> L3[Application Metrics<br/>QPS/Latency/Node.js Memory]
L3 --> L2[Database Metrics<br/>Number of connections/Slow Queries/Index Hit Rate]
L2 --> L1[Infrastructure<br/>CPU/Memory/Disk/Internet]
style L4 fill:#d4edda
style L1 fill:#fff3cd
مؤشرات الأداء الرئيسية:
| فئة المقياس | المقياس المحدد | عتبة التنبيه | طريقة التجميع |
|---|---|---|---|
| الاتصالات | الاتصالات الحالية | > maxPoolSize × 80% | serverStatus.connections |
| الاستعلام | عدد الاستعلامات البطيئة | > 10/دقيقة | system.profile |
| الاستعلام | متوسط وقت الاستعلام | > 200 مللي ثانية | Mongoose Debug / APM |
| المؤشر | معدل تداول المؤشر | < 95% | $indexStats |
| الذاكرة | الذاكرة المقيمة | > 80% من ذاكرة الوصول العشوائي المتاحة | serverStatus.mem |
| القرص | استخدام القرص | > 80% | db.stats() |
| المثيل | تأخير النسخ المتماثل | > 10 ثوانٍ | rs.status().lag |
| الأداة | الميزات | التطبيقات |
|---|---|---|
| مراقبة MongoDB Atlas | رسمية، مدمجة | مستخدمو Atlas |
| Prometheus + mongo_exporter | مفتوح المصدر، مُستضاف ذاتيًا | بيئة إنتاج واسعة النطاق |
| Datadog APM | تجاري، كامل الميزات | مؤسسي |
| New Relic | الأعمال، مطور متكامل | المؤسسات |
| Prometheus + Grafana | مفتوح المصدر، مجاني | مراقبة ذاتية الاستضافة |
7. قائمة مراجعة لنشر الإنتاج
الاعتبارات الرئيسية لبيئات الإنتاج: لا يقتصر النشر في بيئة الإنتاج على «التأكد من تشغيل الكود» فحسب؛ بل يتعلق أيضًا بضمان التوافر العالي (ألا تؤثر نقطة فشل واحدة على الخدمة)، وأمن البيانات (عدم فقدان البيانات أو تسربها)، وقابلية المراقبة (القدرة على تحديد المشكلات بسرعة)، وقابلية الاستعادة (القدرة على الاستعادة من النسخ الاحتياطية).
الركائز الأربع لنشر الإنتاج:
| الركيزة | الهدف | المقاييس الرئيسية |
|---|---|---|
| التوافر العالي | توافر بنسبة 99.9% أو أكثر | مجموعة نسخ متماثلة مكونة من 3 عقد + التحويل التلقائي في حالة الفشل |
| أمن البيانات | عدم فقدان البيانات + عدم تسرب البيانات | مستوى الحماية عند الكتابة: الأغلبية + TLS + RBAC |
| قابلية الرصد | تحديد المشكلات بدقة في غضون 5 دقائق | السجلات + المقاييس + التنبيهات + APM |
| قابلية الاستعادة | RPO < 1 ساعة، RTO < 4 ساعات | النسخ الاحتياطية المجدولة + PITR + تدريبات الاستعادة |
---
## 8. Performance Optimization Checklist
---
### (1) Database
- [ ] Dungeon Collection 3 Node(High Availability)
- [ ] Write Concern majority(No data loss)
- [ ] Read Preference primaryPreferred
- [ ] Slow Query Monitoring Enabled
- [ ] Index Usage Monitoring
- [ ] Connection Pool maxPoolSize Settings
### (2) Application Layer
- [ ] mongoose lean() For read-only queries
- [ ] projection Search only the required fields
- [ ] bulkWrite Bulk Operations
- [ ] Avoid $where / $regex Unanchored
- [ ] Error-handling middleware
- [ ] Health Check Endpoints /healthz
### (3) Operations and Maintenance
- [ ] Daily mongodump Backup
- [ ] Backup Retention 7-30 days
- [ ] Monitoring Alerts(Number of connections / Slow Queries / Disk)
- [ ] TLS/SSL Encrypted Transmission
- [ ] SCRAM + RBAC Access Control
- [ ] Atlas PITR or oplog Continuous Backup
9. تدريب عملي: ضبط الأداء الشامل
منهجية عملية لتحسين الأداء: لا يقتصر تحسين الأداء على إجراء عمليات تحسين قائمة على التخمين؛ بل هو عملية ذات حلقة مغلقة تتمثل في «القياس → التحليل → التحسين → التحقق». أولاً، استخدم explain() لقياس الأداء الحالي وتحديد نقاط الاختناق (عمليات مسح كاملة للجداول؟ نقل مفرط للبيانات؟ عبء إضافي لـ Hydrate؟)، ثم قم بتنفيذ تحسينات محددة الهدف، وأخيرًا استخدم explain() مرة أخرى للتحقق من النتائج.
ضبط الأداء في الحلقة المغلقة:
graph LR
A[Measurement: explain + Slow Log] --> B[Analysis: Bottleneck Identification]
B --> C[Optimization: Index/Projection/lean]
C --> D[Verify: use explain again]
D -->|Did not meet the standard| A
D -->|Meet the requirements| E[Launch ✅]
style A fill:#cce5ff
style C fill:#d4edda
style E fill:#d4edda
دراسة حالة تحسين ShopHub: قامت أليس من شركة TechCorp بتحسين واجهة برمجة تطبيقات (API) قائمة المنتجات، مما أدى إلى تقليل وقت الاستجابة من 3 ثوانٍ إلى 50 مللي ثانية — ① كشف explain() عن وجود COLLSCAN → تمت إضافة فهرس مركب {category:1, isActive:1, createdAt:-1}؛ ② تم إرجاع جميع الحقول → استخدمت select() للاستعلام عن 5 حقول فقط؛ ③ إرجاع مستند Mongoose → أضافت lean()؛ ④ كانت find وcount متتاليتين → استخدمت Promise.all للمعالجة المتوازية.
// === Slow Queries Before Optimization ===
app.get('/api/products', async (req, res) => {
const products = await Product.find({ isActive: true });
// 100 Full-Table Scan of 10,000 Documents, ~3 seconds
res.json(products);
});
// === After optimization ===
app.get('/api/products', async (req, res) => {
const { page = 1, limit = 20, category, search } = req.query;
// 1. Building Indexes to Optimize Queries
const query = { isActive: true };
if (category) query.category = category;
if (search) query.title = new RegExp(search, 'i');
// 2. Projection + lean + limit
const products = await Product.find(query)
.select('sku title price thumbnail') // Projection
.hint({ isActive: 1, category: 1 }) // Forced Index
.limit(Math.min(+limit, 100)) // Maximum Limit
.skip((+page - 1) * +limit)
.lean(); // Performance Optimization
// 3. Parallel count
const total = await Product.countDocuments(query);
res.json({ data: products, meta: { page: +page, limit: +limit, total } });
});
// After optimization:~50ms(Performance ↑60x)
▶ المثال 1: تشخيصات دالة explain() + تحسين الفهرس
// === Scenario: ShopHub product list queries are slow, Alice uses explain to diagnose ===
// 1. Diagnose the Current Query
const explain = await Product.find({ category: 'Electronics', isActive: true })
.sort({ createdAt: -1 })
.limit(20)
.explain('executionStats');
console.log('Stage:', explain.queryPlanner.winningPlan.stage);
// Output:COLLSCAN ❌ Full Table Scan!
console.log('Docs examined:', explain.executionStats.totalDocsExamined);
// Output:1000000(Scanned all of them 100 10,000 Documents)
console.log('Docs returned:', explain.executionStats.nReturned);
// Output: 20 (Returned only 20 docs)
console.log('Time:', explain.executionStats.executionTimeMillis, 'ms');
// Output: 3200ms (too slow!)
// 2. Create a composite index
db.products.createIndex({ category: 1, isActive: 1, createdAt: -1 });
// 3. Once again explain Verification
const explain2 = await Product.find({ category: 'Electronics', isActive: true })
.sort({ createdAt: -1 })
.limit(20)
.hint({ category: 1, isActive: 1, createdAt: -1 })
.explain('executionStats');
console.log('Stage:', explain2.queryPlanner.winningPlan.stage);
// Output:IXSCAN ✅ Using Indexes!
console.log('Docs examined:', explain2.executionStats.totalDocsExamined);
// Output:20(Precise Scanning)
console.log('Time:', explain2.executionStats.executionTimeMillis, 'ms');
// Output:5ms(↑640x Performance Improvements!)
النتيجة: حدد التشخيص باستخدام explain() عملية COLLSCAN → مما أدى إلى إنشاء فهرس مركب → IXSCAN، وانخفض وقت الاستعلام من 3,200 مللي ثانية إلى 5 مللي ثانية.
▶ المثال 2: دليل عملي لتحسين الأداء الشامل (مجموعة الاتصالات + الفهرسة + التبسيط + المراقبة)
// === Scene:Product List API Performance Optimization(3s → 50ms)===
// === Before Optimization (slow) ===
app.get('/api/products', async (req, res) => {
const products = await Product.find(); // Full Table Scan + Return all fields
res.json(products);
});
// 100 10,000 Documents,~3000ms,~50MB Data
// === After optimization (fast) ===
// 1. Enable mongoose debug(Monitoring Queries During Development)
mongoose.set('debug', (coll, method, query) => {
console.log(`${coll}.${method}(${JSON.stringify(query)})`);
});
// 2. Optimizing the Connection Pool
mongoose.connect(uri, {
maxPoolSize: 50, // Adjust Based on Concurrency
minPoolSize: 5,
maxIdleTimeMS: 30000,
waitQueueTimeoutMS: 10000
});
// 3. Enable the slow query log(>100ms)
mongoose.connection.db.admin().command({
setParameter: 1,
slowms: 100
});
// 4. Create Appropriate Indexes
db.products.createIndex({ category: 1, isActive: 1, createdAt: -1 });
db.products.createIndex({ sku: 1 }, { unique: true });
db.products.createIndex({ title: 'text', description: 'text' });
// 5. Optimize Queries:projection + lean + hint + limit
app.get('/api/products', async (req, res) => {
const { page = 1, limit = 20, category, search } = req.query;
const query = { isActive: true };
if (category) query.category = category;
if (search) query.title = new RegExp(search, 'i');
const [products, total] = await Promise.all([
Product.find(query)
.select('sku title price thumbnail rating') // Projection:Return only 5 field
.hint({ category: 1, isActive: 1, createdAt: -1 }) // Forced Index
.sort({ createdAt: -1 })
.skip((page - 1) * limit)
.limit(Math.min(+limit, 100))
.lean(), // Skip mongoose hydrate
Product.countDocuments(query)
]);
res.json({
success: true,
data: products,
meta: { page: +page, limit: +limit, total, pages: Math.ceil(total / limit) }
});
});
// 100 10,000 Documents,~50ms(Performance ↑60x),~200KB Data(Reduce 99.6%)
// === 6. explain() Verify that the index is active ===
const explain = await Product.find({ category: 'Electronics', isActive: true })
.sort({ createdAt: -1 })
.limit(20)
.explain('executionStats');
console.log('Stage:', explain.queryPlanner.winningPlan.stage);
// Output:IXSCAN(An index was used)
console.log('Docs examined:', explain.executionStats.totalDocsExamined);
console.log('Keys examined:', explain.executionStats.totalKeysExamined);
console.log('Returned:', explain.executionStats.nReturned);
console.log('Time:', explain.executionStats.executionTimeMillis, 'ms');
// === 7. Monitoring Alerts ===
// 7.1 Monitor Connection Count
const connStatus = await mongoose.connection.db.admin().command({ serverStatus: 1 });
if (connStatus.connections.current > 1000) {
console.warn(`⚠️ Too many connections: ${connStatus.connections.current}`);
// Send an Alert (Email/DingTalk/Slack)
}
// 7.2 Monitoring Slow Queries
const slowQueries = await mongoose.connection.db.collection('system.profile')
.find({ millis: { $gt: 100 } })
.sort({ ts: -1 })
.limit(10)
.toArray();
slowQueries.forEach(q => {
console.log(`[${q.ts}] ${q.command.find}: ${q.millis}ms`);
console.log(` Plan: ${q.planSummary}`);
});
// 7.3 Index Usage Rate
const indexStats = await mongoose.connection.db.collection('products')
.aggregate([{ $indexStats: {} }])
.toArray();
const unused = indexStats.filter(s => s.accesses.ops === 0);
unused.forEach(i => {
console.log(`⚠️ Index not used: ${i.name}`);
// Automatic Deletion(Caution in Production Environments)
// await mongoose.connection.db.collection('products').dropIndex(i.name);
});
// === 8. APM Integration(Datadog/New Relic)===
const tracer = require('dd-trace').init();
tracer.use('mongoose', { service: 'shopdb' });
// All mongoose Query Auto-Tracking,Performance Data Reporting Datadog
النتائج: من خلال التحسين الشامل لمجموعة الاتصالات، والفهارس، ومنهجية «لين»، والتوقعات، تحسّن أداء الاستعلامات من 3 ثوانٍ إلى 50 مللي ثانية (بزيادة قدرها 60 ضعفًا)، وانخفض نقل البيانات بنسبة 99.6%.
▶ مثال 3: نظام مراقبة الأداء في الوقت الفعلي مع التنبيهات(الصعوبة ⭐⭐)
// نظام مراقبة أداء MongoDB لـ TechCorp
const mongoose = require('mongoose');
class PerformanceMonitor {
constructor() {
this.db = mongoose.connection.db;
}
// مراقبة الاتصالات
async checkConnections() {
const status = await this.db.admin().command({ serverStatus: 1 });
const { current, available } = status.connections;
console.log(`الاتصالات: ${current} / ${current + available}`);
if (current > (current + available) * 0.8) {
console.log('⚠️ تحذير: استخدام الاتصالات مرتفع!');
}
return { current, available };
}
// مراقبة الاستعلامات البطيئة
async checkSlowQueries() {
const slowQueries = await this.db.collection('system.profile')
.find({ millis: { $gt: 100 } })
.sort({ ts: -1 })
.limit(10)
.toArray();
slowQueries.forEach(q => {
console.log(`[بطيء] ${q.ns}: ${q.millis}مللي ثانية`);
console.log(` العملية: ${q.command.find ? 'find' : 'update'}`);
});
return slowQueries;
}
// مراقبة استخدام الفهرس
async checkIndexUsage(collectionName) {
const stats = await this.db.collection(collectionName)
.aggregate([{ $indexStats: {} }])
.toArray();
const unused = stats.filter(s => s.accesses.ops === 0);
unused.forEach(i => console.log(`⚠️ فهرس غير مستخدم: ${i.name}`));
return { total: stats.length, unused: unused.length };
}
// تشغيل الفحص الكامل
async runHealthCheck() {
console.log('=== فحص صحة الأداء ===');
const connections = await this.checkConnections();
const slowQueries = await this.checkSlowQueries();
const indexStats = await this.checkIndexUsage('products');
const health = {
status: slowQueries.length < 5 ? 'healthy' : 'warning',
connections,
slowQueryCount: slowQueries.length,
indexStats
};
console.log(`الحالة: ${health.status}`);
return health;
}
}
// الاستخدام
const monitor = new PerformanceMonitor();
await monitor.runHealthCheck();
// إعداد المراقبة الدورية (كل 60 ثانية)
setInterval(() => monitor.runHealthCheck(), 60000);
الإخراج:
TEXT 📖 للعرض فقط=== فحص صحة الأداء === الاتصالات: 45 / 100 [بطيء] shopdb.products: 250مللي ثانية العملية: find ⚠️ فهرس غير مستخدم: description_text الحالة: healthy
❓ أسئلة شائعة
lean()؟save وpopulate). وقد صُمم هذا الخيار للاستخدام مع واجهة برمجة التطبيقات المخصصة للاستعلامات فقط.query.explain('executionStats') تحقق من المرحلة: IXSCAN (مسح الفهرس) / COLLSCAN (مسح الجدول بالكامل).📖 ملخص
- ضبط تجمع الاتصالات: maxPoolSize / minPoolSize
- تحسين الاستعلامات: التلميح / الإسقاط / التبسيط / تجنب استخدام $where
- تحليل الاستعلامات البطيئة: setProfilingLevel / mongoose debug
- مراقبة استخدام الفهرس: $indexStats / system.profile
- أدوات إدارة الأداء (APM): Atlas / Prometheus / Datadog
- قائمة مراجعة الإنتاج: مجموعات النسخ المتماثلة + النسخ الاحتياطية + المراقبة + الأمان
📝 تمارين
- المشكلة الأساسية (⭐): استخدم
lean()لتحسين واجهة برمجة تطبيقات قائمة المنتجات ومقارنة الفروق في الأداء. - السؤال الأساسي (⭐): قم بتفعيل سجل الاستعلامات البطيئة وقم بتحليل أهم 10 استعلامات بطيئة.
- مشكلة متقدمة (⭐⭐): استخدم تلميحًا لإجبار النظام على الفهرسة، وقارن بين أداء COLLSCAN و IXSCAN.
- مشكلة متقدمة (⭐⭐): قم بتحليل $indexStats لتحديد الفهارس غير المستخدمة وحذفها.
- التحدي (⭐⭐⭐): قم بإجراء تحسين شامل للأداء (مجموعة الاتصالات + الفهارس + التبسيط + المراقبة)، وقارن الأداء قبل وبعد ذلك.